/** * @flow */ import { dom, roles as rolesMap, } from 'aria-query'; import type { Node } from 'ast-types-flow'; import { getProp, getLiteralPropValue } from 'jsx-ast-utils'; import includes from 'array-includes'; const roles = [...rolesMap.keys()]; const nonInteractiveRoles = roles .filter(name => !rolesMap.get(name).abstract) .filter(name => !rolesMap.get(name).superClass.some( klasses => includes(klasses, 'widget')), ); /** * Returns boolean indicating whether the given element has a role * that is associated with a non-interactive component. Non-interactive roles * include `listitem`, `article`, or `dialog`. These are roles that indicate * for the most part containers. * * Elements with these roles should not respond or handle user interactions. * For example, an `onClick` handler should not be assigned to an element with * the role `listitem`. An element inside the `listitem`, like a button or a * link, should handle the click. * * This utility returns true for elements that are assigned a non-interactive * role. It will return false for elements that do not have a role. So whereas * a `div` might be considered non-interactive, for the purpose of this utility, * it is considered neither interactive nor non-interactive -- a determination * cannot be made in this case and false is returned. */ const isNonInteractiveRole = ( tagName: string, attributes: Array, ): boolean => { // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. if (!dom.has(tagName)) { return false; } const role = getLiteralPropValue(getProp(attributes, 'role')); let isNonInteractive = false; const normalizedValues = String(role).toLowerCase().split(' '); const validRoles = normalizedValues.reduce(( accumulator: Array, name: string, ) => { if (includes(roles, name)) { accumulator.push(name); } return accumulator; }, []); if (validRoles.length > 0) { // The first role value is a series takes precedence. isNonInteractive = includes(nonInteractiveRoles, validRoles[0]); } return isNonInteractive; }; export default isNonInteractiveRole;