Skip to content

Commit 6e3392b

Browse files
authored
fix(a11y): track inert elements as hidden (microsoft#36947)
1 parent eea3f4f commit 6e3392b

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

packages/injected/src/roleUtils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,10 @@ export function isElementHiddenForAria(element: Element): boolean {
284284
const isOptionInsideSelect = element.nodeName === 'OPTION' && !!element.closest('select');
285285
if (!isOptionInsideSelect && !isSlot && !isElementStyleVisibilityVisible(element, style))
286286
return true;
287-
return belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element);
287+
return belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(element);
288288
}
289289

290-
function belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element: Element): boolean {
290+
function belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(element: Element): boolean {
291291
let hidden = cacheIsHidden?.get(element);
292292
if (hidden === undefined) {
293293
hidden = false;
@@ -298,17 +298,17 @@ function belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element: Element): boolean
298298
if (element.parentElement && element.parentElement.shadowRoot && !element.assignedSlot)
299299
hidden = true;
300300

301-
// display:none and aria-hidden=true are considered hidden for aria.
301+
// display:none and aria-hidden=true and inert are considered hidden for aria.
302302
if (!hidden) {
303303
const style = getElementComputedStyle(element);
304-
hidden = !style || style.display === 'none' || getAriaBoolean(element.getAttribute('aria-hidden')) === true;
304+
hidden = !style || style.display === 'none' || getAriaBoolean(element.getAttribute('aria-hidden')) === true || element.getAttribute('inert') !== null;
305305
}
306306

307307
// Check recursively.
308308
if (!hidden) {
309309
const parent = parentElementOrShadowHost(element);
310310
if (parent)
311-
hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlotted(parent);
311+
hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(parent);
312312
}
313313
cacheIsHidden?.set(element, hidden);
314314
}

tests/library/role-utils.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,32 @@ test('should support search element', async ({ page }) => {
564564
await expect.soft(page.getByRole('search', { name: 'example' })).toBeVisible();
565565
});
566566

567+
test('should consider inert elements to be hidden', async ({ page }) => {
568+
await page.setContent(`
569+
<div aria-hidden="true">
570+
<button type="button">First</button>
571+
</div>
572+
<div inert>
573+
<button type="button">Second</button>
574+
</div>
575+
<button type="button" inert>Third</button>
576+
`);
577+
578+
await expect(page.getByRole('button', { name: 'First' })).toHaveCount(0);
579+
await expect(page.getByRole('button', { name: 'Second' })).toHaveCount(0);
580+
await expect(page.getByRole('button', { name: 'Third' })).toHaveCount(0);
581+
582+
await expect(
583+
page.getByRole('button', { name: 'First', includeHidden: true })
584+
).toHaveCount(1);
585+
await expect(
586+
page.getByRole('button', { name: 'Second', includeHidden: true })
587+
).toHaveCount(1);
588+
await expect(
589+
page.getByRole('button', { name: 'Third', includeHidden: true })
590+
).toHaveCount(1);
591+
});
592+
567593
function toArray(x: any): any[] {
568594
return Array.isArray(x) ? x : [x];
569595
}

0 commit comments

Comments
 (0)