fix(overlays): fix deepContains wrt recursed children, nested shadows
This commit is contained in:
parent
83359ac2b9
commit
7709d7c265
3 changed files with 60 additions and 15 deletions
5
.changeset/itchy-tools-run.md
Normal file
5
.changeset/itchy-tools-run.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -10,15 +10,13 @@ export function deepContains(el, targetEl) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {HTMLElement} elem */
|
/** @param {HTMLElement|ShadowRoot} elem */
|
||||||
function checkChildren(elem) {
|
function checkChildren(elem) {
|
||||||
for (let i = 0; i < elem.children.length; i += 1) {
|
for (let i = 0; i < elem.children.length; i += 1) {
|
||||||
const child = /** @type {HTMLElement} */ (elem.children[i]);
|
const child = /** @type {HTMLElement} */ (elem.children[i]);
|
||||||
if (child.shadowRoot) {
|
if (child.shadowRoot && deepContains(child.shadowRoot, targetEl)) {
|
||||||
containsTarget = deepContains(child.shadowRoot, targetEl);
|
containsTarget = true;
|
||||||
if (containsTarget) {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (child.children.length > 0) {
|
if (child.children.length > 0) {
|
||||||
checkChildren(child);
|
checkChildren(child);
|
||||||
|
|
@ -27,14 +25,12 @@ export function deepContains(el, targetEl) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If element is not shadowRoot itself
|
// If element is not shadowRoot itself
|
||||||
if (el instanceof HTMLElement) {
|
if (el instanceof HTMLElement && el.shadowRoot) {
|
||||||
if (el.shadowRoot) {
|
containsTarget = deepContains(el.shadowRoot, targetEl);
|
||||||
containsTarget = deepContains(el.shadowRoot, targetEl);
|
if (containsTarget) {
|
||||||
if (containsTarget) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
checkChildren(el);
|
|
||||||
}
|
}
|
||||||
|
checkChildren(el);
|
||||||
return containsTarget;
|
return containsTarget;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,20 @@ describe('deepContains()', () => {
|
||||||
<button id="light-el-1"></button>
|
<button id="light-el-1"></button>
|
||||||
</div>
|
</div>
|
||||||
`));
|
`));
|
||||||
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, shadowElement)).to.be.true;
|
||||||
expect(deepContains(element, shadowElementChild)).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, shadowElement)).to.be.true;
|
||||||
expect(deepContains(shadowElement, shadowElementChild)).to.be.true;
|
expect(deepContains(shadowElement, shadowElementChild)).to.be.true;
|
||||||
expect(deepContains(shadowRoot, 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 () => {
|
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(shadowElement2, shadowElementChild2)).to.be.true;
|
||||||
expect(deepContains(shadowRoot2, 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('<div id="shadow-nested"></div>');
|
||||||
|
const shadowNestedRoot = shadowNestedElement.attachShadow({ mode: 'open' });
|
||||||
|
shadowNestedRoot.innerHTML = `
|
||||||
|
<button id="nested-el-1">Button</button>
|
||||||
|
<a id="nested-el-2" href="#foo">Href</a>
|
||||||
|
<input id="nested-el-3">
|
||||||
|
`;
|
||||||
|
|
||||||
|
const shadowElement = /** @type {HTMLElement} */ (await fixture('<div id="shadow"></div>'));
|
||||||
|
const shadowRoot = shadowElement.attachShadow({ mode: 'open' });
|
||||||
|
shadowRoot.innerHTML = `
|
||||||
|
<button id="el-1">Button</button>
|
||||||
|
|
||||||
|
<input id="el-3">
|
||||||
|
`;
|
||||||
|
shadowRoot.insertBefore(shadowNestedElement, shadowRoot.lastElementChild);
|
||||||
|
|
||||||
|
const element = /** @type {HTMLElement} */ (await fixture(html`
|
||||||
|
<div id="light">
|
||||||
|
${shadowElement}
|
||||||
|
<button id="light-el-1"></button>
|
||||||
|
</div>
|
||||||
|
`));
|
||||||
|
|
||||||
|
expect(deepContains(element, element.firstElementChild)).to.be.true;
|
||||||
|
expect(deepContains(element, element.firstElementChild.shadowRoot)).to.be.true;
|
||||||
|
expect(deepContains(element, element.firstElementChild.shadowRoot.children[1])).to.be.true;
|
||||||
|
expect(deepContains(element, element.firstElementChild.shadowRoot.children[1].shadowRoot)).to.be
|
||||||
|
.true;
|
||||||
|
expect(
|
||||||
|
deepContains(
|
||||||
|
element,
|
||||||
|
element.firstElementChild.shadowRoot.children[1].shadowRoot.lastElementChild,
|
||||||
|
),
|
||||||
|
).to.be.true;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue