cherry-pick(#18929): fix(role selector): expanded=false does not match elements without aria-expanded
Fixes #18920.
This commit is contained in:
parent
226eedf019
commit
bbe27dc564
|
|
@ -72,6 +72,11 @@ function validateAttributes(attrs: AttributeSelectorPart[], role: string) {
|
||||||
validateSupportedRole(attr.name, kAriaExpandedRoles, role);
|
validateSupportedRole(attr.name, kAriaExpandedRoles, role);
|
||||||
validateSupportedValues(attr, [true, false]);
|
validateSupportedValues(attr, [true, false]);
|
||||||
validateSupportedOp(attr, ['<truthy>', '=']);
|
validateSupportedOp(attr, ['<truthy>', '=']);
|
||||||
|
if (attr.op === '<truthy>') {
|
||||||
|
// Do not match "none" in "treeitem[expanded]".
|
||||||
|
attr.op = '=';
|
||||||
|
attr.value = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'level': {
|
case 'level': {
|
||||||
|
|
|
||||||
|
|
@ -670,14 +670,20 @@ export function getAriaPressed(element: Element): boolean | 'mixed' {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const kAriaExpandedRoles = ['application', 'button', 'checkbox', 'combobox', 'gridcell', 'link', 'listbox', 'menuitem', 'row', 'rowheader', 'tab', 'treeitem', 'columnheader', 'menuitemcheckbox', 'menuitemradio', 'rowheader', 'switch'];
|
export const kAriaExpandedRoles = ['application', 'button', 'checkbox', 'combobox', 'gridcell', 'link', 'listbox', 'menuitem', 'row', 'rowheader', 'tab', 'treeitem', 'columnheader', 'menuitemcheckbox', 'menuitemradio', 'rowheader', 'switch'];
|
||||||
export function getAriaExpanded(element: Element): boolean {
|
export function getAriaExpanded(element: Element): boolean | 'none' {
|
||||||
// https://www.w3.org/TR/wai-aria-1.2/#aria-expanded
|
// https://www.w3.org/TR/wai-aria-1.2/#aria-expanded
|
||||||
// https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings
|
// https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings
|
||||||
if (element.tagName === 'DETAILS')
|
if (element.tagName === 'DETAILS')
|
||||||
return (element as HTMLDetailsElement).open;
|
return (element as HTMLDetailsElement).open;
|
||||||
if (kAriaExpandedRoles.includes(getAriaRole(element) || ''))
|
if (kAriaExpandedRoles.includes(getAriaRole(element) || '')) {
|
||||||
return getAriaBoolean(element.getAttribute('aria-expanded')) === true;
|
const expanded = element.getAttribute('aria-expanded');
|
||||||
return false;
|
if (expanded === null)
|
||||||
|
return 'none';
|
||||||
|
if (expanded === 'true')
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const kAriaLevelRoles = ['heading', 'listitem', 'row', 'treeitem'];
|
export const kAriaLevelRoles = ['heading', 'listitem', 'row', 'treeitem'];
|
||||||
|
|
|
||||||
|
|
@ -169,26 +169,42 @@ test('should support pressed', async ({ page }) => {
|
||||||
|
|
||||||
test('should support expanded', async ({ page }) => {
|
test('should support expanded', async ({ page }) => {
|
||||||
await page.setContent(`
|
await page.setContent(`
|
||||||
<button>Hi</button>
|
<div role="treeitem">Hi</div>
|
||||||
<button aria-expanded="true">Hello</button>
|
<div role="treeitem" aria-expanded="true">Hello</div>
|
||||||
<button aria-expanded="false">Bye</button>
|
<div role="treeitem" aria-expanded="false">Bye</div>
|
||||||
`);
|
`);
|
||||||
expect(await page.locator(`role=button[expanded]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
|
||||||
`<button aria-expanded="true">Hello</button>`,
|
expect(await page.locator('role=treeitem').evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
|
`<div role="treeitem">Hi</div>`,
|
||||||
|
`<div role="treeitem" aria-expanded="true">Hello</div>`,
|
||||||
|
`<div role="treeitem" aria-expanded="false">Bye</div>`,
|
||||||
]);
|
]);
|
||||||
expect(await page.locator(`role=button[expanded=true]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
expect(await page.getByRole('treeitem').evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
`<button aria-expanded="true">Hello</button>`,
|
`<div role="treeitem">Hi</div>`,
|
||||||
|
`<div role="treeitem" aria-expanded="true">Hello</div>`,
|
||||||
|
`<div role="treeitem" aria-expanded="false">Bye</div>`,
|
||||||
]);
|
]);
|
||||||
expect(await page.getByRole('button', { expanded: true }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
|
||||||
`<button aria-expanded="true">Hello</button>`,
|
expect(await page.locator(`role=treeitem[expanded]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
|
`<div role="treeitem" aria-expanded="true">Hello</div>`,
|
||||||
]);
|
]);
|
||||||
expect(await page.locator(`role=button[expanded=false]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
expect(await page.locator(`role=treeitem[expanded=true]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
`<button>Hi</button>`,
|
`<div role="treeitem" aria-expanded="true">Hello</div>`,
|
||||||
`<button aria-expanded="false">Bye</button>`,
|
|
||||||
]);
|
]);
|
||||||
expect(await page.getByRole('button', { expanded: false }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
expect(await page.getByRole('treeitem', { expanded: true }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
`<button>Hi</button>`,
|
`<div role="treeitem" aria-expanded="true">Hello</div>`,
|
||||||
`<button aria-expanded="false">Bye</button>`,
|
]);
|
||||||
|
|
||||||
|
expect(await page.locator(`role=treeitem[expanded=false]`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
|
`<div role="treeitem" aria-expanded="false">Bye</div>`,
|
||||||
|
]);
|
||||||
|
expect(await page.getByRole('treeitem', { expanded: false }).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
|
`<div role="treeitem" aria-expanded="false">Bye</div>`,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Workaround for expanded="none".
|
||||||
|
expect(await page.locator(`[role=treeitem]:not([aria-expanded])`).evaluateAll(els => els.map(e => e.outerHTML))).toEqual([
|
||||||
|
`<div role="treeitem">Hi</div>`,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -403,4 +419,7 @@ test('errors', async ({ page }) => {
|
||||||
|
|
||||||
const e7 = await page.$('role=button[name]').catch(e => e);
|
const e7 = await page.$('role=button[name]').catch(e => e);
|
||||||
expect(e7.message).toContain(`"name" attribute must have a value`);
|
expect(e7.message).toContain(`"name" attribute must have a value`);
|
||||||
|
|
||||||
|
const e8 = await page.$('role=treeitem[expanded="none"]').catch(e => e);
|
||||||
|
expect(e8.message).toContain(`"expanded" must be one of true, false`);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue