fix(role): make sure to ignore style/script/noscript/template (#32231)
Even when these are a part of a hidden `aria-labelledby` traversal, all browsers ignore them anyway.
This commit is contained in:
parent
b599335404
commit
b4a9b247b4
|
|
@ -261,12 +261,16 @@ function getAriaBoolean(attr: string | null) {
|
||||||
return attr === null ? undefined : attr.toLowerCase() === 'true';
|
return attr === null ? undefined : attr.toLowerCase() === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isElementIgnoredForAria(element: Element) {
|
||||||
|
return ['STYLE', 'SCRIPT', 'NOSCRIPT', 'TEMPLATE'].includes(elementSafeTagName(element));
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion, but including "none" and "presentation" roles
|
// https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion, but including "none" and "presentation" roles
|
||||||
// Not implemented:
|
// Not implemented:
|
||||||
// `Any descendants of elements that have the characteristic "Children Presentational: True"`
|
// `Any descendants of elements that have the characteristic "Children Presentational: True"`
|
||||||
// https://www.w3.org/TR/wai-aria-1.2/#aria-hidden
|
// https://www.w3.org/TR/wai-aria-1.2/#aria-hidden
|
||||||
export function isElementHiddenForAria(element: Element): boolean {
|
export function isElementHiddenForAria(element: Element): boolean {
|
||||||
if (['STYLE', 'SCRIPT', 'NOSCRIPT', 'TEMPLATE'].includes(elementSafeTagName(element)))
|
if (isElementIgnoredForAria(element))
|
||||||
return true;
|
return true;
|
||||||
const style = getElementComputedStyle(element);
|
const style = getElementComputedStyle(element);
|
||||||
const isSlot = element.nodeName === 'SLOT';
|
const isSlot = element.nodeName === 'SLOT';
|
||||||
|
|
@ -496,14 +500,17 @@ function getTextAlternativeInternal(element: Element, options: AccessibleNameOpt
|
||||||
// step 2a. Hidden Not Referenced: If the current node is hidden and is:
|
// step 2a. Hidden Not Referenced: If the current node is hidden and is:
|
||||||
// Not part of an aria-labelledby or aria-describedby traversal, where the node directly referenced by that relation was hidden.
|
// Not part of an aria-labelledby or aria-describedby traversal, where the node directly referenced by that relation was hidden.
|
||||||
// Nor part of a native host language text alternative element (e.g. label in HTML) or attribute traversal, where the root of that traversal was hidden.
|
// Nor part of a native host language text alternative element (e.g. label in HTML) or attribute traversal, where the root of that traversal was hidden.
|
||||||
if (!options.includeHidden &&
|
if (!options.includeHidden) {
|
||||||
!options.embeddedInLabelledBy?.hidden &&
|
const isEmbeddedInHiddenReferenceTraversal =
|
||||||
!options.embeddedInDescribedBy?.hidden &&
|
!!options.embeddedInLabelledBy?.hidden ||
|
||||||
!options?.embeddedInNativeTextAlternative?.hidden &&
|
!!options.embeddedInDescribedBy?.hidden ||
|
||||||
!options?.embeddedInLabel?.hidden &&
|
!!options.embeddedInNativeTextAlternative?.hidden ||
|
||||||
isElementHiddenForAria(element)) {
|
!!options.embeddedInLabel?.hidden;
|
||||||
options.visitedElements.add(element);
|
if (isElementIgnoredForAria(element) ||
|
||||||
return '';
|
(!isEmbeddedInHiddenReferenceTraversal && isElementHiddenForAria(element))) {
|
||||||
|
options.visitedElements.add(element);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelledBy = getAriaLabelledByElements(element);
|
const labelledBy = getAriaLabelledByElements(element);
|
||||||
|
|
|
||||||
|
|
@ -462,6 +462,19 @@ test('should work with form and tricky input names', async ({ page }) => {
|
||||||
expect.soft(await getNameAndRole(page, 'form')).toEqual({ role: 'form', name: 'my form' });
|
expect.soft(await getNameAndRole(page, 'form')).toEqual({ role: 'form', name: 'my form' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should ignore stylesheet from hidden aria-labelledby subtree', async ({ page }) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<div id=mylabel style="display:none">
|
||||||
|
<template shadowrootmode=open>
|
||||||
|
<style>span { color: red; }</style>
|
||||||
|
<span>hello</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<input aria-labelledby=mylabel type=text>
|
||||||
|
`);
|
||||||
|
expect.soft(await getNameAndRole(page, 'input')).toEqual({ role: 'textbox', name: 'hello' });
|
||||||
|
});
|
||||||
|
|
||||||
function toArray(x: any): any[] {
|
function toArray(x: any): any[] {
|
||||||
return Array.isArray(x) ? x : [x];
|
return Array.isArray(x) ? x : [x];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue