Skip to content

Commit

Permalink
Communicate checked status to screenreaders when OptionItems are used…
Browse files Browse the repository at this point in the history
… in ActionMenu (#2255)

## Summary:
When OptionItems are used within an ActionMenu, they should have role="menuitemcheckbox" and use aria-checked instead of aria-selected

Issue: WB-1659

## Test plan:
- Confirm that an ActionMenu with OptionItems communicates when an option item is selected (`?path=/story/packages-dropdown-actionmenu--with-option-items`)
  - rendered option items should have role="menuitemcheckbox" and aria-checked="true"
  - screenreaders communicate the checked status

## Screen Recording
### Before & After
In the before videos, the selected option item in the menu is not communicated to the user using a screen reader. In the after videos, it is communicated (safari reads out "Checkmark", Chrome reads out "checked")

#### Safari

https://github.com/Khan/wonder-blocks/assets/14334617/114ac914-0ece-4e65-a886-987e8ba2f163

https://github.com/Khan/wonder-blocks/assets/14334617/b54486bb-ec8a-4fc0-a38b-1093267782ff

#### Chrome

https://github.com/Khan/wonder-blocks/assets/14334617/24dc0246-34a4-4c1f-a5d8-26386bb55619

https://github.com/Khan/wonder-blocks/assets/14334617/bfb849c7-6dab-44a5-8a0e-5b442b167796

Author: beaesguerra

Reviewers: beaesguerra, jandrade

Required Reviewers:

Approved By: jandrade, jandrade

Checks: ✅ Chromatic - Get results on regular PRs (ubuntu-latest, 20.x), ✅ codecov/project, ✅ Test (ubuntu-latest, 20.x, 2/2), ✅ Test (ubuntu-latest, 20.x, 1/2), ✅ Lint (ubuntu-latest, 20.x), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Chromatic - Build on regular PRs / chromatic (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ⏭️  Chromatic - Skip on Release PR (changesets), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ gerald, ⏭️  dependabot

Pull Request URL: #2255
  • Loading branch information
beaesguerra authored Jun 24, 2024
1 parent 371749e commit f099cf8
Show file tree
Hide file tree
Showing 11 changed files with 451 additions and 17 deletions.
14 changes: 14 additions & 0 deletions .changeset/small-avocados-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@khanacademy/wonder-blocks-clickable": patch
"@khanacademy/wonder-blocks-dropdown": patch
"@khanacademy/wonder-blocks-cell": minor
---

Improves accessibility of the checked status on `OptionItem` components used
within the `ActionMenu` component. The checked status is communicated to
screenreaders by using a `menuitemcheckbox` role with the `aria-checked`
attribute (instead of `aria-selected`).
- `CellCore` (used by `CompactCell` and `DetailCell`) has a new optional
prop for `aria-checked`
- `ClickableRole` type now supports the `menuitemcheckbox` role
- `OptionItem`'s `role` prop now also supports the `menuitemcheckbox` role
27 changes: 17 additions & 10 deletions __docs__/wonder-blocks-cell/compact-cell.argtypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,9 @@ export default {
/**
* Accessibility
*/
ariaLabel: {
"aria-label": {
name: "aria-label",
control: {
type: "string",
},
control: {type: "text"},
description: "Used to announce the cell's content to screen readers.",
table: {
category: "Accessibility",
Expand All @@ -219,16 +217,24 @@ export default {
},
},
},
ariaSelected: {
"aria-selected": {
name: "aria-selected",
control: {
type: "string",
},
control: {type: "boolean"},
description: " Used to indicate the current element is selected",
table: {
category: "Accessibility",
type: {
summary: "string",
summary: "boolean",
},
},
},
"aria-checked": {
name: "aria-checked",
control: {type: "boolean"},
table: {
category: "Accessibility",
type: {
summary: "boolean",
},
},
},
Expand All @@ -243,14 +249,15 @@ export default {
"listbox",
"menu",
"menuitem",
"menuitemcheckbox",
"radio",
"tab",
],
table: {
category: "Accessibility",
type: {
summary: "ClickableRole",
detail: `"button" | "link" | "checkbox" | "radio" | "listbox" | "option" | "menuitem" | "menu" | "tab"`,
detail: `"button" | "link" | "checkbox" | "radio" | "listbox" | "option" | "menuitem" | "menuitemcheckbox" | "menu" | "tab"`,
},
},
},
Expand Down
3 changes: 2 additions & 1 deletion __docs__/wonder-blocks-clickable/clickable.argtypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,15 @@ export default {
"listbox",
"menu",
"menuitem",
"menuitemcheckbox",
"radio",
"tab",
],
table: {
category: "Accessibility",
type: {
summary: "ClickableRole",
detail: `"button" | "link" | "checkbox" | "radio" | "listbox" | "option" | "menuitem" | "menu" | "tab"`,
detail: `"button" | "link" | "checkbox" | "radio" | "listbox" | "option" | "menuitem" | "menuitemcheckbox" | "menu" | "tab"`,
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,60 @@ describe("CellCore", () => {
// eslint-disable-next-line testing-library/no-node-access
expect(elem.parentElement).toHaveStyle("align-self: flex-start");
});

describe("aria-checked", () => {
it("should add aria-checked if aria-checked is set and it is a link", () => {
// Arrange

// Act
const {container} = render(
<CellCore aria-checked={true} href="#">
<div>cell core content</div>
</CellCore>,
);

// Assert
// Verify that the root element has the aria-current attribute
// eslint-disable-next-line testing-library/no-node-access
expect(container.firstChild).toHaveAttribute(
"aria-checked",
"true",
);
});

it("should add aria-checked if aria-checked is set and it has an onClick handler", () => {
// Arrange

// Act
const {container} = render(
<CellCore aria-checked={true} onClick={jest.fn()}>
<div>cell core content</div>
</CellCore>,
);

// Assert
// Verify that the root element has the aria-current attribute
// eslint-disable-next-line testing-library/no-node-access
expect(container.firstChild).toHaveAttribute(
"aria-checked",
"true",
);
});

it("should not add aria-checked if it is not clickable", () => {
// Arrange

// Act
const {container} = render(
<CellCore aria-checked={true}>
<div>cell core content</div>
</CellCore>,
);

// Assert
// Verify that the root element has the aria-current attribute
// eslint-disable-next-line testing-library/no-node-access
expect(container.firstChild).not.toHaveAttribute("aria-checked");
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ const CellCore = (props: CellCoreProps): React.ReactElement => {
onClick,
"aria-label": ariaLabel,
"aria-selected": ariaSelected,
"aria-checked": ariaChecked,
target,
role,
rootStyle,
Expand All @@ -189,6 +190,7 @@ const CellCore = (props: CellCoreProps): React.ReactElement => {
hideDefaultFocusRing={true}
aria-label={ariaLabel ? ariaLabel : undefined}
aria-selected={ariaSelected ? ariaSelected : undefined}
aria-checked={ariaChecked}
role={role}
target={target}
style={[
Expand Down
4 changes: 4 additions & 0 deletions packages/wonder-blocks-cell/src/util/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ export type CellProps = {
* Used to indicate the current element is selected.
*/
"aria-selected"?: AriaProps["aria-selected"];
/**
* Used to indicate the current item is checked.
*/
"aria-checked"?: AriaProps["aria-checked"];
/**
* Optinal href which Cell should direct to, uses client-side routing
* by default if react-router is present.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type ClickableRole =
| "listbox"
| "menu"
| "menuitem"
| "menuitemcheckbox"
| "option"
| "radio"
| "switch"
Expand Down
Loading

0 comments on commit f099cf8

Please sign in to comment.