Skip to content

Commit a1daca5

Browse files
nolanlawsoneps1lon
andauthored
fix: Follow aria-labelledby and aria-describedby if they point to elements in the same shadow root (#827)
* fix: fix co-located elements in shadow DOM Partially addresses #768 * chore: fix prettier * get rid of seemingly redundant "co-located" term * add changeset Co-authored-by: eps1lon <[email protected]>
1 parent bc4c891 commit a1daca5

File tree

4 files changed

+39
-1
lines changed

4 files changed

+39
-1
lines changed

.changeset/violet-cooks-promise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"dom-accessibility-api": patch
3+
---
4+
5+
Follow aria-labelledby and aria-describedby if they point to elements in the same shadow root.

sources/__tests__/accessible-description.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,17 @@ describe("wpt copies", () => {
6363
testMarkup(markup, expectedAccessibleName)
6464
);
6565
});
66+
67+
describe("content in shadow DOM", () => {
68+
it("works for aria-labelledby on elements in same shadow root", () => {
69+
const container = renderIntoDocument("<div></div>");
70+
const div = container.querySelector("div");
71+
div.attachShadow({ mode: "open" }).innerHTML = `
72+
<div id="theDescription">This is a button</div>
73+
<button aria-describedby="theDescription">Click me</button>
74+
`;
75+
76+
const button = div.shadowRoot.querySelector("button");
77+
expect(computeAccessibleDescription(button)).toEqual("This is a button");
78+
});
79+
});

sources/__tests__/accessible-name.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,3 +589,17 @@ describe("options.hidden", () => {
589589
);
590590
});
591591
});
592+
593+
describe("content in shadow DOM", () => {
594+
it("works for aria-labelledby on elements in same shadow root", () => {
595+
const container = renderIntoDocument("<div></div>");
596+
const div = container.querySelector("div");
597+
div.attachShadow({ mode: "open" }).innerHTML = `
598+
<label id="theLabel">Type here</label>
599+
<input type="text" aria-labelledby="theLabel">
600+
`;
601+
602+
const input = div.shadowRoot.querySelector("input");
603+
expect(computeAccessibleName(input)).toEqual("Type here");
604+
});
605+
});

sources/util.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,13 @@ export function queryIdRefs(node: Node, attributeName: string): Element[] {
9090
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- safe due to hasAttribute check
9191
const ids = node.getAttribute(attributeName)!.split(" ");
9292

93+
// Browsers that don't support shadow DOM won't have getRootNode
94+
const root = node.getRootNode
95+
? (node.getRootNode() as Document | ShadowRoot)
96+
: node.ownerDocument;
97+
9398
return ids
94-
.map((id) => node.ownerDocument.getElementById(id))
99+
.map((id) => root.getElementById(id))
95100
.filter(
96101
(element: Element | null): element is Element => element !== null
97102
// TODO: why does this not narrow?

0 commit comments

Comments
 (0)