From 1977764636e5c5923ac5ec23da6cc5be7696ccf4 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Fri, 17 Oct 2025 16:41:46 -0400 Subject: [PATCH 01/19] feat(tray): add dismissHelper function --- packages/tray/src/Tray.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/tray/src/Tray.ts b/packages/tray/src/Tray.ts index 901910c9380..d2a73407b1d 100644 --- a/packages/tray/src/Tray.ts +++ b/packages/tray/src/Tray.ts @@ -81,6 +81,23 @@ export class Tray extends SpectrumElement { } } + /** + * Returns a visually hidden dismiss button for mobile screen reader accessibility. + * This button is placed before and after tray content to allow mobile screen reader + * users (particularly VoiceOver on iOS) to easily dismiss the overlay. + */ + protected get dismissHelper(): TemplateResult { + return html` +
+ +
+ `; + } + private dispatchClosed(): void { this.dispatchEvent( new Event('close', { @@ -131,7 +148,9 @@ export class Tray extends SpectrumElement { tabindex="-1" @transitionend=${this.handleTrayTransitionend} > + ${this.dismissHelper} + ${this.dismissHelper} `; } From 4183066f65976a2c3605ebffbf5cd32571c68fc8 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Fri, 17 Oct 2025 16:42:36 -0400 Subject: [PATCH 02/19] refactor(tray): expand visually-hidden selector for dismiss buttons --- packages/tray/src/tray.css | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/tray/src/tray.css b/packages/tray/src/tray.css index d6bd646b275..33ec07f30b4 100644 --- a/packages/tray/src/tray.css +++ b/packages/tray/src/tray.css @@ -30,6 +30,7 @@ sp-underlay { overscroll-behavior: contain; } +.visually-hidden, ::slotted(.visually-hidden) { border: 0; clip: rect(0, 0, 0, 0); From 1d369483cd180b8dc17bf1d2981167a3128085d7 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Fri, 17 Oct 2025 16:43:58 -0400 Subject: [PATCH 03/19] docs(tray): add tray docs with visually hidden dismiss --- packages/tray/README.md | 49 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/packages/tray/README.md b/packages/tray/README.md index 3bbdaadb38d..85bca444c92 100644 --- a/packages/tray/README.md +++ b/packages/tray/README.md @@ -7,19 +7,19 @@ [![See it on NPM!](https://img.shields.io/npm/v/@spectrum-web-components/tray?style=for-the-badge)](https://www.npmjs.com/package/@spectrum-web-components/tray) [![How big is this package in your project?](https://img.shields.io/bundlephobia/minzip/@spectrum-web-components/tray?style=for-the-badge)](https://bundlephobia.com/result?p=@spectrum-web-components/tray) -``` +```bash yarn add @spectrum-web-components/tray ``` Import the side effectful registration of `` via: -``` +```js import '@spectrum-web-components/tray/sp-tray.js'; ``` When looking to leverage the `Tray` base class as a type and/or for extension purposes, do so via: -``` +```js import { Tray } from '@spectrum-web-components/tray'; ``` @@ -70,3 +70,46 @@ A tray has a single default `slot`. ### Accessibility `` presents a page blocking experience and should be opened with the `Overlay` API using the `modal` interaction to ensure that the content appropriately manages the presence of other content in the tab order of the page and the availability of that content for a screen reader. + +#### Mobile screen reader support + +The `` component automatically includes visually hidden dismiss buttons before and after its content to support mobile screen readers. This is particularly important for VoiceOver on iOS, where users navigate through interactive elements sequentially. + +These built-in dismiss buttons: + +- Are visually hidden but accessible to screen readers +- Use `tabindex="-1"` to prevent keyboard tab navigation interference +- Allow mobile screen reader users to easily dismiss the tray from either the beginning or end of the content +- Are labeled "Dismiss" for clear screen reader announcements + +This dismiss helper pattern is also implemented in the [``](https://opensource.adobe.com/spectrum-web-components/components/picker/) component, which uses the same approach when rendering menu content in a tray on mobile devices. + +Simply place your content inside the tray - the dismiss buttons are automatically rendered: + +```html + + + Toggle menu content + + + + Deselect + Select Inverse + Feather... + Select and Mask... + + + + + + + Toggle dialog content + + + +

New messages

+ You have 5 new messages. +
+
+
+``` From 92fb18bf86c27c273a2c04806cc95633dd088ca2 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 21 Oct 2025 14:33:33 -0400 Subject: [PATCH 04/19] feat(dialog-base): add dismissHelper for visually-hidden buttons --- packages/dialog/src/DialogBase.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/dialog/src/DialogBase.ts b/packages/dialog/src/DialogBase.ts index 2973c20ac69..7d35e8a584d 100644 --- a/packages/dialog/src/DialogBase.ts +++ b/packages/dialog/src/DialogBase.ts @@ -194,6 +194,23 @@ export class DialogBase extends FocusVisiblePolyfillMixin(SpectrumElement) { super.update(changes); } + /** + * Returns a visually hidden dismiss button for mobile screen reader accessibility. + * This button is placed before and after dialog content to allow mobile screen reader + * users (particularly VoiceOver on iOS) to easily dismiss the overlay. + */ + protected get dismissHelper(): TemplateResult { + return html` +
+ +
+ `; + } + protected renderDialog(): TemplateResult { return html` From a1132bf2d48086ae81c5f65550d008a7e33447d8 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 21 Oct 2025 14:36:32 -0400 Subject: [PATCH 05/19] feat(dialog-wrapper): implement conditional dismissHelper buttons --- packages/dialog/src/DialogWrapper.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/dialog/src/DialogWrapper.ts b/packages/dialog/src/DialogWrapper.ts index f6d6953fe9b..d46df91f638 100644 --- a/packages/dialog/src/DialogWrapper.ts +++ b/packages/dialog/src/DialogWrapper.ts @@ -125,6 +125,7 @@ export class DialogWrapper extends DialogBase { } return html` + ${this.dismissHelper} + ${!this.dismissable ? this.dismissHelper : nothing} `; } } From c96463d0d7aa25843624abae5f5f9c3be3351d7d Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Tue, 21 Oct 2025 14:46:02 -0400 Subject: [PATCH 06/19] feat(modal): add visually-hidden styles for dialog dismissHelpers --- packages/modal/src/modal.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/modal/src/modal.css b/packages/modal/src/modal.css index 996d22d3d6d..5c632d34982 100644 --- a/packages/modal/src/modal.css +++ b/packages/modal/src/modal.css @@ -24,3 +24,16 @@ .modal { overflow: visible; } + +.visually-hidden { + border: 0; + clip: rect(0, 0, 0, 0); + clip-path: inset(50%); + height: 1px; + margin: 0 -1px -1px 0; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + white-space: nowrap; +} From 5420c2147262f04747215be12111e4480422192a Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 22 Oct 2025 15:15:22 -0400 Subject: [PATCH 07/19] revert: feat(modal): add visually-hidden styles for dialog dismissHelpers we do not need to provide a close/visually hidden button for non-dismissible diaologs --- packages/modal/src/modal.css | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/modal/src/modal.css b/packages/modal/src/modal.css index 5c632d34982..996d22d3d6d 100644 --- a/packages/modal/src/modal.css +++ b/packages/modal/src/modal.css @@ -24,16 +24,3 @@ .modal { overflow: visible; } - -.visually-hidden { - border: 0; - clip: rect(0, 0, 0, 0); - clip-path: inset(50%); - height: 1px; - margin: 0 -1px -1px 0; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; - white-space: nowrap; -} From 40834f4b83cb098a933a51ec667cf6c13f509788 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 22 Oct 2025 15:16:47 -0400 Subject: [PATCH 08/19] revert: feat(dialog-wrapper): implement conditional dismissHelper buttons we do not need to provide a close/visually hidden button for non-dismissible dialogs --- packages/dialog/src/DialogWrapper.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/dialog/src/DialogWrapper.ts b/packages/dialog/src/DialogWrapper.ts index d46df91f638..f6d6953fe9b 100644 --- a/packages/dialog/src/DialogWrapper.ts +++ b/packages/dialog/src/DialogWrapper.ts @@ -125,7 +125,6 @@ export class DialogWrapper extends DialogBase { } return html` - ${this.dismissHelper} - ${!this.dismissable ? this.dismissHelper : nothing} `; } } From 36dea1c4288df82c8b60f29b2206db628bba607f Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 22 Oct 2025 15:17:38 -0400 Subject: [PATCH 09/19] revert: feat(dialog-base): add dismissHelper for visually-hidden buttons we do not need to provide a close/visually hidden button for non-dismissible dialogs --- packages/dialog/src/DialogBase.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/dialog/src/DialogBase.ts b/packages/dialog/src/DialogBase.ts index 7d35e8a584d..2973c20ac69 100644 --- a/packages/dialog/src/DialogBase.ts +++ b/packages/dialog/src/DialogBase.ts @@ -194,23 +194,6 @@ export class DialogBase extends FocusVisiblePolyfillMixin(SpectrumElement) { super.update(changes); } - /** - * Returns a visually hidden dismiss button for mobile screen reader accessibility. - * This button is placed before and after dialog content to allow mobile screen reader - * users (particularly VoiceOver on iOS) to easily dismiss the overlay. - */ - protected get dismissHelper(): TemplateResult { - return html` -
- -
- `; - } - protected renderDialog(): TemplateResult { return html` From 5d1e987e14e40541f32632296e4a5864bd55ca1d Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Wed, 22 Oct 2025 15:48:59 -0400 Subject: [PATCH 10/19] feat(tray): expand API for dismissible behavior - adds several properties and methods to tray API to support more flexibile dismissible behavior. - queries for keyboard-accessible dismiss buttons in the tray's slot content - adds a state property to track if dismiss buttons are needed - adds manual override for dismissible behavior --- packages/tray/src/Tray.ts | 99 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/packages/tray/src/Tray.ts b/packages/tray/src/Tray.ts index d2a73407b1d..0a622153525 100644 --- a/packages/tray/src/Tray.ts +++ b/packages/tray/src/Tray.ts @@ -13,6 +13,7 @@ import { CSSResultArray, html, + nothing, PropertyValues, SpectrumElement, TemplateResult, @@ -20,6 +21,7 @@ import { import { property, query, + state, } from '@spectrum-web-components/base/src/decorators.js'; import '@spectrum-web-components/underlay/sp-underlay.js'; import { firstFocusableIn } from '@spectrum-web-components/shared/src/first-focusable-in.js'; @@ -55,6 +57,9 @@ export class Tray extends SpectrumElement { @query('.tray') private tray!: HTMLDivElement; + @query('slot') + private slot!: HTMLSlotElement; + public override focus(): void { const firstFocusable = firstFocusableIn(this); if (firstFocusable) { @@ -81,6 +86,16 @@ export class Tray extends SpectrumElement { } } + /** + * When set, prevents the tray from rendering visually-hidden dismiss helpers. + * Use this if your slotted content has custom keyboard-accessible dismiss functionality + * that the auto-detection doesn't recognize. + * + * By default, the tray automatically detects buttons in slotted content. + */ + @property({ type: Boolean, attribute: 'has-keyboard-dismiss' }) + public hasKeyboardDismissButton = false; + /** * Returns a visually hidden dismiss button for mobile screen reader accessibility. * This button is placed before and after tray content to allow mobile screen reader @@ -98,6 +113,74 @@ export class Tray extends SpectrumElement { `; } + /** + * Internal state tracking whether dismiss helpers are needed. + * Automatically updated when slotted content changes. + */ + @state() + private needsDismissHelper = true; + + /** + * Check if slotted content has keyboard-accessible dismiss buttons. + * Looks for buttons in light DOM and checks for known components with built-in dismiss. + */ + private checkForDismissButtons(): void { + if (!this.slot) { + this.needsDismissHelper = true; + return; + } + + const slottedElements = this.slot.assignedElements({ flatten: true }); + + if (slottedElements.length === 0) { + this.needsDismissHelper = true; + return; + } + + const hasDismissButton = slottedElements.some((element) => { + // Check if element is a button itself + if ( + element.tagName === 'SP-BUTTON' || + element.tagName === 'SP-CLOSE-BUTTON' || + element.tagName === 'BUTTON' + ) { + return true; + } + + // Check for dismissable dialog (has built-in dismiss button in shadow DOM) + if ( + element.tagName === 'SP-DIALOG' && + element.hasAttribute('dismissable') + ) { + return true; + } + + // Check for dismissable dialog-wrapper + if ( + element.tagName === 'SP-DIALOG-WRAPPER' && + element.hasAttribute('dismissable') + ) { + return true; + } + + // Check for buttons in light DOM (won't see shadow DOM) + const buttons = element.querySelectorAll( + 'sp-button, sp-close-button, button' + ); + if (buttons.length > 0) { + return true; + } + + return false; + }); + + this.needsDismissHelper = !hasDismissButton; + } + + private handleSlotChange(): void { + this.checkForDismissButtons(); + } + private dispatchClosed(): void { this.dispatchEvent( new Event('close', { @@ -119,6 +202,12 @@ export class Tray extends SpectrumElement { } } + protected override firstUpdated(changes: PropertyValues): void { + super.firstUpdated(changes); + // Run initial button detection + this.checkForDismissButtons(); + } + protected override update(changes: PropertyValues): void { if ( changes.has('open') && @@ -148,9 +237,13 @@ export class Tray extends SpectrumElement { tabindex="-1" @transitionend=${this.handleTrayTransitionend} > - ${this.dismissHelper} - - ${this.dismissHelper} + ${!this.hasKeyboardDismissButton && this.needsDismissHelper + ? this.dismissHelper + : nothing} + + ${!this.hasKeyboardDismissButton && this.needsDismissHelper + ? this.dismissHelper + : nothing} `; } From 542fb4df50ea34760aef14e46b41bb42cb0cfc37 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 23 Oct 2025 09:13:14 -0400 Subject: [PATCH 11/19] docs(tray): update README with new properties/examples --- packages/tray/README.md | 45 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/tray/README.md b/packages/tray/README.md index 85bca444c92..d435db13af0 100644 --- a/packages/tray/README.md +++ b/packages/tray/README.md @@ -7,7 +7,7 @@ [![See it on NPM!](https://img.shields.io/npm/v/@spectrum-web-components/tray?style=for-the-badge)](https://www.npmjs.com/package/@spectrum-web-components/tray) [![How big is this package in your project?](https://img.shields.io/bundlephobia/minzip/@spectrum-web-components/tray?style=for-the-badge)](https://bundlephobia.com/result?p=@spectrum-web-components/tray) -```bash +```zsh yarn add @spectrum-web-components/tray ``` @@ -71,9 +71,9 @@ A tray has a single default `slot`. `` presents a page blocking experience and should be opened with the `Overlay` API using the `modal` interaction to ensure that the content appropriately manages the presence of other content in the tab order of the page and the availability of that content for a screen reader. -#### Mobile screen reader support +#### Auto-detection behavior -The `` component automatically includes visually hidden dismiss buttons before and after its content to support mobile screen readers. This is particularly important for VoiceOver on iOS, where users navigate through interactive elements sequentially. +By default, `` automatically detects whether its slotted content includes keyboard-accessible dismiss buttons (like ``, ``, or HTML ` + + + `); + await elementUpdated(el); + + const helpers = el.shadowRoot.querySelectorAll('.visually-hidden'); + expect(helpers.length).to.equal(0); + }); + + it('does not render dismiss helpers with has-keyboard-dismiss attribute', async () => { + const el = await fixture(html` + +

Custom content with custom dismiss handling

+
+ `); + await elementUpdated(el); + + expect(el.hasKeyboardDismissButton).to.be.true; + + const helpers = el.shadowRoot.querySelectorAll('.visually-hidden'); + expect(helpers.length).to.equal(0); + }); + + it('renders dismiss helpers after slot content changes to remove buttons', async () => { + const el = await fixture(html` + + Close + + `); + await elementUpdated(el); + + // Should not have helpers initially + let helpers = el.shadowRoot.querySelectorAll('.visually-hidden'); + expect(helpers.length).to.equal(0); + + // Remove the button + const button = el.querySelector('sp-button'); + button?.remove(); + await elementUpdated(el); + + // Should now have helpers + helpers = el.shadowRoot.querySelectorAll('.visually-hidden'); + expect(helpers.length).to.equal(2); + }); + + it('removes dismiss helpers after slot content changes to add buttons', async () => { + const el = await fixture(html` + +

Some content

+
+ `); + await elementUpdated(el); + + // Should have helpers initially + let helpers = el.shadowRoot.querySelectorAll('.visually-hidden'); + expect(helpers.length).to.equal(2); + + // Add a button + const button = document.createElement('sp-button'); + button.textContent = 'Close'; + el.appendChild(button); + await elementUpdated(el); + + // Should no longer have helpers + helpers = el.shadowRoot.querySelectorAll('.visually-hidden'); + expect(helpers.length).to.equal(0); + }); + + it('dismiss helper buttons trigger close when clicked', async () => { + const el = await fixture(html` + +

Content without buttons

+
+ `); + await elementUpdated(el); + + expect(el.open).to.be.true; + + const dismissButton = el.shadowRoot.querySelector( + '.visually-hidden button' + ) as HTMLButtonElement; + expect(dismissButton).to.exist; + + const closed = oneEvent(el, 'close'); + dismissButton.click(); + await closed; + + expect(el.open).to.be.false; + }); + }); }); From 1dcd561c7bb50cc673ecb948bd949bb7ff25c659 Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Thu, 23 Oct 2025 12:59:18 -0400 Subject: [PATCH 13/19] chore(tray): add changeset --- .changeset/every-worlds-push.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changeset/every-worlds-push.md diff --git a/.changeset/every-worlds-push.md b/.changeset/every-worlds-push.md new file mode 100644 index 00000000000..938db1eb553 --- /dev/null +++ b/.changeset/every-worlds-push.md @@ -0,0 +1,11 @@ +--- +'@spectrum-web-components/tray': minor +--- + +**Added**: Automatic dismiss button detection and visually-hidden helpers for screen reader accessibility + +- **Added**: `` now automatically detects keyboard-accessible dismiss buttons (like ``, ``, or HTML ` + `; } From 4321b3452412d1a12717c7d55962e5972b823d8f Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Fri, 24 Oct 2025 12:19:41 -0400 Subject: [PATCH 17/19] test(tray): add keyboard accessible test assertion --- packages/tray/test/tray.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tray/test/tray.test.ts b/packages/tray/test/tray.test.ts index 9e1781edae0..4eb929afb9b 100644 --- a/packages/tray/test/tray.test.ts +++ b/packages/tray/test/tray.test.ts @@ -106,7 +106,7 @@ describe('Tray', () => { }); describe('Dismiss helpers', () => { - it('renders visually-hidden dismiss helpers when no buttons detected', async () => { + it('renders visually-hidden, keyboard-accessible dismiss helpers when no buttons detected', async () => { const el = await fixture(html` @@ -125,7 +125,7 @@ describe('Tray', () => { ); expect(buttons.length).to.equal(2); expect(buttons[0].getAttribute('aria-label')).to.equal('Dismiss'); - expect(buttons[0].getAttribute('tabindex')).to.equal('-1'); + expect(buttons[0].getAttribute('tabindex')).to.be.null; }); it('does not render dismiss helpers when sp-button is detected', async () => { From 320f0d7f6b72d105cb9392e9d036610dc68050dc Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Fri, 24 Oct 2025 12:21:18 -0400 Subject: [PATCH 18/19] docs(tray): update docs with keyboard nav info --- packages/tray/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/tray/README.md b/packages/tray/README.md index d435db13af0..e8ad8ca004e 100644 --- a/packages/tray/README.md +++ b/packages/tray/README.md @@ -78,7 +78,6 @@ By default, `` automatically detects whether its slotted content includ These built-in dismiss buttons: - Are visually hidden but accessible to screen readers -- Use `tabindex="-1"` to prevent keyboard tab navigation interference - Allow mobile screen reader users to easily dismiss the tray from either the beginning or end of the content - Are labeled "Dismiss" for clear screen reader announcements @@ -88,7 +87,7 @@ This dismiss helper pattern is also implemented in the [``](https://o Content has no buttons -This example shows the default behavior where the tray automatically detects that the menu content lacks dismiss buttons and renders visually hidden helpers. Screen readers will announce them as "Dismiss, button." +This example shows the default behavior where the tray automatically detects that the menu content lacks dismiss buttons and renders visually hidden helpers. Screen readers will announce them as "Dismiss, button" and these helpers are keyboard accessible. ```html From 661de4a528e5472a803430447218416983a0715a Mon Sep 17 00:00:00 2001 From: Marissa Huysentruyt Date: Fri, 24 Oct 2025 12:48:59 -0400 Subject: [PATCH 19/19] test(tray): add tab and close() keyboard tests --- packages/tray/test/tray.test.ts | 62 ++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/tray/test/tray.test.ts b/packages/tray/test/tray.test.ts index 4eb929afb9b..e8057320ed4 100644 --- a/packages/tray/test/tray.test.ts +++ b/packages/tray/test/tray.test.ts @@ -106,7 +106,7 @@ describe('Tray', () => { }); describe('Dismiss helpers', () => { - it('renders visually-hidden, keyboard-accessible dismiss helpers when no buttons detected', async () => { + it('renders visually-hidden dismiss helpers when no buttons detected', async () => { const el = await fixture(html` @@ -128,6 +128,66 @@ describe('Tray', () => { expect(buttons[0].getAttribute('tabindex')).to.be.null; }); + it('allows focusing dismiss helper buttons', async () => { + const el = await fixture(html` + +

Content without buttons

+
+ `); + await elementUpdated(el); + + const dismissButton = el.shadowRoot.querySelector( + '.visually-hidden button' + ) as HTMLButtonElement; + expect(dismissButton).to.exist; + + dismissButton.focus(); + await elementUpdated(el); + + expect(document.activeElement).to.equal(el); + expect( + el.shadowRoot.activeElement, + 'dismiss button is focused in shadow root' + ).to.equal(dismissButton); + }); + + it('closes tray when Enter key is pressed on dismiss button', async () => { + const test = await fixture(html` + + +

Content without buttons

+
+
+ `); + + const el = test.querySelector('sp-tray') as Tray; + await nextFrame(); // allows for animation to complete + await nextFrame(); + await elementUpdated(el); + expect(el.open).to.be.true; + + const dismissButton = el.shadowRoot.querySelector( + '.visually-hidden button' + ) as HTMLButtonElement; + + dismissButton.focus(); + await elementUpdated(el); + + const closed = oneEvent(el, 'close'); + const enterEvent = new KeyboardEvent('keydown', { + key: 'Enter', + code: 'Enter', + bubbles: true, + cancelable: true, + }); + dismissButton.dispatchEvent(enterEvent); + // Trigger the default button behavior + dismissButton.click(); + await closed; + + expect(el.open).to.be.false; + }); + it('does not render dismiss helpers when sp-button is detected', async () => { const el = await fixture(html`