diff --git a/src/__tests__/ariaAttributes.js b/src/__tests__/ariaAttributes.js index 2f0b76de..1ceb210d 100644 --- a/src/__tests__/ariaAttributes.js +++ b/src/__tests__/ariaAttributes.js @@ -177,6 +177,31 @@ test('`pressed: true|false` matches `pressed` elements with proper role', () => expect(getByRole('button', {pressed: false})).toBeInTheDocument() }) +test.each([ + ['true', true], + ['false', false], + ['date', 'date'], + ['location', 'location'], + ['page', 'page'], + ['step', 'step'], + ['time', 'time'], +])( + '`aria-current="%s"` matches `current: %j` elements with proper role', + (ariaCurrentValue, filterByValue) => { + const {getByRole} = renderIntoDocument( + ` `, + ) + expect(getByRole('link', {current: filterByValue})).toBeInTheDocument() + }, +) + +test('Missing `aria-current` matches `current: false`', () => { + const {getByRole} = renderIntoDocument( + `StartAbout`, + ) + expect(getByRole('link', {current: false})).toBeInTheDocument() +}) + test('`level` matches elements with `heading` role', () => { const {getAllByRole, queryByRole} = renderIntoDocument( `
diff --git a/src/queries/role.js b/src/queries/role.js index 0b1ebd94..8f08ae67 100644 --- a/src/queries/role.js +++ b/src/queries/role.js @@ -4,6 +4,7 @@ import { computeAriaSelected, computeAriaChecked, computeAriaPressed, + computeAriaCurrent, computeAriaExpanded, computeHeadingLevel, getImplicitAriaRoles, @@ -35,6 +36,7 @@ function queryAllByRole( selected, checked, pressed, + current, level, expanded, } = {}, @@ -64,6 +66,16 @@ function queryAllByRole( } } + if (current !== undefined) { + /* istanbul ignore next */ + // guard against unknown roles + // All currently released ARIA versions support `aria-current` on all roles. + // Leaving this for symetry and forward compatibility + if (allRoles.get(role)?.props['aria-current'] === undefined) { + throw new Error(`"aria-current" is not supported on role "${role}".`) + } + } + if (level !== undefined) { // guard against using `level` option with any role other than `heading` if (role !== 'heading') { @@ -124,6 +136,9 @@ function queryAllByRole( if (pressed !== undefined) { return pressed === computeAriaPressed(element) } + if (current !== undefined) { + return current === computeAriaCurrent(element) + } if (expanded !== undefined) { return expanded === computeAriaExpanded(element) } diff --git a/src/role-helpers.js b/src/role-helpers.js index 58fdfb6f..7de55932 100644 --- a/src/role-helpers.js +++ b/src/role-helpers.js @@ -247,6 +247,19 @@ function computeAriaPressed(element) { return checkBooleanAttribute(element, 'aria-pressed') } +/** + * @param {Element} element - + * @returns {boolean | string | null} - + */ +function computeAriaCurrent(element) { + // https://www.w3.org/TR/wai-aria-1.1/#aria-current + return ( + checkBooleanAttribute(element, 'aria-current') ?? + element.getAttribute('aria-current') ?? + false + ) +} + /** * @param {Element} element - * @returns {boolean | undefined} - false/true if (not)expanded, undefined if not expand-able @@ -301,6 +314,7 @@ export { computeAriaSelected, computeAriaChecked, computeAriaPressed, + computeAriaCurrent, computeAriaExpanded, computeHeadingLevel, } diff --git a/types/queries.d.ts b/types/queries.d.ts index 42777ffd..1d20f0b5 100644 --- a/types/queries.d.ts +++ b/types/queries.d.ts @@ -88,6 +88,10 @@ export interface ByRoleOptions extends MatcherOptions { * pressed in the accessibility tree, i.e., `aria-pressed="true"` */ pressed?: boolean + /** + * Filters elements by their `aria-current` state. `true` and `false` match `aria-current="true"` and `aria-current="false"` (as well as a missing `aria-current` attribute) respectively. + */ + current?: boolean | string /** * If true only includes elements in the query set that are marked as * expanded in the accessibility tree, i.e., `aria-expanded="true"`