Skip to content

fix(multi-select): Correct selected pill accessibility - #4192

Open
minwookshin wants to merge 2 commits into
Workday:supportfrom
minwookshin:fix/multiselect-selected-accessibility
Open

minwookshin wants to merge 2 commits into
Workday:supportfrom
minwookshin:fix/multiselect-selected-accessibility

Conversation

@minwookshin

@minwookshin minwookshin commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #3919.
Fixes #4212.

Based on support (v15), as requested.

Expose selected pills as a named toolbar of native removal buttons, and hide the redundant, untabbable clear button from assistive technology.

The removal flow also keeps the focus cursor in sync, returns focus to the search input after the last removal, and avoids submitting the surrounding form. Search text remains independent of the submitted selection value.

Release Category

Components

Checklist

  • No MDX documentation changes
  • Label ready for review has been added to PR

For the Reviewer

  • PR title and summary describe the change and link the issue
  • No breaking changes or additional release notes

Where Should the Reviewer Start?

MultiSelectedList.tsx, MultiSelectedItem.tsx, and useMultiSelectItemRemove.ts. Both input variants pass their accessible name to the selected-item toolbar. Horizontal roving focus provides one tab stop, Left/Right movement with wrapping, and Home/End navigation. Focus remains on the native removal buttons. The search input needs a separate focus ref because its text is not the constrained form value.

Testing Manually

In the Searching story, focus the input, press ArrowDown to open the popup, then type apple, select two results, and press Escape. Tab to a removal button, move with Left/Right or Home/End, then remove items with Backspace/Delete, Enter, or Space. Removing the last item returns focus to the input; clicking remove does not submit the form. Clearing search retains input focus.

Verification on support (October 27): all 9 Chrome Cypress cases pass, covering axe, the single tab stop, wrapping Left/Right and Home/End navigation, Enter/Space activation, deletion/focus return, search clearing, and form submission behavior. All 112 focused unit tests, all typecheck targets, full lint, and changed-file ESLint/formatting checks pass. The support tests use the existing ArrowDown popup-opening behavior.

JAWS/NVDA behavior on Windows and VoiceOver speech output have not been verified. Please confirm automatic arrow-key passthrough and announcements with the toolbar before merging. Earlier cross-browser checks of the removal flow predate this role change.

Summary by CodeRabbit

  • Accessibility
    • Improved accessible labeling and screen-reader semantics for multi-select inputs and selected-item groups.
    • Updated clear-button accessibility and ensured search clearing keeps focus on the input.
  • Bug Fixes
    • Improved keyboard navigation and focus behavior when removing selected items.
    • Prevented pill-removal actions from submitting forms.
  • Tests
    • Added checks for multi-select accessibility, removal controls, keyboard interactions, focus behavior, and search clearing.

@minwookshin
minwookshin requested a review from a team as a code owner October 3, 2026 09:41
@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 617b8ba and 2b05665.

Files selected for processing: 5
  • cypress/component/MultiSelect.spec.tsx
  • modules/preview-react/multi-select/lib/MultiSelectedItem.tsx
  • modules/preview-react/multi-select/lib/MultiSelectedList.tsx
  • modules/preview-react/multi-select/lib/useMultiSelectModel.ts
  • modules/preview-react/multi-select/spec/MultiSelectInput.spec.tsx

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

Walkthrough

The MultiSelect changes update accessible names and selected-pill semantics, pill-removal focus behavior, and the search clear button’s accessibility attributes. Tests cover accessibility attributes, keyboard interactions, and focus behavior.

Changes: MultiSelect accessibility and interaction

Layer / File(s) Summary
Accessible names and selected-pill semantics
modules/preview-react/multi-select/lib/MultiSelectInput.tsx, modules/preview-react/multi-select/lib/MultiSelectedList.tsx, modules/preview-react/multi-select/lib/MultiSelectedItem.tsx, modules/preview-react/multi-select/spec/MultiSelectInput.spec.tsx, cypress/component/MultiSelect.spec.tsx
Both input variants pass accessible names to their inputs and selected-pill groups. The selected-pill container uses grid and row semantics, and pills use gridcell semantics without aria-selected. Tests check accessible names, attributes, and accessibility scans.
Pill removal and focus behavior
modules/preview-react/multi-select/lib/useMultiSelectModel.ts, modules/preview-react/multi-select/lib/MultiSelectInput.tsx, modules/preview-react/multi-select/lib/useMultiSelectItemRemove.ts, cypress/component/MultiSelect.spec.tsx
The model exposes a search-input ref and derives the selected-pill items for the list model. Pill focus updates the selected cursor. Keyboard and click removal focus the search input when no next item remains. Cypress tests cover keyboard removal and form submission behavior.
Search clear-button accessibility
modules/react/text-input/lib/InputGroup.tsx, cypress/component/MultiSelect.spec.tsx
The clear button uses aria-hidden instead of role="presentation". Cypress tests check its accessibility and tab-order behavior, and verify that clearing search leaves focus on the combobox.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related issues

  • Workday/canvas-kit#4212: The changed selected-pill grid and gridcell structure relates to the issue’s VoiceOver position-count reports.

Possibly related PRs

  • Workday/canvas-kit#4215: This PR documents MultiSelect and Pill accessibility behavior, which overlaps with the selected-pill semantics and keyboard focus changes here.

Suggested reviewers: mannycarrera4, williamjstanton

Merge Risk: Minimal · previous review 2b056

The inspected accessibility and removal flows show no actionable issue; the change is ready to merge after normal checks.

Security architecture risk: Minimal · up to fd5b1

The reviewed changes adjust accessibility and focus behavior without expanding privileges or data access. Submitted selections remain separate from search text, and removal buttons explicitly avoid submitting the surrounding form.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The reviewed exposure is browser-side accessibility, collection interaction, and form behavior. Shared clear-button consumers inherit the accessibility change, but the inspected modifications do not add a privileged sink or expand authority beyond existing component interactions.

Trust Boundaries and Controls

  • observed — Caller-supplied accessible names are forwarded as React attributes rather than selection values. The default selected-item container is a span, and its inner removal control now explicitly uses type="button", preventing implicit surrounding-form submission through that control.
Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: correcting accessibility behavior for selected pills in the multi-select component.
Linked Issues check Passed Issue [#3919] requires no multiselect-related axe issues before or after selection. The PR hides the redundant clear button from assistive technology, replaces the selected-pill listbox with a named g…
Out of Scope Changes check Passed The focus cursor updates, search-input ref, focus return, default-action prevention, and form-submission tests support the selected-pill removal flow. The InputGroup clear-button change directly addre…
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Full details: Linked Issues check

Explanation

Issue [#3919] requires no multiselect-related axe issues before or after selection. The PR hides the redundant clear button from assistive technology, replaces the selected-pill listbox with a named grid containing a row and gridcells, and removes the unsupported aria-selected state from removal buttons. The removal buttons use type="button". Cypress and unit tests cover axe checks, accessible names, roles, keyboard removal, focus behavior, and form submission.

Full details: Out of Scope Changes check

Explanation

The focus cursor updates, search-input ref, focus return, default-action prevention, and form-submission tests support the selected-pill removal flow. The InputGroup clear-button change directly addresses the clear-button findings in [#3919]. No unrelated changes are identified.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@minwookshin minwookshin changed the title fix(multi-select): correct selected pill accessibility fix(multi-select): Correct selected pill accessibility Oct 3, 2026
@cypress

cypress Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Workday/canvas-kit    Run #11788

Run Properties:  status check passed Passed #11788  •  git commit 1bc1d650e0 ℹ️: Merge b9cecf844b27a574a0316a8cba0a75ba0883c9af into 1c3b39366d3cd0c22ca598b71f4c...
Project Workday/canvas-kit
Branch Review fix/multiselect-selected-accessibility
Run status status check passed Passed #11788
Run duration 02m 37s
Commit git commit 1bc1d650e0 ℹ️: Merge b9cecf844b27a574a0316a8cba0a75ba0883c9af into 1c3b39366d3cd0c22ca598b71f4c...
Committer Minwook Shin
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 17
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 873
View all changes introduced in this branch ↗︎
UI Coverage  14.57%
  Untested elements 2501  
  Tested elements 424  
Accessibility  99.48%
  Failed rules  3 critical   5 serious   3 moderate   1 minor
  Failed elements 69  

@williamjstanton

Copy link
Copy Markdown
Collaborator

Nice work on grouping and labeling the pills.

Since this set of selected pills depends on using the arrow keys to move across them, I think we need to investigate an alternative to group role. For PC-based screen readers, (JAWS, NVDA) arrow key events won't pass through to the web app with just group and button roles. So our roving tabindex won't work terrible well on the Windows side.

@minwookshin

minwookshin commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor Author

Changed the pills to a single-row layout grid, with gridcells around the native removal buttons. The roving tab stop, arrows, Home/End, and deletion focus behavior are covered by tests; all 9 Chrome Cypress cases and 113 unit tests pass. I couldn't verify JAWS/NVDA on this Mac. Could you check arrow-key passthrough on Windows with the updated preview?

The current CI run also needs maintainer approval. Could a maintainer approve it and add ready for review? I don't have permission to add the label.

@williamjstanton

williamjstanton commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Changed the pills to a single-row layout grid, with gridcells around the native removal buttons. The roving tab stop, arrows, Home/End, and deletion focus behavior are covered by tests; all 9 Chrome Cypress cases and 113 unit tests pass. I couldn't verify JAWS/NVDA on this Mac. Could you check arrow-key passthrough on Windows with the updated preview?

The current CI run also needs maintainer approval. Could a maintainer approve it and add ready for review? I don't have permission to add the label.

I'm glad that you experimented with this, because it was one of the options I was considering. Functionally, this is going to work but, and this is opinionated, I find it really annoying that screen readers will describe the selected pills as a "table with 1 row and {X} columns" every time. That's not valuable information for this selected pills pattern.

I found a way to make the ARIA listbox pattern from before work properly, but a listbox is intended to map back to HTML <select> (Source: https://www.w3.org/TR/wai-aria/#listbox) and user-removable options is kind of an anti-pattern for a Select. On the other hand, the spec also says it maps back to a list which is more abstract, and could certainly include some remove controls.

<Pill as={Element} disabled={disabled} variant="removable">
  <Pill.Label aria-hidden={true}>{children}</Pill.Label>
  <Pill.IconButton role="option" aria-label={removeLabel} ref={ref} {...(elemProps as any)} />
</Pill>

In conclusion, I think changing the ListBox component role to toolbar might be suitable for this pattern. It's simple, and will also allow arrow key pass-through.

@minwookshin

Copy link
Copy Markdown
Contributor Author

Switched the selected pills to a named toolbar with native removal buttons and horizontal roving focus in 6f7a880. All 10 Chrome Cypress cases and 113 unit tests pass, including wrapping arrows, Home/End, Enter/Space, and deletion focus. Could you check the JAWS/NVDA behavior with the updated preview? The new CI run also needs maintainer approval and the PR still needs ready for review.

@williamjstanton

Copy link
Copy Markdown
Collaborator

Switched the selected pills to a named toolbar with native removal buttons and horizontal roving focus in 6f7a880. All 10 Chrome Cypress cases and 113 unit tests pass, including wrapping arrows, Home/End, Enter/Space, and deletion focus. Could you check the JAWS/NVDA behavior with the updated preview? The new CI run also needs maintainer approval and the PR still needs ready for review.

Oh thank you, I think I'm really happy with this toolbar idea. I spoke to @mannycarrera4 earlier today, and bug fixes like this need to be merged onto the support branch. Would you mind branching off support and cherry-picking the toolbar commits?

Thanks for working on this and I apologize for any inconvenience!

@minwookshin
minwookshin force-pushed the fix/multiselect-selected-accessibility branch from 6f7a880 to 77bdbed Compare October 27, 2026 14:49
@minwookshin
minwookshin changed the base branch from master to support October 27, 2026 14:50
@minwookshin

minwookshin commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Moved the fix onto support and retargeted this PR, thanks. The toolbar and removal behavior pass all 9 Chrome Cypress cases and 112 focused unit tests; lint and all typecheck targets pass too. The new CI runs are awaiting approval; could a maintainer approve them?

@williamjstanton williamjstanton added the ready for review Code is ready for review label Oct 28, 2026

@williamjstanton williamjstanton left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it looks good from a11y.

@williamjstanton

Copy link
Copy Markdown
Collaborator

Requesting an additional code review.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

accessibility ready for review Code is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MultiSelect selected pills announce the wrong position count in VoiceOver bug: a11y axe issues on mutliselect searching example

2 participants