From 52de82403752bb2ccbcac3379925650a0112d4af Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 12 Aug 2023 22:09:36 -0700 Subject: [PATCH] [Refactor] use `es-iterator-helpers` - also, maximally avoid iterator spreads --- .../rules/aria-unsupported-elements-test.js | 4 +- .../role-has-required-aria-props-test.js | 8 +++- .../rules/role-supports-aria-props-test.js | 38 ++++++++++--------- __tests__/src/util/isDOMElement-test.js | 4 +- __tests__/src/util/isFocusable-test.js | 2 +- package.json | 1 + .../aria-activedescendant-has-tabindex.js | 4 +- src/rules/aria-props.js | 2 +- src/rules/aria-role.js | 5 ++- src/rules/aria-unsupported-elements.js | 4 +- src/rules/click-events-have-key-events.js | 4 +- src/rules/interactive-supports-focus.js | 4 +- ...eractive-element-to-noninteractive-role.js | 4 +- .../no-noninteractive-element-interactions.js | 3 +- ...interactive-element-to-interactive-role.js | 4 +- src/rules/no-static-element-interactions.js | 4 +- src/rules/role-supports-aria-props.js | 11 +++--- src/util/isAbstractRole.js | 9 +++-- src/util/isDOMElement.js | 5 +-- src/util/isInteractiveElement.js | 30 +++++++++------ src/util/isNonInteractiveElement.js | 29 +++++++------- src/util/isNonInteractiveRole.js | 5 +-- 22 files changed, 94 insertions(+), 90 deletions(-) diff --git a/__tests__/src/rules/aria-unsupported-elements-test.js b/__tests__/src/rules/aria-unsupported-elements-test.js index 31b348841..0b7533325 100644 --- a/__tests__/src/rules/aria-unsupported-elements-test.js +++ b/__tests__/src/rules/aria-unsupported-elements-test.js @@ -69,7 +69,7 @@ const invalidAriaValidityTests = domElements })); ruleTester.run('aria-unsupported-elements', rule, { - valid: parsers.all([].concat(roleValidityTests.concat(ariaValidityTests))).map(parserOptionsMapper), - invalid: parsers.all([].concat(invalidRoleValidityTests.concat(invalidAriaValidityTests))) + valid: parsers.all([].concat(roleValidityTests, ariaValidityTests)).map(parserOptionsMapper), + invalid: parsers.all([].concat(invalidRoleValidityTests, invalidAriaValidityTests)) .map(parserOptionsMapper), }); diff --git a/__tests__/src/rules/role-has-required-aria-props-test.js b/__tests__/src/rules/role-has-required-aria-props-test.js index 5a06a3f62..fc4f53e45 100644 --- a/__tests__/src/rules/role-has-required-aria-props-test.js +++ b/__tests__/src/rules/role-has-required-aria-props-test.js @@ -10,6 +10,10 @@ import { roles } from 'aria-query'; import { RuleTester } from 'eslint'; +import iterFrom from 'es-iterator-helpers/Iterator.from'; +import map from 'es-iterator-helpers/Iterator.prototype.map'; +import toArray from 'es-iterator-helpers/Iterator.prototype.toArray'; + import parserOptionsMapper from '../../__util__/parserOptionsMapper'; import parsers from '../../__util__/helpers/parsers'; import rule from '../../../src/rules/role-has-required-aria-props'; @@ -38,7 +42,7 @@ const componentsSettings = { }; // Create basic test cases using all valid role types. -const basicValidityTests = [...roles.keys()].map((role) => { +const basicValidityTests = toArray(map(iterFrom(roles.keys()), (role) => { const { requiredProps: requiredPropKeyValues, } = roles.get(role); @@ -48,7 +52,7 @@ const basicValidityTests = [...roles.keys()].map((role) => { return { code: `
`, }; -}); +})); ruleTester.run('role-has-required-aria-props', rule, { valid: parsers.all([].concat( diff --git a/__tests__/src/rules/role-supports-aria-props-test.js b/__tests__/src/rules/role-supports-aria-props-test.js index 888ff0732..339b94054 100644 --- a/__tests__/src/rules/role-supports-aria-props-test.js +++ b/__tests__/src/rules/role-supports-aria-props-test.js @@ -14,6 +14,11 @@ import { import { RuleTester } from 'eslint'; import { version as eslintVersion } from 'eslint/package.json'; import semver from 'semver'; +import iterFrom from 'es-iterator-helpers/Iterator.from'; +import filter from 'es-iterator-helpers/Iterator.prototype.filter'; +import map from 'es-iterator-helpers/Iterator.prototype.map'; +import toArray from 'es-iterator-helpers/Iterator.prototype.toArray'; + import parserOptionsMapper from '../../__util__/parserOptionsMapper'; import parsers from '../../__util__/helpers/parsers'; import rule from '../../../src/rules/role-supports-aria-props'; @@ -26,8 +31,7 @@ const ruleTester = new RuleTester(); const generateErrorMessage = (attr, role, tag, isImplicit) => { if (isImplicit) { - return `The attribute ${attr} is not supported by the role ${role}. \ -This role is implicit on the element ${tag}.`; + return `The attribute ${attr} is not supported by the role ${role}. This role is implicit on the element ${tag}.`; } return `The attribute ${attr} is not supported by the role ${role}.`; @@ -46,30 +50,28 @@ const componentsSettings = { }, }; -const nonAbstractRoles = [...roles.keys()].filter((role) => roles.get(role).abstract === false); +const nonAbstractRoles = toArray(filter(iterFrom(roles.keys()), (role) => roles.get(role).abstract === false)); const createTests = (rolesNames) => rolesNames.reduce((tests, role) => { const { props: propKeyValues, } = roles.get(role); const validPropsForRole = Object.keys(propKeyValues); - const invalidPropsForRole = [...aria.keys()] - .map((attribute) => attribute.toLowerCase()) - .filter((attribute) => validPropsForRole.indexOf(attribute) === -1); + const invalidPropsForRole = filter( + map(iterFrom(aria.keys()), (attribute) => attribute.toLowerCase()), + (attribute) => validPropsForRole.indexOf(attribute) === -1, + ); const normalRole = role.toLowerCase(); - const allTests = []; - - allTests[0] = tests[0].concat(validPropsForRole.map((prop) => ({ - code: `
`, - }))); - - allTests[1] = tests[1].concat(invalidPropsForRole.map((prop) => ({ - code: `
`, - errors: [errorMessage(prop.toLowerCase(), normalRole, 'div', false)], - }))); - - return allTests; + return [ + tests[0].concat(validPropsForRole.map((prop) => ({ + code: `
`, + }))), + tests[1].concat(toArray(map(invalidPropsForRole, (prop) => ({ + code: `
`, + errors: [errorMessage(prop.toLowerCase(), normalRole, 'div', false)], + })))), + ]; }, [[], []]); const [validTests, invalidTests] = createTests(nonAbstractRoles); diff --git a/__tests__/src/util/isDOMElement-test.js b/__tests__/src/util/isDOMElement-test.js index 1245805bc..ff1e24e49 100644 --- a/__tests__/src/util/isDOMElement-test.js +++ b/__tests__/src/util/isDOMElement-test.js @@ -4,11 +4,9 @@ import { elementType } from 'jsx-ast-utils'; import isDOMElement from '../../../src/util/isDOMElement'; import JSXElementMock from '../../../__mocks__/JSXElementMock'; -const domElements = [...dom.keys()]; - describe('isDOMElement', () => { describe('DOM elements', () => { - domElements.forEach((el) => { + dom.forEach((_, el) => { it(`should identify ${el} as a DOM element`, () => { const element = JSXElementMock(el); expect(isDOMElement(elementType(element.openingElement))) diff --git a/__tests__/src/util/isFocusable-test.js b/__tests__/src/util/isFocusable-test.js index 920891f65..76819c297 100644 --- a/__tests__/src/util/isFocusable-test.js +++ b/__tests__/src/util/isFocusable-test.js @@ -9,7 +9,7 @@ import { import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock'; function mergeTabIndex(index, attributes) { - return [...attributes, JSXAttributeMock('tabIndex', index)]; + return [].concat(attributes, JSXAttributeMock('tabIndex', index)); } describe('isFocusable', () => { diff --git a/package.json b/package.json index cff464c14..c47ed84bd 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "axobject-query": "^3.2.1", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.15", "hasown": "^2.0.0", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", diff --git a/src/rules/aria-activedescendant-has-tabindex.js b/src/rules/aria-activedescendant-has-tabindex.js index 0f229e2fb..e240ec0bb 100644 --- a/src/rules/aria-activedescendant-has-tabindex.js +++ b/src/rules/aria-activedescendant-has-tabindex.js @@ -18,8 +18,6 @@ const errorMessage = 'An element that manages focus with `aria-activedescendant` const schema = generateObjSchema(); -const domElements = [...dom.keys()]; - export default { meta: { docs: { @@ -42,7 +40,7 @@ export default { const type = elementType(node); // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. - if (domElements.indexOf(type) === -1) { + if (!dom.has(type)) { return; } const tabIndex = getTabIndex(getProp(attributes, 'tabIndex')); diff --git a/src/rules/aria-props.js b/src/rules/aria-props.js index 4c9f62d3b..d63aa499b 100644 --- a/src/rules/aria-props.js +++ b/src/rules/aria-props.js @@ -45,7 +45,7 @@ export default { return; } - const isValid = ariaAttributes.indexOf(name) > -1; + const isValid = aria.has(name); if (isValid === false) { context.report({ diff --git a/src/rules/aria-role.js b/src/rules/aria-role.js index 4fa499b57..6b85ae855 100644 --- a/src/rules/aria-role.js +++ b/src/rules/aria-role.js @@ -9,6 +9,9 @@ import { dom, roles } from 'aria-query'; import { getLiteralPropValue, propName } from 'jsx-ast-utils'; +import iterFrom from 'es-iterator-helpers/Iterator.from'; +import filter from 'es-iterator-helpers/Iterator.prototype.filter'; + import getElementType from '../util/getElementType'; import { generateObjSchema } from '../util/schemas'; @@ -28,7 +31,7 @@ const schema = generateObjSchema({ }, }); -const validRoles = new Set([...roles.keys()].filter((role) => roles.get(role).abstract === false)); +const validRoles = new Set(filter(iterFrom(roles.keys()), (role) => roles.get(role).abstract === false)); export default { meta: { diff --git a/src/rules/aria-unsupported-elements.js b/src/rules/aria-unsupported-elements.js index 778a6abd1..b58e1b3f4 100644 --- a/src/rules/aria-unsupported-elements.js +++ b/src/rules/aria-unsupported-elements.js @@ -47,7 +47,7 @@ export default { return; } - const invalidAttributes = [...aria.keys(), 'role']; + const invalidAttributes = new Set([...aria.keys(), 'role']); node.attributes.forEach((prop) => { if (prop.type === 'JSXSpreadAttribute') { @@ -56,7 +56,7 @@ export default { const name = propName(prop).toLowerCase(); - if (invalidAttributes.indexOf(name) > -1) { + if (invalidAttributes.has(name)) { context.report({ node, message: errorMessage(name), diff --git a/src/rules/click-events-have-key-events.js b/src/rules/click-events-have-key-events.js index aa76349de..6c9cf90fe 100644 --- a/src/rules/click-events-have-key-events.js +++ b/src/rules/click-events-have-key-events.js @@ -9,7 +9,6 @@ import { dom } from 'aria-query'; import { getProp, hasAnyProp } from 'jsx-ast-utils'; -import includes from 'array-includes'; import { generateObjSchema } from '../util/schemas'; import getElementType from '../util/getElementType'; import isHiddenFromScreenReader from '../util/isHiddenFromScreenReader'; @@ -19,7 +18,6 @@ import isPresentationRole from '../util/isPresentationRole'; const errorMessage = 'Visible, non-interactive elements with click handlers must have at least one keyboard listener.'; const schema = generateObjSchema(); -const domElements = [...dom.keys()]; export default { meta: { @@ -42,7 +40,7 @@ export default { const type = elementType(node); const requiredProps = ['onkeydown', 'onkeyup', 'onkeypress']; - if (!includes(domElements, type)) { + if (!dom.has(type)) { // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. return; diff --git a/src/rules/interactive-supports-focus.js b/src/rules/interactive-supports-focus.js index 2dec63006..e872c5d10 100644 --- a/src/rules/interactive-supports-focus.js +++ b/src/rules/interactive-supports-focus.js @@ -36,12 +36,12 @@ import getTabIndex from '../util/getTabIndex'; // ---------------------------------------------------------------------------- const schema = generateObjSchema({ + // TODO: convert to use iterFilter and iterFrom tabbable: enumArraySchema([...roles.keys()].filter((name) => ( !roles.get(name).abstract && roles.get(name).superClass.some((klasses) => includes(klasses, 'widget')) ))), }); -const domElements = [...dom.keys()]; const interactiveProps = [].concat( eventHandlersByType.mouse, @@ -69,7 +69,7 @@ export default ({ const hasInteractiveProps = hasAnyProp(attributes, interactiveProps); const hasTabindex = getTabIndex(getProp(attributes, 'tabIndex')) !== undefined; - if (!includes(domElements, type)) { + if (!dom.has(type)) { // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. return; diff --git a/src/rules/no-interactive-element-to-noninteractive-role.js b/src/rules/no-interactive-element-to-noninteractive-role.js index 7561b4e32..99be51264 100644 --- a/src/rules/no-interactive-element-to-noninteractive-role.js +++ b/src/rules/no-interactive-element-to-noninteractive-role.js @@ -27,8 +27,6 @@ import isPresentationRole from '../util/isPresentationRole'; const errorMessage = 'Interactive elements should not be assigned non-interactive roles.'; -const domElements = [...dom.keys()]; - export default ({ meta: { docs: { @@ -62,7 +60,7 @@ export default ({ const type = elementType(node); const role = getLiteralPropValue(getProp(node.attributes, 'role')); - if (!includes(domElements, type)) { + if (!dom.has(type)) { // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. return; diff --git a/src/rules/no-noninteractive-element-interactions.js b/src/rules/no-noninteractive-element-interactions.js index 92e82a0a0..f31c0cddf 100644 --- a/src/rules/no-noninteractive-element-interactions.js +++ b/src/rules/no-noninteractive-element-interactions.js @@ -32,7 +32,6 @@ import isPresentationRole from '../util/isPresentationRole'; const errorMessage = 'Non-interactive elements should not be assigned mouse or keyboard event listeners.'; -const domElements = [...dom.keys()]; const defaultInteractiveProps = [].concat( eventHandlersByType.focus, eventHandlersByType.image, @@ -72,7 +71,7 @@ export default ({ && getPropValue(getProp(attributes, prop)) != null )); - if (!includes(domElements, type)) { + if (!dom.has(type)) { // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. return; diff --git a/src/rules/no-noninteractive-element-to-interactive-role.js b/src/rules/no-noninteractive-element-to-interactive-role.js index 57286a7e2..ab9c040ea 100644 --- a/src/rules/no-noninteractive-element-to-interactive-role.js +++ b/src/rules/no-noninteractive-element-to-interactive-role.js @@ -25,8 +25,6 @@ import isInteractiveRole from '../util/isInteractiveRole'; const errorMessage = 'Non-interactive elements should not be assigned interactive roles.'; -const domElements = [...dom.keys()]; - export default ({ meta: { docs: { @@ -60,7 +58,7 @@ export default ({ const type = elementType(node); const role = getExplicitRole(type, node.attributes); - if (!includes(domElements, type)) { + if (!dom.has(type)) { // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. return; diff --git a/src/rules/no-static-element-interactions.js b/src/rules/no-static-element-interactions.js index e48c4bd0f..038d7c1e4 100644 --- a/src/rules/no-static-element-interactions.js +++ b/src/rules/no-static-element-interactions.js @@ -16,7 +16,6 @@ import { hasProp, } from 'jsx-ast-utils'; import type { JSXOpeningElement } from 'ast-types-flow'; -import includes from 'array-includes'; import type { ESLintConfig, ESLintContext, ESLintVisitorSelectorConfig } from '../../flow/eslint'; import { arraySchema, generateObjSchema } from '../util/schemas'; import getElementType from '../util/getElementType'; @@ -31,7 +30,6 @@ import isPresentationRole from '../util/isPresentationRole'; const errorMessage = 'Avoid non-native interactive elements. If using native HTML is not possible, add an appropriate role and support for tabbing, mouse, keyboard, and touch inputs to an interactive content element.'; -const domElements = [...dom.keys()]; const defaultInteractiveProps = [].concat( eventHandlersByType.focus, eventHandlersByType.keyboard, @@ -69,7 +67,7 @@ export default ({ && getPropValue(getProp(attributes, prop)) != null )); - if (!includes(domElements, type)) { + if (!dom.has(type)) { // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. return; diff --git a/src/rules/role-supports-aria-props.js b/src/rules/role-supports-aria-props.js index ec961032f..f9ada8f63 100644 --- a/src/rules/role-supports-aria-props.js +++ b/src/rules/role-supports-aria-props.js @@ -18,14 +18,16 @@ import { getPropValue, propName, } from 'jsx-ast-utils'; +import iterFrom from 'es-iterator-helpers/Iterator.from'; +import filter from 'es-iterator-helpers/Iterator.prototype.filter'; + import { generateObjSchema } from '../util/schemas'; import getElementType from '../util/getElementType'; import getImplicitRole from '../util/getImplicitRole'; const errorMessage = (attr, role, tag, isImplicit) => { if (isImplicit) { - return `The attribute ${attr} is not supported by the role ${role}. \ -This role is implicit on the element ${tag}.`; + return `The attribute ${attr} is not supported by the role ${role}. This role is implicit on the element ${tag}.`; } return `The attribute ${attr} is not supported by the role ${role}.`; @@ -66,15 +68,14 @@ export default { const { props: propKeyValues, } = roles.get(roleValue); - const invalidAriaPropsForRole = [...aria.keys()] - .filter((attribute) => !(attribute in propKeyValues)); + const invalidAriaPropsForRole = new Set(filter(iterFrom(aria.keys()), (attribute) => !(attribute in propKeyValues))); node.attributes.filter((prop) => ( getPropValue(prop) != null // Ignore the attribute if its value is null or undefined. && prop.type !== 'JSXSpreadAttribute' // Ignore the attribute if it's a spread. )).forEach((prop) => { const name = propName(prop); - if (invalidAriaPropsForRole.indexOf(name) > -1) { + if (invalidAriaPropsForRole.has(name)) { context.report({ node, message: errorMessage(name, roleValue, type, isImplicit), diff --git a/src/util/isAbstractRole.js b/src/util/isAbstractRole.js index 75bc48c22..723ff1bc8 100644 --- a/src/util/isAbstractRole.js +++ b/src/util/isAbstractRole.js @@ -3,16 +3,17 @@ import { roles, } from 'aria-query'; import { getProp, getLiteralPropValue } from 'jsx-ast-utils'; +import iterFrom from 'es-iterator-helpers/Iterator.from'; +import filter from 'es-iterator-helpers/Iterator.prototype.filter'; -const abstractRoles = new Set([...roles.keys()] - .filter((role) => roles.get(role).abstract)); +const abstractRoles = new Set(filter(iterFrom(roles.keys()), (role) => roles.get(role).abstract)); -const DOMElements = [...dom.keys()]; +const DOMElements = new Set(dom.keys()); const isAbstractRole = (tagName, attributes) => { // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. - if (DOMElements.indexOf(tagName) === -1) { + if (!DOMElements.has(tagName)) { return false; } diff --git a/src/util/isDOMElement.js b/src/util/isDOMElement.js index 216ed51ca..82e286d4a 100644 --- a/src/util/isDOMElement.js +++ b/src/util/isDOMElement.js @@ -2,15 +2,12 @@ * @flow */ import { dom } from 'aria-query'; -import includes from 'array-includes'; - -const domElements = [...dom.keys()]; /** * Returns boolean indicating whether the given element is a DOM element. */ const isDOMElement = ( tagName: string, -): boolean => includes(domElements, tagName); +): boolean => dom.has(tagName); export default isDOMElement; diff --git a/src/util/isInteractiveElement.js b/src/util/isInteractiveElement.js index 3475d678c..0f9714540 100644 --- a/src/util/isInteractiveElement.js +++ b/src/util/isInteractiveElement.js @@ -13,9 +13,13 @@ import { } from 'axobject-query'; import includes from 'array-includes'; import flatMap from 'array.prototype.flatmap'; +import iterFrom from 'es-iterator-helpers/Iterator.from'; +// import iterFlatMap from 'es-iterator-helpers/Iterator.prototype.flatMap'; +import filter from 'es-iterator-helpers/Iterator.prototype.filter'; +import some from 'es-iterator-helpers/Iterator.prototype.some'; + import attributesComparator from './attributesComparator'; -const domKeys = [...dom.keys()]; const roleKeys = [...roles.keys()]; const elementRoleEntries = [...elementRoles]; @@ -51,22 +55,24 @@ const interactiveRoles = new Set(roleKeys 'toolbar', )); -const nonInteractiveElementRoleSchemas = flatMap( +// TODO: convert to use iterFlatMap and iterFrom +const interactiveElementRoleSchemas = flatMap( elementRoleEntries, - ([elementSchema, roleSet]) => ([...roleSet].every((role): boolean => nonInteractiveRoles.has(role)) ? [elementSchema] : []), + ([elementSchema, rolesArr]) => (rolesArr.some((role): boolean => interactiveRoles.has(role)) ? [elementSchema] : []), ); -const interactiveElementRoleSchemas = flatMap( +// TODO: convert to use iterFlatMap and iterFrom +const nonInteractiveElementRoleSchemas = flatMap( elementRoleEntries, - ([elementSchema, roleSet]) => ([...roleSet].some((role): boolean => interactiveRoles.has(role)) ? [elementSchema] : []), + ([elementSchema, rolesArr]) => (rolesArr.every((role): boolean => nonInteractiveRoles.has(role)) ? [elementSchema] : []), ); -const interactiveAXObjects = new Set([...AXObjects.keys()] - .filter((name) => AXObjects.get(name).type === 'widget')); +const interactiveAXObjects = new Set(filter(iterFrom(AXObjects.keys()), (name) => AXObjects.get(name).type === 'widget')); +// TODO: convert to use iterFlatMap and iterFrom const interactiveElementAXObjectSchemas = flatMap( [...elementAXObjects], - ([elementSchema, AXObjectSet]) => ([...AXObjectSet].every((role): boolean => interactiveAXObjects.has(role)) ? [elementSchema] : []), + ([elementSchema, AXObjectsArr]) => (AXObjectsArr.every((role): boolean => interactiveAXObjects.has(role)) ? [elementSchema] : []), ); function checkIsInteractiveElement(tagName, attributes): boolean { @@ -78,18 +84,18 @@ function checkIsInteractiveElement(tagName, attributes): boolean { } // Check in elementRoles for inherent interactive role associations for // this element. - const isInherentInteractiveElement = interactiveElementRoleSchemas.some(elementSchemaMatcher); + const isInherentInteractiveElement = some(iterFrom(interactiveElementRoleSchemas), elementSchemaMatcher); if (isInherentInteractiveElement) { return true; } // Check in elementRoles for inherent non-interactive role associations for // this element. - const isInherentNonInteractiveElement = nonInteractiveElementRoleSchemas.some(elementSchemaMatcher); + const isInherentNonInteractiveElement = some(iterFrom(nonInteractiveElementRoleSchemas), elementSchemaMatcher); if (isInherentNonInteractiveElement) { return false; } // Check in elementAXObjects for AX Tree associations for this element. - const isInteractiveAXElement = interactiveElementAXObjectSchemas.some(elementSchemaMatcher); + const isInteractiveAXElement = some(iterFrom(interactiveElementAXObjectSchemas), elementSchemaMatcher); if (isInteractiveAXElement) { return true; } @@ -109,7 +115,7 @@ const isInteractiveElement = ( ): boolean => { // Do not test higher level JSX components, as we do not know what // low-level DOM element this maps to. - if (!includes(domKeys, tagName)) { + if (!dom.has(tagName)) { return false; } diff --git a/src/util/isNonInteractiveElement.js b/src/util/isNonInteractiveElement.js index 85e1037a9..a37326dab 100644 --- a/src/util/isNonInteractiveElement.js +++ b/src/util/isNonInteractiveElement.js @@ -14,6 +14,10 @@ import { import type { Node } from 'ast-types-flow'; import includes from 'array-includes'; import flatMap from 'array.prototype.flatmap'; +import iterFrom from 'es-iterator-helpers/Iterator.from'; +// import iterFlatMap from 'es-iterator-helpers/Iterator.prototype.flatMap'; +import filter from 'es-iterator-helpers/Iterator.prototype.filter'; +import some from 'es-iterator-helpers/Iterator.prototype.some'; import attributesComparator from './attributesComparator'; @@ -62,22 +66,24 @@ const interactiveRoles = new Set(roleKeys 'toolbar', )); -const nonInteractiveElementRoleSchemas = flatMap( +// TODO: convert to use iterFlatMap and iterFrom +const interactiveElementRoleSchemas = flatMap( elementRoleEntries, - ([elementSchema, roleSet]) => ([...roleSet].every((role): boolean => nonInteractiveRoles.has(role)) ? [elementSchema] : []), + ([elementSchema, rolesArr]) => (rolesArr.some((role): boolean => interactiveRoles.has(role)) ? [elementSchema] : []), ); -const interactiveElementRoleSchemas = flatMap( +// TODO: convert to use iterFlatMap and iterFrom +const nonInteractiveElementRoleSchemas = flatMap( elementRoleEntries, - ([elementSchema, roleSet]) => ([...roleSet].some((role): boolean => interactiveRoles.has(role)) ? [elementSchema] : []), + ([elementSchema, rolesArr]) => (rolesArr.every((role): boolean => nonInteractiveRoles.has(role)) ? [elementSchema] : []), ); -const nonInteractiveAXObjects = new Set([...AXObjects.keys()] - .filter((name) => includes(['window', 'structure'], AXObjects.get(name).type))); +const nonInteractiveAXObjects = new Set(filter(iterFrom(AXObjects.keys()), (name) => includes(['window', 'structure'], AXObjects.get(name).type))); +// TODO: convert to use iterFlatMap and iterFrom const nonInteractiveElementAXObjectSchemas = flatMap( [...elementAXObjects], - ([elementSchema, AXObjectSet]) => ([...AXObjectSet].every((role): boolean => nonInteractiveAXObjects.has(role)) ? [elementSchema] : []), + ([elementSchema, AXObjectsArr]) => (AXObjectsArr.every((role): boolean => nonInteractiveAXObjects.has(role)) ? [elementSchema] : []), ); function checkIsNonInteractiveElement(tagName, attributes): boolean { @@ -89,21 +95,18 @@ function checkIsNonInteractiveElement(tagName, attributes): boolean { } // Check in elementRoles for inherent non-interactive role associations for // this element. - const isInherentNonInteractiveElement = nonInteractiveElementRoleSchemas - .some(elementSchemaMatcher); + const isInherentNonInteractiveElement = some(iterFrom(nonInteractiveElementRoleSchemas), elementSchemaMatcher); if (isInherentNonInteractiveElement) { return true; } // Check in elementRoles for inherent interactive role associations for // this element. - const isInherentInteractiveElement = interactiveElementRoleSchemas - .some(elementSchemaMatcher); + const isInherentInteractiveElement = some(iterFrom(interactiveElementRoleSchemas), elementSchemaMatcher); if (isInherentInteractiveElement) { return false; } // Check in elementAXObjects for AX Tree associations for this element. - const isNonInteractiveAXElement = nonInteractiveElementAXObjectSchemas - .some(elementSchemaMatcher); + const isNonInteractiveAXElement = some(iterFrom(nonInteractiveElementAXObjectSchemas), elementSchemaMatcher); if (isNonInteractiveAXElement) { return true; } diff --git a/src/util/isNonInteractiveRole.js b/src/util/isNonInteractiveRole.js index 54aff8550..355f3aa9c 100644 --- a/src/util/isNonInteractiveRole.js +++ b/src/util/isNonInteractiveRole.js @@ -11,8 +11,7 @@ import { getProp, getLiteralPropValue } from 'jsx-ast-utils'; import includes from 'array-includes'; import flatMap from 'array.prototype.flatmap'; -const roles = [...rolesMap.keys()]; -const nonInteractiveRoles = roles.filter((name) => ( +const nonInteractiveRoles = [...rolesMap.keys()].filter((name) => ( !rolesMap.get(name).abstract && !rolesMap.get(name).superClass.some((klasses) => includes(klasses, 'widget')) )); @@ -51,7 +50,7 @@ const isNonInteractiveRole = ( const normalizedValues = String(role).toLowerCase().split(' '); const validRoles = flatMap( normalizedValues, - (name: string) => (includes(roles, name) ? [name] : []), + (name: string) => (rolesMap.has(name) ? [name] : []), ); if (validRoles.length > 0) { // The first role value is a series takes precedence.