From ed9c635607e796b6e11fea8ba68610624cf74ade Mon Sep 17 00:00:00 2001 From: konnorrogers Date: Fri, 23 Jun 2023 15:12:41 -0400 Subject: [PATCH 1/9] add modal tab tracking --- src/components/dialog/dialog.test.ts | 41 +++++++++++++++++++++ src/components/dialog/dialog.ts | 6 ++- src/internal/modal.ts | 55 ++++++++++++++++++++++++---- src/internal/tabbable.ts | 18 ++++++--- 4 files changed, 105 insertions(+), 15 deletions(-) diff --git a/src/components/dialog/dialog.test.ts b/src/components/dialog/dialog.test.ts index a8d9ff82c2..8e78764687 100644 --- a/src/components/dialog/dialog.test.ts +++ b/src/components/dialog/dialog.test.ts @@ -1,5 +1,6 @@ import '../../../dist/shoelace.js'; // cspell:dictionaries lorem-ipsum +import { LitElement } from "lit" import { expect, fixture, html, waitUntil } from '@open-wc/testing'; import { sendKeys } from '@web/test-runner-commands'; import sinon from 'sinon'; @@ -146,4 +147,44 @@ describe('', () => { expect(el.open).to.be.false; }); + + // https://github.com/shoelace-style/shoelace/issues/1382 + it('should properly cycle through tabbable elements when sl-dialog is used in a shadowRoot', async () => { + class AContainer extends LitElement { + get dialog () { + return this.shadowRoot?.querySelector("sl-dialog") + } + + openDialog() { + this.dialog?.show(); + } + + render() { + return html` +

Dialog Example

+ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. +
+ A + B +
+ + Open Dialog + `; + } + } + + window.customElements.define("a-container", AContainer); + + const testCase = await fixture(html` + + +

+ Open the dialog, then use Tab to cycle through the inputs. Focus should be trapped, but it reaches things outside the dialog. +

+ `) + + + testCase.querySelector("a-container")?.openDialog() + }) }); diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index b483922bff..7af34c3cd9 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -104,6 +104,7 @@ export default class SlDialog extends ShoelaceElement { disconnectedCallback() { super.disconnectedCallback(); + this.modal.deactivate() unlockBodyScrolling(this); } @@ -269,7 +270,7 @@ export default class SlDialog extends ShoelaceElement { aria-hidden=${this.open ? 'false' : 'true'} aria-label=${ifDefined(this.noHeader ? this.label : undefined)} aria-labelledby=${ifDefined(!this.noHeader ? 'title' : undefined)} - tabindex="0" + tabindex="-1" > ${!this.noHeader ? html` @@ -293,7 +294,8 @@ export default class SlDialog extends ShoelaceElement { ` : ''} - + ${'' /* The tabindex="-1" is here because the body is technically scrollable if overflowing. However, if there's no focusable elements inside, you won't actually be able to scroll it via keyboard. */} +