Skip to content

Commit ea3ddcf

Browse files
committed
fix: update screen reader interface with menu items list
1 parent 26a2dde commit ea3ddcf

File tree

6 files changed

+133
-68
lines changed

6 files changed

+133
-68
lines changed

packages/overlay/stories/overlay-story-components.ts

+21-63
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@ import {
2020
query,
2121
} from '@spectrum-web-components/base';
2222

23-
import { Overlay, Placement } from '../';
23+
import { Overlay, OverlayTrigger, Placement } from '../';
2424
import { RadioGroup } from '@spectrum-web-components/radio';
2525
import '@spectrum-web-components/button/sp-button.js';
2626
import { Button } from '@spectrum-web-components/button';
2727
import '@spectrum-web-components/popover/sp-popover.js';
2828
import '@spectrum-web-components/radio/sp-radio.js';
2929
import '@spectrum-web-components/radio/sp-radio-group.js';
3030
import '@spectrum-web-components/overlay/overlay-trigger.js';
31-
import { Picker } from '@spectrum-web-components/picker';
3231

3332
// Prevent infinite recursion in browser
3433
const MAX_DEPTH = 7;
@@ -307,72 +306,31 @@ class RecursivePopover extends LitElement {
307306
customElements.define('recursive-popover', RecursivePopover);
308307

309308
export class PopoverContent extends LitElement {
310-
@query('sp-picker')
311-
public picker!: Picker;
309+
@query('[slot="trigger"]')
310+
public button!: Button;
311+
312+
@query('overlay-trigger')
313+
public trigger!: OverlayTrigger;
312314

313315
render(): TemplateResult {
314316
return html`
315-
<sp-field-label for="picker1">Test</sp-field-label>
316-
317-
<sp-picker id="picker1" focusable .label=${'test'}>
318-
<sp-menu-item value=${'timeSinceLastSynced'}>
319-
${'test'}
320-
</sp-menu-item>
321-
<sp-menu-item value=${'timeSinceLastSynced'}>
322-
${'test'}
323-
</sp-menu-item>
324-
<sp-menu-item value=${'timeSinceLastSynced'}>
325-
${'test'}
326-
</sp-menu-item>
327-
<sp-menu-item value=${'timeSinceLastSynced'}>
328-
${'test'}
329-
</sp-menu-item>
330-
<sp-menu-item value=${'timeSinceLastSynced'}>
331-
${'test'}
332-
</sp-menu-item>
333-
</sp-picker>
334-
335-
<sp-field-label for="picker2">Test2</sp-field-label>
336-
337-
<sp-picker id="picker2" focusable .label=${'test'}>
338-
<sp-menu-item value=${'timeSinceLastSynced'}>
339-
${'test'}
340-
</sp-menu-item>
341-
<sp-menu-item value=${'timeSinceLastSynced'}>
342-
${'test'}
343-
</sp-menu-item>
344-
<sp-menu-item value=${'timeSinceLastSynced'}>
345-
${'test'}
346-
</sp-menu-item>
347-
<sp-menu-item value=${'timeSinceLastSynced'}>
348-
${'test'}
349-
</sp-menu-item>
350-
<sp-menu-item value=${'timeSinceLastSynced'}>
351-
${'test'}
352-
</sp-menu-item>
353-
</sp-picker>
354-
355-
<sp-field-label for="picker3">Test 3</sp-field-label>
356-
357-
<sp-picker id="picker3" focusable .label=${'test'}>
358-
<sp-menu-item value=${'timeSinceLastSynced'}>
359-
${'test'}
360-
</sp-menu-item>
361-
<sp-menu-item value=${'timeSinceLastSynced'}>
362-
${'test'}
363-
</sp-menu-item>
364-
<sp-menu-item value=${'timeSinceLastSynced'}>
365-
${'test'}
366-
</sp-menu-item>
367-
<sp-menu-item value=${'timeSinceLastSynced'}>
368-
${'test'}
369-
</sp-menu-item>
370-
<sp-menu-item value=${'timeSinceLastSynced'}>
371-
${'test'}
372-
</sp-menu-item>
373-
</sp-picker>
317+
<overlay-trigger>
318+
<sp-button slot="trigger">Open me</sp-button>
319+
<sp-popover slot="click-content" direction="bottom" dialog>
320+
<p>This is all the content.</p>
321+
<p>This is all the content.</p>
322+
<p>This is all the content.</p>
323+
<p>This is all the content.</p>
324+
</sp-popover>
325+
</overlay-trigger>
374326
`;
375327
}
376328
}
377329

378330
customElements.define('popover-content', PopoverContent);
331+
332+
declare global {
333+
interface HTMLElementTagNameMap {
334+
'popover-content': PopoverContent;
335+
}
336+
}

packages/overlay/stories/overlay.stories.ts

+61
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,65 @@ export const detachedElement = (): TemplateResult => {
835835
`;
836836
};
837837

838+
class DefinedOverlayReady extends HTMLElement {
839+
ready!: (value: boolean | PromiseLike<boolean>) => void;
840+
841+
constructor() {
842+
super();
843+
this.readyPromise = new Promise((res) => {
844+
this.ready = res;
845+
this.setup();
846+
});
847+
}
848+
849+
async setup(): Promise<void> {
850+
await nextFrame();
851+
852+
const overlay = document.querySelector(
853+
`overlay-trigger`
854+
) as OverlayTrigger;
855+
const button = document.querySelector(
856+
`[slot="trigger"]`
857+
) as HTMLButtonElement;
858+
overlay.addEventListener('sp-opened', this.handleTriggerOpened);
859+
button.click();
860+
}
861+
862+
handleTriggerOpened = async (): Promise<void> => {
863+
await nextFrame();
864+
865+
const popover = document.querySelector('popover-content');
866+
if (!popover) {
867+
return;
868+
}
869+
popover.addEventListener('sp-opened', this.handlePopoverOpen);
870+
popover.button.click();
871+
};
872+
873+
handlePopoverOpen = async (): Promise<void> => {
874+
await nextFrame();
875+
876+
this.ready(true);
877+
};
878+
879+
private readyPromise: Promise<boolean> = Promise.resolve(false);
880+
881+
get updateComplete(): Promise<boolean> {
882+
return this.readyPromise;
883+
}
884+
}
885+
886+
customElements.define('defined-overlay-ready', DefinedOverlayReady);
887+
888+
const definedOverlayDecorator = (
889+
story: () => TemplateResult
890+
): TemplateResult => {
891+
return html`
892+
${story()}
893+
<defined-overlay-ready></defined-overlay-ready>
894+
`;
895+
};
896+
838897
export const definedOverlayElement = (): TemplateResult => {
839898
return html`
840899
<overlay-trigger placement="bottom" type="modal">
@@ -845,3 +904,5 @@ export const definedOverlayElement = (): TemplateResult => {
845904
</overlay-trigger>
846905
`;
847906
};
907+
908+
definedOverlayElement.decorators = [definedOverlayDecorator];

packages/overlay/test/overlay.test.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ describe('Overlay - type="modal"', () => {
561561
expect(firstOverlay, 'first overlay').to.not.be.null;
562562
expect(firstOverlay.isConnected).to.be.true;
563563
expect(firstHeadline.textContent).to.equal('Menu source: end');
564-
const closed = oneEvent(document, 'sp-closed');
564+
let closed = oneEvent(document, 'sp-closed');
565565
opened = oneEvent(document, 'sp-opened');
566566
// Right click to out of the "context menu" overlay to both close
567567
// the first overlay and have the event passed to the surfacing page
@@ -594,6 +594,21 @@ describe('Overlay - type="modal"', () => {
594594
expect(firstOverlay.isConnected).to.be.false;
595595
expect(secondOverlay.isConnected).to.be.true;
596596
expect(secondHeadline.textContent).to.equal('Menu source: start');
597+
closed = oneEvent(document, 'sp-closed');
598+
executeServerCommand('send-mouse', {
599+
steps: [
600+
{
601+
type: 'move',
602+
position: [width / 8, height / 8],
603+
},
604+
{
605+
type: 'click',
606+
position: [width / 8, height / 8],
607+
},
608+
],
609+
});
610+
await closed;
611+
await nextFrame();
597612
});
598613
it('opens children in the modal stack through shadow roots', async () => {
599614
const el = await fixture<OverlayTrigger>(definedOverlayElement());
@@ -607,14 +622,14 @@ describe('Overlay - type="modal"', () => {
607622
'popover-content'
608623
) as PopoverContent;
609624
open = oneEvent(content, 'sp-opened');
610-
content.picker.click();
625+
content.button.click();
611626
await open;
612627
const activeOverlays = document.querySelectorAll('active-overlay');
613628
activeOverlays.forEach((overlay) => {
614629
expect(overlay.slot).to.equal('open');
615630
});
616631
let close = oneEvent(content, 'sp-closed');
617-
content.picker.open = false;
632+
content.trigger.removeAttribute('open');
618633
await close;
619634
close = oneEvent(el, 'sp-closed');
620635
el.removeAttribute('open');

packages/picker/src/Picker.ts

+15
Original file line numberDiff line numberDiff line change
@@ -431,19 +431,34 @@ export class PickerBase extends SizedMixin(Focusable) {
431431
`;
432432
}
433433

434+
protected get dismissHelper(): TemplateResult {
435+
return html`
436+
<div class="visually-hidden">
437+
<button
438+
tabindex="-1"
439+
arial-label="Dismiss"
440+
@click=${this.close}
441+
></button>
442+
</div>
443+
`;
444+
}
445+
434446
protected get renderPopover(): TemplateResult {
435447
return html`
436448
<sp-popover
437449
id="popover"
450+
role="dialog"
438451
@sp-overlay-closed=${this.onOverlayClosed}
439452
.overlayCloseCallback=${this.overlayCloseCallback}
440453
>
454+
${this.dismissHelper}
441455
<sp-menu
442456
id="menu"
443457
role="${this.listRole}"
444458
selects="single"
445459
@change=${this.handleChange}
446460
></sp-menu>
461+
${this.dismissHelper}
447462
</sp-popover>
448463
`;
449464
}

packages/picker/src/picker.css

+5-2
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,16 @@ sp-popover {
7777
}
7878

7979
.visually-hidden {
80-
clip: rect(0 0 0 0);
80+
border: 0;
81+
clip: rect(0, 0, 0, 0);
8182
clip-path: inset(50%);
8283
height: 1px;
84+
margin: 0 -1px -1px 0;
8385
overflow: hidden;
86+
padding: 0;
8487
position: absolute;
85-
white-space: nowrap;
8688
width: 1px;
89+
white-space: nowrap;
8790
}
8891

8992
:host([dir='ltr']) #label.visually-hidden + .picker {

packages/popover/src/popover.css

+13
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,16 @@ governing permissions and limitations under the License.
6868
transform-origin: 0% 0%;
6969
transform: rotate(90deg);
7070
}
71+
72+
::slotted(.visually-hidden) {
73+
border: 0;
74+
clip: rect(0, 0, 0, 0);
75+
clip-path: inset(50%);
76+
height: 1px;
77+
margin: 0 -1px -1px 0;
78+
overflow: hidden;
79+
padding: 0;
80+
position: absolute;
81+
width: 1px;
82+
white-space: nowrap;
83+
}

0 commit comments

Comments
 (0)