/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
// Below code forked from dom-accessibility-api
const tagToRoleMappings = {
ARTICLE: 'article',
ASIDE: 'complementary',
BODY: 'document',
BUTTON: 'button',
DATALIST: 'listbox',
DD: 'definition',
DETAILS: 'group',
DIALOG: 'dialog',
DT: 'term',
FIELDSET: 'group',
FIGURE: 'figure',
// WARNING: Only with an accessible name
FORM: 'form',
FOOTER: 'contentinfo',
H1: 'heading',
H2: 'heading',
H3: 'heading',
H4: 'heading',
H5: 'heading',
H6: 'heading',
HEADER: 'banner',
HR: 'separator',
LEGEND: 'legend',
LI: 'listitem',
MATH: 'math',
MAIN: 'main',
MENU: 'list',
NAV: 'navigation',
OL: 'list',
OPTGROUP: 'group',
// WARNING: Only in certain context
OPTION: 'option',
OUTPUT: 'status',
PROGRESS: 'progressbar',
// WARNING: Only with an accessible name
SECTION: 'region',
SUMMARY: 'button',
TABLE: 'table',
TBODY: 'rowgroup',
TEXTAREA: 'textbox',
TFOOT: 'rowgroup',
// WARNING: Only in certain context
TD: 'cell',
TH: 'columnheader',
THEAD: 'rowgroup',
TR: 'row',
UL: 'list',
};
function getImplicitRole(element: Element): string | null {
const mappedByTag = tagToRoleMappings[element.tagName];
if (mappedByTag !== undefined) {
return mappedByTag;
}
switch (element.tagName) {
case 'A':
case 'AREA':
case 'LINK':
if (element.hasAttribute('href')) {
return 'link';
}
break;
case 'IMG':
if ((element.getAttribute('alt') || '').length > 0) {
return 'img';
}
break;
case 'INPUT': {
const type = (element: any).type;
switch (type) {
case 'button':
case 'image':
case 'reset':
case 'submit':
return 'button';
case 'checkbox':
case 'radio':
return type;
case 'range':
return 'slider';
case 'email':
case 'tel':
case 'text':
case 'url':
if (element.hasAttribute('list')) {
return 'combobox';
}
return 'textbox';
case 'search':
if (element.hasAttribute('list')) {
return 'combobox';
}
return 'searchbox';
default:
return null;
}
}
case 'SELECT':
if (element.hasAttribute('multiple') || (element: any).size > 1) {
return 'listbox';
}
return 'combobox';
}
return null;
}
function getExplicitRoles(element: Element): Array<string> | null {
const role = element.getAttribute('role');
if (role) {
return role.trim().split(' ');
}
return null;
}
// https://w3c.github.io/html-aria/#document-conformance-requirements-for-use-of-aria-attributes-in-html
export function hasRole(element: Element, role: string): boolean {
const explicitRoles = getExplicitRoles(element);
if (explicitRoles !== null && explicitRoles.indexOf(role) >= 0) {
return true;
}
return role === getImplicitRole(element);
}