Skip to content

Commit eb364cc

Browse files
authored
Add aria-expanded to getAccessibilityTree and <details>/<summary> (#451)
1 parent cf3ad32 commit eb364cc

3 files changed

Lines changed: 93 additions & 1 deletion

File tree

.changeset/silver-actors-jam.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'pleasantest': major
3+
---
4+
5+
Added `aria-expanded` support to `getAccessibilityTree` and fix handling for `<details>`/`<summary>`
6+
7+
Now, elements which have the `aria-expanded` attribute will represent the state of that attribute in accessibility tree snapshots. `<details>`/`<summary>` elements will represent their expanded state in the tree as well.
8+
9+
Also, for collapsed `<details>`/`<summary>` elements, the hidden content is now hidden in the accessibility tree, to match screen reader behavior.

src/accessibility/browser.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@ const getElementAccessibilityState = (element: Element): AccessibilityState => {
4444
if (
4545
(element as HTMLElement).hidden ||
4646
element.getAttribute('aria-hidden') === 'true' ||
47-
computedStyle.display === 'none'
47+
computedStyle.display === 'none' ||
48+
(element.parentElement?.tagName === 'DETAILS' &&
49+
!(element.parentElement as HTMLDetailsElement).open &&
50+
!(
51+
element.tagName === 'SUMMARY' &&
52+
element.parentElement.firstElementChild === element
53+
))
4854
)
4955
return AccessibilityState.SelfAndDescendentsExcludedFromTree;
5056

@@ -97,6 +103,18 @@ export const getAccessibilityTree = (
97103
if (selfIsInAccessibilityTree) {
98104
const name = computeAccessibleName(element);
99105
if (name) text += ` "${name}"`;
106+
if (
107+
element.ariaExpanded === 'true' ||
108+
(element.tagName === 'SUMMARY' &&
109+
(element.parentElement as HTMLDetailsElement).open)
110+
)
111+
text += ` (expanded=true)`;
112+
if (
113+
element.ariaExpanded === 'false' ||
114+
(element.tagName === 'SUMMARY' &&
115+
!(element.parentElement as HTMLDetailsElement).open)
116+
)
117+
text += ` (expanded=false)`;
100118
if (document.activeElement === element) text += ` (focused)`;
101119
if (includeDescriptions) {
102120
const description = computeAccessibleDescription(element);

tests/accessibility/getAccessibilityTree.test.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,68 @@ test(
281281
`);
282282
}),
283283
);
284+
285+
test(
286+
'<details>/<summary>',
287+
withBrowser(async ({ utils, page, screen, user }) => {
288+
await utils.injectHTML(`
289+
<details>
290+
<summary>
291+
Click me!
292+
<h1>Tags in summary do not preserve their semantic meaning</h1>
293+
</summary>
294+
<p>Some content</p>
295+
<h2>Tags in details do preserve their semantic meaning</h2>
296+
</details>
297+
`);
298+
299+
// Starts collapsed
300+
expect(await getAccessibilityTree(page)).toMatchInlineSnapshot(`
301+
document
302+
group
303+
button "Click me! Tags in summary do not preserve their semantic meaning" (expanded=false)
304+
`);
305+
306+
const toggle = await screen.getByText(/click me!/i);
307+
await user.click(toggle);
308+
309+
// After toggling it should be expanded
310+
expect(await getAccessibilityTree(page)).toMatchInlineSnapshot(`
311+
document
312+
group
313+
button "Click me! Tags in summary do not preserve their semantic meaning" (expanded=true) (focused)
314+
text "Some content"
315+
heading "Tags in details do preserve their semantic meaning"
316+
text "Tags in details do preserve their semantic meaning"
317+
`);
318+
}),
319+
);
320+
321+
test(
322+
'aria-expanded and aria-collapsed',
323+
withBrowser(async ({ utils, page }) => {
324+
await utils.injectHTML(`
325+
<button aria-expanded="false">Click me!</button>
326+
`);
327+
expect(await getAccessibilityTree(page)).toMatchInlineSnapshot(`
328+
document
329+
button "Click me!" (expanded=false)
330+
`);
331+
332+
await utils.injectHTML(`
333+
<button aria-expanded="true">Click me!</button>
334+
`);
335+
expect(await getAccessibilityTree(page)).toMatchInlineSnapshot(`
336+
document
337+
button "Click me!" (expanded=true)
338+
`);
339+
340+
await utils.injectHTML(`
341+
<button>Click me!</button>
342+
`);
343+
expect(await getAccessibilityTree(page)).toMatchInlineSnapshot(`
344+
document
345+
button "Click me!"
346+
`);
347+
}),
348+
);

0 commit comments

Comments
 (0)