Skip to content

Commit

Permalink
feat(ByRole): Allow filter by disabled state
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon committed Mar 8, 2023
1 parent d1ff495 commit eeebfc2
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 0 deletions.
54 changes: 54 additions & 0 deletions src/__tests__/ariaAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,57 @@ test('`expanded: true|false` matches `expanded` elements with proper role', () =
expect(getByRole('button', {expanded: true})).toBeInTheDocument()
expect(getByRole('button', {expanded: false})).toBeInTheDocument()
})

test('`disabled` throws on unsupported roles', () => {
const {getByRole} = render(
`<div role="alert" aria-disabled="true">Hello, Dave!</div>`,
)
expect(() =>
getByRole('alert', {disabled: true}),
).toThrowErrorMatchingInlineSnapshot(
`"aria-disabled" is not supported on role "alert".`,
)
})

test('`disabled: true|false` matches `disabled` buttons', () => {
const {getByRole} = renderIntoDocument(
`<div>
<button disabled="true" />
<button />
</div>`,
)
expect(getByRole('button', {disabled: true})).toBeInTheDocument()
expect(getByRole('button', {disabled: false})).toBeInTheDocument()
})

test('`disabled: true|false` matches `aria-disabled` buttons', () => {
const {getByRole} = renderIntoDocument(
`<div>
<button aria-disabled="true" />
<button aria-disabled="false" />
</div>`,
)
expect(getByRole('button', {disabled: true})).toBeInTheDocument()
expect(getByRole('button', {disabled: false})).toBeInTheDocument()
})

test('`disabled` attributes overrides `aria-dsiabled`', () => {
const {getByRole} = renderIntoDocument(
`<div>
<button disabled="true" aria-disabled="false" />
<button />
</div>`,
)
expect(getByRole('button', {disabled: true})).toBeInTheDocument()
})

test('consider `disabled` attribute only if supported', () => {
const {getByRole, queryByRole} = renderIntoDocument(
`<div>
<button disabled="true" />
<div role="slider" disabled="true" />
</div>`,
)
expect(getByRole('button', {disabled: true})).toBeInTheDocument()
expect(queryByRole('slider', {disabled: true})).toBe(null)
})
15 changes: 15 additions & 0 deletions src/queries/role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
computeAriaChecked,
computeAriaPressed,
computeAriaCurrent,
computeAriaDisabled,
computeAriaExpanded,
computeHeadingLevel,
getImplicitAriaRoles,
Expand Down Expand Up @@ -45,6 +46,7 @@ const queryAllByRole: AllByRole = (
checked,
pressed,
current,
disabled,
level,
expanded,
} = {},
Expand Down Expand Up @@ -111,6 +113,16 @@ const queryAllByRole: AllByRole = (
}
}

if (disabled !== undefined) {
// guard against unknown roles
if (
allRoles.get(role as ARIARoleDefinitionKey)?.props['aria-disabled'] ===
undefined
) {
throw new Error(`"aria-disabled" is not supported on role "${role}".`)
}
}

const subtreeIsInaccessibleCache = new WeakMap<Element, Boolean>()
function cachedIsSubtreeInaccessible(element: Element) {
if (!subtreeIsInaccessibleCache.has(element)) {
Expand Down Expand Up @@ -161,6 +173,9 @@ const queryAllByRole: AllByRole = (
if (current !== undefined) {
return current === computeAriaCurrent(element)
}
if (disabled !== undefined) {
return disabled === computeAriaDisabled(element)
}
if (expanded !== undefined) {
return expanded === computeAriaExpanded(element)
}
Expand Down
24 changes: 24 additions & 0 deletions src/role-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,29 @@ function computeAriaCurrent(element) {
)
}

const elementsSupportingDisabledAttribute = new Set([
'button',
'fieldset',
'input',
'optgroup',
'option',
'select',
'textarea',
])

/**
* @param {Element} element -
* @returns {boolean} -
*/
function computeAriaDisabled(element) {
return elementsSupportingDisabledAttribute.has(element.localName) &&
element.hasAttribute('disabled')
? // https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings
true
: // https://www.w3.org/TR/wai-aria-1.1/#aria-disabled
element.getAttribute('aria-disabled') === 'true'
}

/**
* @param {Element} element -
* @returns {boolean | undefined} - false/true if (not)expanded, undefined if not expand-able
Expand Down Expand Up @@ -336,6 +359,7 @@ export {
computeAriaChecked,
computeAriaPressed,
computeAriaCurrent,
computeAriaDisabled,
computeAriaExpanded,
computeHeadingLevel,
}
5 changes: 5 additions & 0 deletions types/queries.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ export interface ByRoleOptions {
* 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
* disabled in the accessibility tree, i.e., `aria-disabled="true"` or `disabled="true"`.
*/
disabled?: boolean
/**
* If true only includes elements in the query set that are marked as
* expanded in the accessibility tree, i.e., `aria-expanded="true"`
Expand Down

0 comments on commit eeebfc2

Please sign in to comment.