diff --git a/src/config.ts b/src/config.ts index 0168ba88..079d78c3 100644 --- a/src/config.ts +++ b/src/config.ts @@ -24,6 +24,8 @@ let config: InternalConfig = { eventWrapper: cb => cb(), // default value for the `hidden` option in `ByRole` queries defaultHidden: false, + // default value for the `hidden` option in `ByText` queries + defaultExcludeHiddenText: false, // default value for the `ignore` option in `ByText` queries defaultIgnore: 'script, style', // showOriginalStackTrace flag to show the full error stack traces for async errors diff --git a/src/queries/role.js b/src/queries/role.js index a46d8a2b..cde33d19 100644 --- a/src/queries/role.js +++ b/src/queries/role.js @@ -13,7 +13,7 @@ import { getImplicitAriaRoles, prettyRoles, isInaccessible, - isSubtreeInaccessible, + getCachedIsSubtreeInaccessible, } from '../role-helpers' import {wrapAllByQueryWithSuggestion} from '../query-helpers' import {checkContainerType} from '../helpers' @@ -94,14 +94,7 @@ function queryAllByRole( } } - const subtreeIsInaccessibleCache = new WeakMap() - function cachedIsSubtreeInaccessible(element) { - if (!subtreeIsInaccessibleCache.has(element)) { - subtreeIsInaccessibleCache.set(element, isSubtreeInaccessible(element)) - } - - return subtreeIsInaccessibleCache.get(element) - } + const cachedIsSubtreeInaccessible = getCachedIsSubtreeInaccessible() return Array.from( container.querySelectorAll( diff --git a/src/queries/text.ts b/src/queries/text.ts index 41594ce7..a97457ce 100644 --- a/src/queries/text.ts +++ b/src/queries/text.ts @@ -6,6 +6,7 @@ import { SelectorMatcherOptions, Matcher, } from '../../types' +import {getCachedIsSubtreeInaccessible, isInaccessible} from '../role-helpers' import { fuzzyMatches, matches, @@ -25,6 +26,7 @@ const queryAllByText: AllByText = ( trim, ignore = getConfig().defaultIgnore, normalizer, + excludeHidden = getConfig().defaultExcludeHiddenText, } = {}, ) => { checkContainerType(container) @@ -34,13 +36,27 @@ const queryAllByText: AllByText = ( if (typeof container.matches === 'function' && container.matches(selector)) { baseArray = [container] } + const cachedIsSubtreeInaccessible = getCachedIsSubtreeInaccessible() + return ( [ ...baseArray, ...Array.from(container.querySelectorAll(selector)), ] // TODO: `matches` according lib.dom.d.ts can get only `string` but according our code it can handle also boolean :) - .filter(node => !ignore || !node.matches(ignore as string)) + .filter(node => { + if (ignore && node.matches(ignore as string)) { + return false + } + + if (!excludeHidden) { + return true + } + + return !isInaccessible(node, { + isSubtreeInaccessible: cachedIsSubtreeInaccessible, + }) + }) .filter(node => matcher(getNodeText(node), node, text, matchNormalizer)) ) } diff --git a/src/role-helpers.js b/src/role-helpers.js index 500bcdd2..d9d769d6 100644 --- a/src/role-helpers.js +++ b/src/role-helpers.js @@ -29,6 +29,20 @@ function isSubtreeInaccessible(element) { return false } +/** + * @returns {function (element: Element): boolean} + */ +function getCachedIsSubtreeInaccessible() { + const subtreeIsInaccessibleCache = new WeakMap() + return function cachedIsSubtreeInaccessible(element) { + if (!subtreeIsInaccessibleCache.has(element)) { + subtreeIsInaccessibleCache.set(element, isSubtreeInaccessible(element)) + } + + return subtreeIsInaccessibleCache.get(element) + } +} + /** * Partial implementation https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion * which should only be used for elements with a non-presentational role i.e. @@ -330,6 +344,7 @@ export { logRoles, getImplicitAriaRoles, isSubtreeInaccessible, + getCachedIsSubtreeInaccessible, prettyRoles, isInaccessible, computeAriaSelected, diff --git a/types/config.d.ts b/types/config.d.ts index c3617ef0..b0f41c1b 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -12,6 +12,7 @@ export interface Config { asyncUtilTimeout: number computedStyleSupportsPseudoElements: boolean defaultHidden: boolean + defaultExcludeHiddenText: boolean /** default value for the `ignore` option in `ByText` queries */ defaultIgnore: string showOriginalStackTrace: boolean diff --git a/types/queries.d.ts b/types/queries.d.ts index 1fb66b7d..94efd004 100644 --- a/types/queries.d.ts +++ b/types/queries.d.ts @@ -40,29 +40,37 @@ export type QueryByText = ( options?: SelectorMatcherOptions, ) => T | null +export interface ByTextOptions extends SelectorMatcherOptions { + /** + * If true exclude elements in the query set that are usually excluded from + * the accessibility tree. + */ + excludeHidden?: boolean +} + export type AllByText = ( container: HTMLElement, id: Matcher, - options?: SelectorMatcherOptions, + options?: ByTextOptions, ) => T[] export type FindAllByText = ( container: HTMLElement, id: Matcher, - options?: SelectorMatcherOptions, + options?: ByTextOptions, waitForElementOptions?: waitForOptions, ) => Promise export type GetByText = ( container: HTMLElement, id: Matcher, - options?: SelectorMatcherOptions, + options?: ByTextOptions, ) => T export type FindByText = ( container: HTMLElement, id: Matcher, - options?: SelectorMatcherOptions, + options?: ByTextOptions, waitForElementOptions?: waitForOptions, ) => Promise