mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-02 10:34:27 +03:00
fix(role): compute <output> accessible name from labels (#27415)
Fixes: https://github.com/microsoft/playwright/issues/27403
This commit is contained in:
parent
13cca1db3d
commit
ae08d03d75
@ -508,15 +508,8 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN
|
||||
if (element.tagName === 'INPUT' && (element as HTMLInputElement).type === 'image') {
|
||||
options.visitedElements.add(element);
|
||||
const labels = (element as HTMLInputElement).labels || [];
|
||||
if (labels.length && options.embeddedInLabelledBy === 'none') {
|
||||
return [...labels].map(label => getElementAccessibleNameInternal(label, {
|
||||
...options,
|
||||
embeddedInLabel: 'self',
|
||||
embeddedInTextAlternativeElement: false,
|
||||
embeddedInLabelledBy: 'none',
|
||||
embeddedInTargetElement: 'none',
|
||||
})).filter(accessibleName => !!accessibleName).join(' ');
|
||||
}
|
||||
if (labels.length && options.embeddedInLabelledBy === 'none')
|
||||
return getAccessibleNameFromAssociatedLabels(labels, options);
|
||||
const alt = element.getAttribute('alt') || '';
|
||||
if (alt.trim())
|
||||
return alt;
|
||||
@ -532,18 +525,20 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN
|
||||
if (!labelledBy && 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(' ');
|
||||
}
|
||||
if (labels.length)
|
||||
return getAccessibleNameFromAssociatedLabels(labels, options);
|
||||
// From here, fallthrough to step 2f.
|
||||
}
|
||||
|
||||
// https://w3c.github.io/html-aam/#output-element-accessible-name-computation
|
||||
if (!labelledBy && element.tagName === 'OUTPUT') {
|
||||
options.visitedElements.add(element);
|
||||
const labels = (element as HTMLOutputElement).labels || [];
|
||||
if (labels.length)
|
||||
return getAccessibleNameFromAssociatedLabels(labels, options);
|
||||
return element.getAttribute('title') || '';
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -552,15 +547,8 @@ function getElementAccessibleNameInternal(element: Element, options: AccessibleN
|
||||
if (!labelledBy && (element.tagName === 'TEXTAREA' || element.tagName === 'SELECT' || element.tagName === 'INPUT')) {
|
||||
options.visitedElements.add(element);
|
||||
const labels = (element as (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)).labels || [];
|
||||
if (labels.length) {
|
||||
return [...labels].map(label => getElementAccessibleNameInternal(label, {
|
||||
...options,
|
||||
embeddedInLabel: 'self',
|
||||
embeddedInTextAlternativeElement: false,
|
||||
embeddedInLabelledBy: 'none',
|
||||
embeddedInTargetElement: 'none',
|
||||
})).filter(accessibleName => !!accessibleName).join(' ');
|
||||
}
|
||||
if (labels.length)
|
||||
return getAccessibleNameFromAssociatedLabels(labels, options);
|
||||
|
||||
const usePlaceholder = (element.tagName === 'INPUT' && ['text', 'password', 'search', 'tel', 'email', 'url'].includes((element as HTMLInputElement).type)) || element.tagName === 'TEXTAREA';
|
||||
const placeholder = element.getAttribute('placeholder') || '';
|
||||
@ -836,6 +824,16 @@ function hasExplicitAriaDisabled(element: Element | undefined): boolean {
|
||||
return hasExplicitAriaDisabled(parentElementOrShadowHost(element));
|
||||
}
|
||||
|
||||
function getAccessibleNameFromAssociatedLabels(labels: Iterable<HTMLLabelElement>, options: AccessibleNameOptions) {
|
||||
return [...labels].map(label => getElementAccessibleNameInternal(label, {
|
||||
...options,
|
||||
embeddedInLabel: 'self',
|
||||
embeddedInTextAlternativeElement: false,
|
||||
embeddedInLabelledBy: 'none',
|
||||
embeddedInTargetElement: 'none',
|
||||
})).filter(accessibleName => !!accessibleName).join(' ');
|
||||
}
|
||||
|
||||
let cacheAccessibleName: Map<Element, string> | undefined;
|
||||
let cacheAccessibleNameHidden: Map<Element, string> | undefined;
|
||||
let cacheIsHidden: Map<Element, boolean> | undefined;
|
||||
|
@ -479,3 +479,8 @@ test('hidden with shadow dom slots', async ({ page }) => {
|
||||
`<button>visible2</button>`,
|
||||
]);
|
||||
});
|
||||
|
||||
test('should support output accessible name', async ({ page }) => {
|
||||
await page.setContent(`<label>Output1<output>output</output></label>`);
|
||||
await expect(page.getByRole('status', { name: 'Output1' })).toBeVisible();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user