diff --git a/tests/js/workspace-media-viewer.test.js b/tests/js/workspace-media-viewer.test.js index a7cec60..c62965f 100644 --- a/tests/js/workspace-media-viewer.test.js +++ b/tests/js/workspace-media-viewer.test.js @@ -211,6 +211,13 @@ test('viewer renders the concept shell and toolbar order', async () => { assert.deepEqual([details.tagName,details.id,details.attributes['aria-label']],['aside','viewer-details','Image metadata']); const [stage,toolbar] = left.children; assert.deepEqual([stage.tagName,stage.id,toolbar.tagName,toolbar.id,toolbar.className],['div','viewer-stage','div','viewer-toolbar','viewer-toolbar']); + // #249 D34 #8: the toolbar keeps production's named group (the concept's has no role; adding one is invisible). + assert.deepEqual([toolbar.attributes.role,toolbar.attributes['aria-label']],['group','Media controls']); + // With more than one loaded item, Previous/Next are enabled and not described by the position line. + for (const label of ['Previous media','Next media']) { + const [node] = f.walk(f.container,n => n.attributes['aria-label'] === label); + assert.deepEqual([node.attributes['aria-disabled'],node.attributes['aria-describedby']],['false',undefined],label); + } // The concept's toolbar order, then production's position status and its two reasons. assert.deepEqual(toolbar.children.map(node => node.textContent.split(':')[0]), ['← Previous','−','+','Fit','Rotate','Flip','Play','Download','Next →','1 of 2 loaded','Image transforms are unavailable because Viewer.js could not load.','']); diff --git a/web/js/workspace/media-viewer.mjs b/web/js/workspace/media-viewer.mjs index 4339af4..6a9e9d5 100644 --- a/web/js/workspace/media-viewer.mjs +++ b/web/js/workspace/media-viewer.mjs @@ -57,6 +57,7 @@ export function createMediaViewer({container,parentScope,inspectionController,se const error = element('p',undefined,'Media could not be loaded. Retry this file.'); const retry = createButton('Retry media');failure.append(error,retry);failure.hidden = true;stage.append(failure); const controls = element('div','viewer-toolbar');controls.id = 'viewer-toolbar'; + controls.setAttribute('role','group');controls.setAttribute('aria-label','Media controls'); function button(label,text,action) { const node = createButton(text);node.setAttribute('aria-label',label); local.listen(node,'click',() => { if (node.getAttribute('aria-disabled') !== 'true') action(); }); @@ -187,7 +188,7 @@ export function createMediaViewer({container,parentScope,inspectionController,se } for (const node of [previous,next]) { node.setAttribute('aria-disabled',String(state.total === 1)); - if (state.total === 1) {node.setAttribute('aria-describedby',position.id);} + if (state.total === 1) node.setAttribute('aria-describedby',position.id);else node.removeAttribute('aria-describedby'); } const action = state.phase === 'paused-video' ? 'Stop' : state.requested ? 'Pause' : 'Play'; slideshow.textContent = action;slideshow.setAttribute('aria-label',`${action} slideshow`);