diff --git a/.changeset/cyan-onions-juggle.md b/.changeset/cyan-onions-juggle.md new file mode 100644 index 000000000..e8d00433b --- /dev/null +++ b/.changeset/cyan-onions-juggle.md @@ -0,0 +1,5 @@ +--- +'@lion/overlays': patch +--- + +Only set inert when placementMode is global, otherwise events are broken inside local overlay content diff --git a/.changeset/itchy-tools-run.md b/.changeset/itchy-tools-run.md new file mode 100644 index 000000000..d187d2118 --- /dev/null +++ b/.changeset/itchy-tools-run.md @@ -0,0 +1,5 @@ +--- +'@lion/overlays': patch +--- + +Fix deep contains util to properly check multiple nested shadow roots, and do not set containsTarget to false when children deepContains returns false. This would undo a found target in another child somewhere that was found in an earlier recursion. diff --git a/packages/overlays/src/OverlayController.js b/packages/overlays/src/OverlayController.js index 0381d2618..2fb72cbbc 100644 --- a/packages/overlays/src/OverlayController.js +++ b/packages/overlays/src/OverlayController.js @@ -1042,7 +1042,7 @@ export class OverlayController extends EventTargetShim { this._containFocusHandler = containFocus(this.contentNode); this.__hasActiveTrapsKeyboardFocus = true; if (this.manager) { - this.manager.informTrapsKeyboardFocusGotEnabled(); + this.manager.informTrapsKeyboardFocusGotEnabled(this.placementMode); } } diff --git a/packages/overlays/src/OverlaysManager.js b/packages/overlays/src/OverlaysManager.js index 5144255e5..8d67ab04a 100644 --- a/packages/overlays/src/OverlaysManager.js +++ b/packages/overlays/src/OverlaysManager.js @@ -153,8 +153,11 @@ export class OverlaysManager { }); } - informTrapsKeyboardFocusGotEnabled() { - if (this.siblingsInert === false) { + /** + * @param {'local' | 'global' | undefined} placementMode + */ + informTrapsKeyboardFocusGotEnabled(placementMode) { + if (this.siblingsInert === false && placementMode === 'global') { if (OverlaysManager.__globalRootNode) { setSiblingsInert(this.globalRootNode); } diff --git a/packages/overlays/src/utils/deep-contains.js b/packages/overlays/src/utils/deep-contains.js index b5730ab68..0df64dc14 100644 --- a/packages/overlays/src/utils/deep-contains.js +++ b/packages/overlays/src/utils/deep-contains.js @@ -10,15 +10,13 @@ export function deepContains(el, targetEl) { return true; } - /** @param {HTMLElement} elem */ + /** @param {HTMLElement|ShadowRoot} elem */ function checkChildren(elem) { for (let i = 0; i < elem.children.length; i += 1) { const child = /** @type {HTMLElement} */ (elem.children[i]); - if (child.shadowRoot) { - containsTarget = deepContains(child.shadowRoot, targetEl); - if (containsTarget) { - break; - } + if (child.shadowRoot && deepContains(child.shadowRoot, targetEl)) { + containsTarget = true; + break; } if (child.children.length > 0) { checkChildren(child); @@ -27,14 +25,12 @@ export function deepContains(el, targetEl) { } // If element is not shadowRoot itself - if (el instanceof HTMLElement) { - if (el.shadowRoot) { - containsTarget = deepContains(el.shadowRoot, targetEl); - if (containsTarget) { - return true; - } + if (el instanceof HTMLElement && el.shadowRoot) { + containsTarget = deepContains(el.shadowRoot, targetEl); + if (containsTarget) { + return true; } - checkChildren(el); } + checkChildren(el); return containsTarget; } diff --git a/packages/overlays/test/utils-tests/deep-contains.test.js b/packages/overlays/test/utils-tests/deep-contains.test.js index 60b9382a2..fa743a885 100644 --- a/packages/overlays/test/utils-tests/deep-contains.test.js +++ b/packages/overlays/test/utils-tests/deep-contains.test.js @@ -19,14 +19,20 @@ describe('deepContains()', () => { `)); - const lightEl = /** @type {HTMLElement} */ (element.querySelector('#light-el-1')); + const lightChildEl = /** @type {HTMLElement} */ (element.querySelector('#light-el-1')); + expect(deepContains(element, element)).to.be.true; expect(deepContains(element, shadowElement)).to.be.true; expect(deepContains(element, shadowElementChild)).to.be.true; - expect(deepContains(element, lightEl)).to.be.true; + expect(deepContains(element, lightChildEl)).to.be.true; expect(deepContains(shadowElement, shadowElement)).to.be.true; expect(deepContains(shadowElement, shadowElementChild)).to.be.true; expect(deepContains(shadowRoot, shadowElementChild)).to.be.true; + + // Siblings + expect(deepContains(element.firstElementChild, element.lastElementChild)).to.be.false; + // Unrelated + expect(deepContains(lightChildEl, shadowElementChild)).to.be.false; }); it('returns true if element contains a target element with a shadow boundary in between, for multiple shadowroots', async () => { @@ -63,4 +69,42 @@ describe('deepContains()', () => { expect(deepContains(shadowElement2, shadowElementChild2)).to.be.true; expect(deepContains(shadowRoot2, shadowElementChild2)).to.be.true; }); + + it('can detect containing elements inside multiple nested shadow roots', async () => { + const shadowNestedElement = await fixture('
'); + const shadowNestedRoot = shadowNestedElement.attachShadow({ mode: 'open' }); + shadowNestedRoot.innerHTML = ` + + Href + + `; + + const shadowElement = /** @type {HTMLElement} */ (await fixture('')); + const shadowRoot = shadowElement.attachShadow({ mode: 'open' }); + shadowRoot.innerHTML = ` + + + + `; + shadowRoot.insertBefore(shadowNestedElement, shadowRoot.lastElementChild); + + const element = /** @type {HTMLElement} */ (await fixture(html` +