Skip to content

Commit

Permalink
fix: support shadow-dom. Fixes #14
Browse files Browse the repository at this point in the history
  • Loading branch information
theKashey committed Nov 13, 2022
1 parent 95843f2 commit 3dd2b66
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
10 changes: 5 additions & 5 deletions .size-limit.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = [
module.exports = [
{
"path": "dist/es2015/index.js",
"limit": "0.6 KB"
}
];
path: 'dist/es2015/index.js',
limit: '0.8 KB',
},
];
2 changes: 1 addition & 1 deletion .size.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
{
"name": "dist/es2015/index.js",
"passed": true,
"size": 599
"size": 719
}
]
80 changes: 80 additions & 0 deletions __tests__/shadow-dom.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';

import { suppressOthers } from '../src';

describe('shadow dom', () => {
it('Understand the host location', () => {
function App() {
return (
<div className="App">
<input id="input" />
<input id="another-input" />
</div>
);
}

const template = document.createElement('template');

template.innerHTML = `
<div>
<p part="title">React attached below</p>
<div id="root"></div>
</div>
`;

class WebComp extends HTMLElement {
root: ShadowRoot | undefined = undefined;
getTarget() {
return this.root!.querySelector<HTMLInputElement>('#input')!;
}
getAnotherTarget() {
return this.root!.querySelector<HTMLInputElement>('#another-input')!;
}
constructor() {
super();

// attach to the Shadow DOM
this.root = this.attachShadow({ mode: 'closed' });
this.root.appendChild(template.content.cloneNode(true));

ReactDOM.render(<App />, this.root);
}
}

window.customElements.define('web-comp', WebComp);

const webComp = document.createElement('web-comp') as WebComp;
document.body.innerHTML = '<div id="test"></div>';
document.body.appendChild(webComp);

suppressOthers(webComp.getTarget());
expect(webComp.getAttribute('aria-hidden')).toBeFalsy();

expect(document.body.innerHTML).toMatchInlineSnapshot(
`"<div id=\\"test\\" data-suppressed=\\"true\\" aria-hidden=\\"true\\"></div><web-comp></web-comp>"`
);

expect(webComp.root!.innerHTML).toMatchInlineSnapshot(
`"<div class=\\"App\\"><input id=\\"input\\"><input id=\\"another-input\\"></div>"`
);

suppressOthers(webComp.getTarget(), webComp.root as any);
expect(webComp.getTarget().getAttribute('aria-hidden')).toBeFalsy();
expect(webComp.getAnotherTarget().getAttribute('aria-hidden')).not.toBeFalsy();

document.body.innerHTML = '';

// assert error
const error = jest.spyOn(console, 'error');
suppressOthers(webComp.getTarget());

expect(error).toHaveBeenCalledWith(
'aria-hidden',
webComp.getTarget(),
expect.any(String),
document.body,
expect.any(String)
);
});
});
24 changes: 23 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ let uncontrolledNodes = new WeakMap<Element, boolean>();
let markerMap: Record<string, WeakMap<Element, number>> = {};
let lockCount = 0;

const unwrapHost = (node: Element | ShadowRoot): Element | null =>
node && ((node as ShadowRoot).host || unwrapHost(node.parentNode as Element));

const correctTargets = (parent: HTMLElement, targets: Element[]): Element[] =>
targets
.map((target) => {
if (parent.contains(target)) {
return target;
}

const correctedTarget = unwrapHost(target);

if (correctedTarget && parent.contains(correctedTarget)) {
return correctedTarget;
}

console.error('aria-hidden', target, 'in not contained inside', parent, '. Doing nothing');

return null;
})
.filter((x): x is Element => Boolean(x));

/**
* Marks everything except given node(or nodes) as aria-hidden
* @param {Element | Element[]} originalTarget - elements to keep on the page
Expand All @@ -29,7 +51,7 @@ const applyAttributeToOthers = (
markerName: string,
controlAttribute: string
): Undo => {
const targets = Array.isArray(originalTarget) ? originalTarget : [originalTarget];
const targets = correctTargets(parentNode, Array.isArray(originalTarget) ? originalTarget : [originalTarget]);

if (!markerMap[markerName]) {
markerMap[markerName] = new WeakMap();
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"target": "es5",
"target": "es6",
"moduleResolution": "node",
"lib": ["dom", "es5", "scripthost", "es2015.collection", "es2015.symbol", "es2015.iterable", "es2015.promise"],
"jsx": "react"
}
Expand Down

0 comments on commit 3dd2b66

Please sign in to comment.