fix: Screen Reader: Workspace: 3-dot More button in Workspace container not focusable or operable#83324
Conversation
|
@ikevin127 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] |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 62f53daf22
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0fe3bcd87b
ℹ️ 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".
Reviewer Checklist
Screenshots/VideosScreen.Recording.2026-02-25.at.19.37.43.mov |
| <PressableWithoutFeedback | ||
| onPress={onPress} | ||
| disabled={disabled} | ||
| accessible={false} | ||
| sentryLabel={CONST.SENTRY_LABEL.WORKSPACE.LIST.ROW_ARROW} | ||
| style={[styles.justifyContentCenter, styles.alignItemsCenter, styles.touchableButtonImage]} | ||
| > |
There was a problem hiding this comment.
🟡 High-Severity Issue - Arrow PressableWithoutFeedback with accessible={false} — no screen reader label
<PressableWithoutFeedback
onPress={onPress}
disabled={disabled}
accessible={false} // ← screen readers will completely ignore this element
sentryLabel={CONST.SENTRY_LABEL.WORKSPACE.LIST.ROW_ARROW}
style={[styles.justifyContentCenter, styles.alignItemsCenter, styles.touchableButtonImage]}
>Setting accessible={false} means VoiceOver and TalkBack will skip this arrow entirely. While the main row PressableWithoutFeedback has accessible={true} and handles the tap action, the arrow alone is visible as a separate interactive element for sighted mouse users.
This creates an inconsistency — sighted users can click the arrow, but screen reader users can only activate via the row. This is acceptable only if intentional, but per the ACCESSIBILITY.md guide: "ensure that the pressable component has a label".
Bug if not addressed: Minor — keyboard-only users on desktop can Tab to the arrow but get no screen reader announcement. If this is intentional (decorative duplicate), consider adding tabIndex={-1} on web to prevent keyboard focus.
Suggested fix: Either make it accessible with a label, or explicitly remove it from tab order:
// Option A: Make it accessible
<PressableWithoutFeedback
accessible
accessibilityLabel={translate('common.goTo')}
role={CONST.ROLE.BUTTON}
...
>
// Option B: Keep it hidden but also remove from tab order (web)
<PressableWithoutFeedback
accessible={false}
tabIndex={-1}
...
>There was a problem hiding this comment.
Setting accessible={false} means VoiceOver and TalkBack will skip this arrow entirely.
The fix is accessible={false} on iOS setting accessible={true} treats the container as a single element and skips the ThreeDot. So this is intentional. Adding a third VoiceOver stop (row + "More" + arrow) would be redundant
|
🟢 Reviewer checklist completed - there's a few comments that need addressed before I can approve. @TaduJR Please let me know once the code review comments are addressed so I can do a final pass on this 🙌 |
|
|
…er not focusable or operable
f12f512 to
c9d00e9
Compare
Thanks so much @ikevin127 I am sorry unfortunately, made a mistake during rebase, and was forced to force-push. It's ready for re-review |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 @yuwenmemon has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/yuwenmemon in version: 9.3.28-0 🚀
|
|
Deploy Blocker #83965 was identified to be related to this PR. |
|
Hi @TaduJR. QA team failed this PR on iOS with an original issue 1772530868733.83324I.mp4 |
|
I think it's because of the revert. |
|
🚀 Deployed to staging by https://github.com/yuwenmemon in version: 9.3.30-0 🚀
|
|
🚀 Deployed to production by https://github.com/blimpich in version: 9.3.30-3 🚀
|
Explanation of Change
The parent
PressableWithoutFeedbackinWorkspacesListPage.tsxdefaults toaccessible={true}, which groups all children into a single VoiceOver element — making the ThreeDotsMenu invisible to screen readers.Fix:
Set
accessible={false}on the parent and moved accessibility intoWorkspacesListRow.tsx— the content area gets its ownPressableWithoutFeedbackwith a descriptive label, while ThreeDotsMenu sits outside it so VoiceOver can focus both independently. Wide layoutThreeDotMenuOrPendingIconis left untouched to preserve column alignment.Fixed Issues
$ #77471
$ #79231
PROPOSAL: #79231 (comment)
Tests
VoiceOver (iOS) — #77471 fix:
Column alignment (wide layout) — #82727 regression check:
7. Open the app on web (desktop/wide viewport)
8. Go to the Workspaces list with 2+ workspaces
9. Verify Owner and Workspace Type columns are properly aligned across rows
Arrow click (wide layout) — #82739 regression check:
10. On web (desktop/wide viewport), click the arrow icon on any workspace row
11. Verify it navigates to the workspace page
Offline tests
Same as tests
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
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
Screen_Recording_20260224_172300_New.Expensify.Dev.mp4
Android: mWeb Chrome
Screen_Recording_20260224_172721_Chrome.mp4
iOS: Native
Screen.Recording.2026-02-24.at.4.57.28.in.the.afternoon.mov
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-02-24.at.4.40.07.in.the.afternoon.mov