fix: the selected option is announced without selected/not selected state#80467
fix: the selected option is announced without selected/not selected state#80467daledah wants to merge 5 commits intoExpensify:mainfrom
Conversation
|
@linhvovan29546 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
@neil-marcellini Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
@daledah We need to |
|
@linhvovan29546 Can you share with me how you used VoiceOver and TalkBack to test in this PR? |
Android: If you don’t know how to enable TalkBack, please follow these steps:
Screen.Recording.2026-01-28.at.15.42.36.moviOS: For the simulator, use Accessibility Inspector. Screen.Recording.2026-01-28.at.15.47.56.movWeb: You can use this tool to testing |
There was a problem hiding this comment.
(Neil's AI Agent)
LGTM! This accessibility fix properly announces the selected state for list items:
- Uses the semantic
optionrole instead ofbuttonrole for list items - Adds
accessibilityState={{selected: item.isSelected}}andaria-selectedto the PressableWithFeedback, so screen readers announce "selected" or "not selected" appropriately - Removes the duplicate
accessibilityStatefrom the inner View
The change is minimal and focused on improving screen reader experience.
Neil
I have manually reviewed this PR as well.
|
thanks @linhvovan29546. l added the steps and the screenshots for the ios and ios mweb. But i don't have Screen.Recording.2026-02-03.at.01.00.35.mov |
| // eslint-disable-next-line react/jsx-props-no-spreading | ||
| {...bind} | ||
| sentryLabel={CONST.SENTRY_LABEL.BASE_LIST_ITEM.OPTION} | ||
| accessibilityState={{selected: item.isSelected}} |
There was a problem hiding this comment.
@daledah Could you please apply a similar change to src/components/SelectionListWithSections/BaseListItem.tsx? They are the same.
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-02-04.at.19.52.21.movAndroid: mWeb ChromeScreen.Recording.2026-02-04.at.19.36.41.moviOS: HybridAppScreen.Recording.2026-02-04.at.19.23.22.moviOS: mWeb SafariScreen.Recording.2026-02-04.at.19.33.18.mov |
linhvovan29546
left a comment
There was a problem hiding this comment.
Tests
For iOS simulator I used Xcode > Open Developer Tool > Accessibility Inspector
For Android simulator > go to Settings -> Accessibility -> Toggle the "Use TalkBack" switch to On
Please add the suggested tests for web as well, using Chrome + JAWS.
|
@linhvovan29546 looks like the bug is fixed in this PR. Should we put our PR on hold or close it? |
|
This issue focuses on the web. The PR above only handles the native side and is missing an implementation for web. We still need this PR to fix the issue on web, but the fix should be based on that PR |
|
After rechecked again, I think we can hold this PR and then request retest, since it looks like the issue is resolved by the PR above |
Code ReviewThis PR adds 1. Missing
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 55e1f69d8b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| disabled={isDisabled && !item.isSelected} | ||
| interactive={item.isInteractive} | ||
| accessibilityLabel={item.accessibilityLabel ?? [item.text, item.text !== item.alternateText ? item.alternateText : undefined].filter(Boolean).join(', ')} | ||
| role={CONST.ROLE.OPTION} |
There was a problem hiding this comment.
Respect caller role when rendering selection list rows
Do not hardcode role to option here; BaseListItem still computes state from accessibilityRole, and callers like MultiSelectListItem explicitly pass CONST.ROLE.CHECKBOX so screen readers announce checked/unchecked semantics. With this change every row is exposed as an option, so checkbox/radio roles are lost and assistive tech can stop announcing the intended control state for multi-select flows.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
I agree that we need to fix this.
| disabled={isDisabled && !item.isSelected} | ||
| interactive={item.isInteractive} | ||
| accessibilityLabel={item.accessibilityLabel ?? [item.text, item.text !== item.alternateText ? item.alternateText : undefined].filter(Boolean).join(', ')} | ||
| role={CONST.ROLE.OPTION} |
There was a problem hiding this comment.
I agree that we need to fix this.
linhvovan29546
left a comment
There was a problem hiding this comment.
@daledah Please also see my comment: #80467 (comment) as well
|
We have the new PR here |

Explanation of Change
Fixed Issues
$ #74845
PROPOSAL: #74845 (comment)
Tests
Offline tests
Same as tests
QA Steps
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari