diff --git a/package-lock.json b/package-lock.json index d4459f00..cdb67a43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1386,6 +1386,12 @@ "@xtuc/long": "4.2.2" } }, + "@webcomponents/custom-elements": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.5.0.tgz", + "integrity": "sha512-c+7jPQCs9h/BYVcZ2Kna/3tsl3A/9EyXfvWjp5RiTDm1OpTcbZaCa1z4RNcTe/hUtXaqn64JjNW1yrWT+rZ8gg==", + "dev": true + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", diff --git a/package.json b/package.json index d48bce1e..f4b614dd 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ ], "license": "MIT", "devDependencies": { + "@webcomponents/custom-elements": "^1.5.0", "babel-cli": "^6.26.0", "babel-core": "^6.25.0", "babel-eslint": "^8.0.1", diff --git a/specs/Modal.helpers.spec.js b/specs/Modal.helpers.spec.js index 11e22a3f..3043cf17 100644 --- a/specs/Modal.helpers.spec.js +++ b/specs/Modal.helpers.spec.js @@ -1,5 +1,6 @@ /* eslint-env mocha */ import "should"; +import "@webcomponents/custom-elements/src/native-shim"; import tabbable from "../src/helpers/tabbable"; import "sinon"; @@ -114,6 +115,60 @@ export default () => { elem.appendChild(button); tabbable(elem).should.not.containEql(button); }); + + describe("inside Web Components", () => { + let wc; + let input; + class TestWebComponent extends HTMLElement { + constructor() { + super(); + } + + connectedCallback() { + this.attachShadow({ + mode: "open" + }); + this.style.display = "block"; + this.style.width = "100px"; + this.style.height = "25px"; + } + } + + const registerTestComponent = () => { + if (window.customElements.get("test-web-component")) { + return; + } + window.customElements.define("test-web-component", TestWebComponent); + }; + + beforeEach(() => { + registerTestComponent(); + wc = document.createElement("test-web-component"); + + input = document.createElement("input"); + elem.appendChild(input); + + document.body.appendChild(wc); + wc.shadowRoot.appendChild(elem); + }); + + afterEach(() => { + // re-add elem to body for the next afterEach + document.body.appendChild(elem); + + // remove Web Component + document.body.removeChild(wc); + }); + + it("includes elements when inside a Shadow DOM", () => { + tabbable(elem).should.containEql(input); + }); + + it("excludes elements when hidden inside a Shadow DOM", () => { + wc.style.display = "none"; + tabbable(elem).should.not.containEql(input); + }); + }); }); }); }; diff --git a/src/helpers/tabbable.js b/src/helpers/tabbable.js index f2ebbc9c..062216e0 100644 --- a/src/helpers/tabbable.js +++ b/src/helpers/tabbable.js @@ -35,8 +35,13 @@ function hidesContents(element) { function visible(element) { let parentElement = element; + let rootNode = + typeof element.getRootNode === "function" + ? element.getRootNode() + : undefined; while (parentElement) { if (parentElement === document.body) break; + if (rootNode && parentElement === rootNode) parentElement = rootNode.host; if (hidesContents(parentElement)) return false; parentElement = parentElement.parentNode; }