fix(role): update accessible name calculation for native buttons (#22124)

There is a new section in the spec:

https://w3c.github.io/html-aam/#button-element-accessible-name-computation

Fixes #21808.
This commit is contained in:
Dmitry Gozman 2023-03-31 14:17:18 -07:00 committed by GitHub
parent fbdafc5fe3
commit 1fbefa694f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 4 deletions

View File

@ -463,7 +463,7 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN
// step 2e.
if (!['presentation', 'none'].includes(role)) {
// https://w3c.github.io/html-aam/#input-type-button-input-type-submit-and-input-type-reset
// https://w3c.github.io/html-aam/#input-type-button-input-type-submit-and-input-type-reset-accessible-name-computation
if (element.tagName === 'INPUT' && ['button', 'submit', 'reset'].includes((element as HTMLInputElement).type)) {
options.visitedElements.add(element);
const value = (element as HTMLInputElement).value || '';
@ -477,7 +477,7 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN
return title;
}
// https://w3c.github.io/html-aam/#input-type-image
// https://w3c.github.io/html-aam/#input-type-image-accessible-name-computation
if (element.tagName === 'INPUT' && (element as HTMLInputElement).type === 'image') {
options.visitedElements.add(element);
const alt = element.getAttribute('alt') || '';
@ -504,8 +504,24 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN
return 'Submit';
}
// https://w3c.github.io/html-aam/#input-type-text-input-type-password-input-type-search-input-type-tel-input-type-url-and-textarea-element
// https://w3c.github.io/html-aam/#other-form-elements
// https://w3c.github.io/html-aam/#button-element-accessible-name-computation
if (element.tagName === 'BUTTON') {
options.visitedElements.add(element);
const labels = (element as HTMLButtonElement).labels || [];
if (labels.length) {
return [...labels].map(label => getElementAccessibleNameInternal(label, {
...options,
embeddedInLabel: 'self',
embeddedInTextAlternativeElement: false,
embeddedInLabelledBy: 'none',
embeddedInTargetElement: 'none',
})).filter(accessibleName => !!accessibleName).join(' ');
}
// From here, fallthrough to step 2f.
}
// https://w3c.github.io/html-aam/#input-type-text-input-type-password-input-type-number-input-type-search-input-type-tel-input-type-email-input-type-url-and-textarea-element-accessible-name-computation
// https://w3c.github.io/html-aam/#other-form-elements-accessible-name-computation
// For "other form elements", we count select and any other input.
if (element.tagName === 'TEXTAREA' || element.tagName === 'SELECT' || element.tagName === 'INPUT') {
options.visitedElements.add(element);

View File

@ -240,6 +240,32 @@ test('svg title', async ({ page }) => {
expect.soft(await getNameAndRole(page, 'a')).toEqual({ role: 'link', name: 'a link' });
});
test('native controls', async ({ page }) => {
await page.setContent(`
<label for="text1">TEXT1</label><input id="text1" type=text>
<input id="text2" type=text title="TEXT2">
<input id="text3" type=text placeholder="TEXT3">
<label for="image1">IMAGE1</label><input id="image1" type=image>
<input id="image2" type=image alt="IMAGE2">
<label for="button1">BUTTON1</label><button id="button1" role="combobox">button</button>
<button id="button2" role="combobox">BUTTON2</button>
<button id="button3">BUTTON3</button>
<button id="button4" title="BUTTON4"></button>
`);
expect.soft(await getNameAndRole(page, '#text1')).toEqual({ role: 'textbox', name: 'TEXT1' });
expect.soft(await getNameAndRole(page, '#text2')).toEqual({ role: 'textbox', name: 'TEXT2' });
expect.soft(await getNameAndRole(page, '#text3')).toEqual({ role: 'textbox', name: 'TEXT3' });
expect.soft(await getNameAndRole(page, '#image1')).toEqual({ role: 'button', name: 'IMAGE1' });
expect.soft(await getNameAndRole(page, '#image2')).toEqual({ role: 'button', name: 'IMAGE2' });
expect.soft(await getNameAndRole(page, '#button1')).toEqual({ role: 'combobox', name: 'BUTTON1' });
expect.soft(await getNameAndRole(page, '#button2')).toEqual({ role: 'combobox', name: '' });
expect.soft(await getNameAndRole(page, '#button3')).toEqual({ role: 'button', name: 'BUTTON3' });
expect.soft(await getNameAndRole(page, '#button4')).toEqual({ role: 'button', name: 'BUTTON4' });
});
function toArray(x: any): any[] {
return Array.isArray(x) ? x : [x];
}