From 6ecbf7746538d82f1f5715527cecf4cb7da36fd4 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 1 Aug 2024 16:57:29 -0400 Subject: [PATCH] feat: Add strictVisibility check option to ByRole queries --- src/__tests__/element-queries.js | 31 +++++++++++++++++++++++++++++++ src/queries/role.ts | 16 +++++++++++----- src/role-helpers.js | 21 +++++++++++++++++++++ types/queries.d.ts | 11 +++++++++++ 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index a8e00011..056918dd 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -813,6 +813,37 @@ test('queryAllByRole returns semantic html elements', () => { expect(queryAllByRole('listbox')).toHaveLength(1) }) +test('getAllByRole matchers with strictVisibility enabled and disabled', () => { + const {getAllByRole} = render(` + +
+ +
+
+ +
+
+
+ +
+
+
+ +
+ `) + + const defaultResults = getAllByRole('button') + expect(defaultResults).toHaveLength(1) + + const strictResults = getAllByRole('button', {strictVisibility: true}) + expect(strictResults).toHaveLength(1) + + const looseResults = getAllByRole('button', {strictVisibility: false}) + expect(looseResults).toHaveLength(1) +}) + test('getAll* matchers return an array', () => { const { getAllByAltText, diff --git a/src/queries/role.ts b/src/queries/role.ts index e4dd395f..caa15a16 100644 --- a/src/queries/role.ts +++ b/src/queries/role.ts @@ -24,6 +24,7 @@ import { prettyRoles, isInaccessible, isSubtreeInaccessible, + isInaccessibleLoose, } from '../role-helpers' import {wrapAllByQueryWithSuggestion} from '../query-helpers' import {checkContainerType} from '../helpers' @@ -54,6 +55,7 @@ const queryAllByRole: AllByRole = ( current, level, expanded, + strictVisibilityCheck = true, value: { now: valueNow, min: valueMin, @@ -296,11 +298,15 @@ const queryAllByRole: AllByRole = ( ) }) .filter(element => { - return hidden === false - ? isInaccessible(element, { - isSubtreeInaccessible: cachedIsSubtreeInaccessible, - }) === false - : true + if (strictVisibilityCheck) { + return hidden === false + ? isInaccessible(element, { + isSubtreeInaccessible: cachedIsSubtreeInaccessible, + }) === false + : true + } else { + return hidden === false ? isInaccessibleLoose(element) === false : true + } }) } diff --git a/src/role-helpers.js b/src/role-helpers.js index 8d7e7cb0..25e4f2d9 100644 --- a/src/role-helpers.js +++ b/src/role-helpers.js @@ -65,6 +65,26 @@ function isInaccessible(element, options = {}) { return false } +/** + * A fast check to see if an element is inaccessible. + * @param {Element} element + * @returns {boolean} true if exluded, otherwise false + */ +function isInaccessibleLoose(element) { + const explictlyHidden = + element.style.visibility === 'hidden' || element.style.display === 'none' + if (explictlyHidden) { + return true + } + + const ariaHidden = element.getAttribute('aria-hidden') === 'true' + if (ariaHidden) { + return true + } + + return false +} + function getImplicitAriaRoles(currentNode) { // eslint bug here: // eslint-disable-next-line no-unused-vars @@ -382,6 +402,7 @@ export { isSubtreeInaccessible, prettyRoles, isInaccessible, + isInaccessibleLoose, computeAriaSelected, computeAriaBusy, computeAriaChecked, diff --git a/types/queries.d.ts b/types/queries.d.ts index c6ce9054..549b921e 100644 --- a/types/queries.d.ts +++ b/types/queries.d.ts @@ -110,6 +110,17 @@ export interface ByRoleOptions { * the `aria-level` attribute. */ level?: number + + /** + * Whether or not to strictly check the visibliity of the element. Doing so + * can cause issues with the performance of tests, because it requires the DOM tree + * is traversed, and the `getComputedStyle` function is called on each element. + * + * If you're finding that your tests are slow, you may want to disable this option. + * @default true + */ + strictVisibilityCheck?: boolean + value?: { now?: number min?: number