Skip to content

fix(Select/List): improvedA11y combobox VPAT fixes (Hub #242, #391, #392)#1149

Merged
agupta-eightfold merged 3 commits into
mainfrom
agupta/comp-vpat-fixes
Jul 9, 2026
Merged

fix(Select/List): improvedA11y combobox VPAT fixes (Hub #242, #391, #392)#1149
agupta-eightfold merged 3 commits into
mainfrom
agupta/comp-vpat-fixes

Conversation

@agupta-eightfold

@agupta-eightfold agupta-eightfold commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes three WAI-ARIA APG combobox a11y defects confirmed live on QA (Allyant Hub #242, #391, #392), all rooted in the shared Select/Menu/List components in improvedA11y mode. A single library fix resolves them for every improvedA11y Select consumer (TemplateSelectModal, TimeSlotSelect, and any other usage).

1. Listbox missing accessible name (#391)

List destructured only aria-label and applied it to the <ul>/<ol>; aria-labelledby fell into ...rest and landed on the outer wrapper <div> instead of the role="listbox" element. Consumers passing aria-labelledby via menuProps (e.g. TimeSlotSelect) got a listbox with no accessible name. Now forwarded to both <ul> and <ol> alongside aria-label.

2. aria-selected doesn't follow keyboard focus (#392, #242)

aria-selected was set once from the committed value (opt.selected) and never moved as Arrow keys changed the active descendant, so focus and selection state diverged. Per the APG Combobox pattern ("selection follows focus in the listbox"), single-select listboxes now track the active descendant. Multi-select listboxes keep aria-selected on the committed choices (guarded with !multiple) — moving it with focus there would be incorrect.

3. VoiceOver never announces the highlighted option (#392, #242)

The visually-hidden live region reacted only to dropdownVisible/options/searchQuery/selectLocale, never to activeDescendantId, so VoiceOver announced only "expanded" and nothing as the user arrowed through options (NVDA compensates via native aria-activedescendant). The active option's text and position are now announced.

⚠️ Reviewer flag: this causes NVDA to hear the option name twice (once natively via activedescendant, once via this echo) — accepted tradeoff for VoiceOver support.

The "of" connector in the announcement ("Apple, 1 of 3") is localizable via a new optional optionPositionSeparatorText locale key (set on en_US/en_GB; other locales fall back to "of" in code pending translation).

Test plan

  • New unit tests for all three fixes (List: attr lands on list el not wrapper; Select: selection-follows-focus single-select, multi-select unchanged, live-region echo on arrow nav).
  • npx jest src/components/List src/components/Select src/components/Menu122 tests pass, snapshots clean.
  • Verified end-to-end chain: menuProps.aria-labelledby → Select restMenuProps → Menu ...rest → List → <ul role="listbox">.

Consumer impact

No app-side code changes needed. vscode consumers already pass correct attributes; they only need to bump @eightfold.ai/octuple after this releases.

List destructured only aria-label and applied it to the ul/ol, so
aria-labelledby fell into ...rest and landed on the outer wrapper div
instead of the role="listbox" element. Consumers passing aria-labelledby
via menuProps (e.g. Select in improvedA11y mode) got a listbox with no
accessible name. Destructure aria-labelledby and apply it alongside
aria-label on both ul and ol.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codesandbox-ci

codesandbox-ci Bot commented Jul 9, 2026

Copy link
Copy Markdown

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

…ncements

Two WAI-ARIA APG combobox defects in improvedA11y mode:

1. aria-selected was set once from the committed value (opt.selected) and
   never moved as arrow keys changed the active descendant, so focus and
   selection state diverged. For single-select listboxes, selection now
   follows focus (aria-selected tracks the active option). Multi-select
   listboxes keep aria-selected on the committed choices, as required.

2. The visually-hidden live region reacted only to dropdownVisible/options/
   searchQuery/selectLocale, never to activeDescendantId, so VoiceOver
   announced only "expanded" and never the highlighted option as the user
   arrowed through (NVDA compensates via native aria-activedescendant). The
   active option's text and position are now announced. Tradeoff: NVDA hears
   the option name twice (native + echo). The "of" connector is localizable
   via optionPositionSeparatorText (added to en_US/en_GB; other locales fall
   back to "of" pending translation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@agupta-eightfold agupta-eightfold force-pushed the agupta/comp-vpat-fixes branch from 49e0625 to 3b8c11c Compare July 9, 2026 13:41
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.99%. Comparing base (14cf524) to head (3cb8446).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1149      +/-   ##
==========================================
+ Coverage   84.97%   84.99%   +0.01%     
==========================================
  Files        1230     1230              
  Lines       21542    21553      +11     
  Branches     8185     8191       +6     
==========================================
+ Hits        18306    18318      +12     
+ Misses       3150     3149       -1     
  Partials       86       86              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/components/Select/Locale/en_GB.tsx
Adds the live-region position connector ("of" in "Apple, 1 of 3") to all
42 Select locales. Order-reversed / no-natural-word languages (Hindi,
Telugu, CJK, Turkish, Finnish, Hungarian) use the universal "/" separator
where a bare connector word would be grammatically wrong.

Addresses review feedback on #1149.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@agupta-eightfold agupta-eightfold merged commit d67e1f7 into main Jul 9, 2026
7 of 9 checks passed
@agupta-eightfold

Copy link
Copy Markdown
Collaborator Author

Tracked in ENG-202332https://eightfoldai.atlassian.net/browse/ENG-202332 (TA Everest - Sprint 40).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants