feat(a11y): adjust the name and role of the multi-select#6499
feat(a11y): adjust the name and role of the multi-select#6499diegolmello merged 120 commits intodevelopfrom
Conversation
|
Android Build Available Rocket.Chat Experimental 4.63.0.87803 |
|
iOS Build Available Rocket.Chat Experimental 4.63.0.87803 |
|
Android Build Available Rocket.Chat Experimental 4.64.0.93303 |
|
iOS Build Available Rocket.Chat Experimental 4.64.0.100081 |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/stacks/types.ts (1)
98-105: Critical: Type inconsistency fornextActionacross parameter lists.The
nextActionfield has incompatible types in different stack parameter lists:
ChatsStackParamList(line 104):nextAction?(): void;NewMessageStackParamList(line 249):nextAction?: Function;ModalStackParamList(per relevant snippets):nextAction?: Function;This inconsistency causes type conflicts when the navigation types are merged (as in
app/views/SelectedUsersView/index.tsxlines 30-34). TypeScript may silently widen or narrow the type, leading to runtime errors if callers pass incompatible values.Standardize the type across all three parameter lists. The specific function signature
() => voidis preferable for type safety:export type NewMessageStackParamList = { NewMessageView: undefined; SelectedUsersView: { maxUsers?: number; showButton?: boolean; title?: string; buttonText?: string; - nextAction?: Function; + nextAction?(): void; showSkipText?: boolean; }; // TODO: ChangeApply the same fix to
ModalStackParamListinapp/stacks/MasterDetailStack/types.ts.Also applies to: 244-251
♻️ Duplicate comments (3)
app/views/AccessibilityAndAppearanceView/index.tsx (2)
123-133: Critical: Remove duplicate ListPicker section.Lines 123-133 contain an exact duplicate of the ListPicker section at lines 112-122. Remove this duplicate section.
Apply this diff to remove the duplicate:
- <List.Section> - <List.Separator /> - <ListPicker - onChangeValue={value => { - setAlertDisplayType(value); - }} - title={I18n.t('A11y_appearance_show_alerts_as')} - value={alertDisplayType ?? 'TOAST'} - /> - <List.Separator /> - </List.Section>
92-92: Critical: Remove platform-specific accessibilityRole and use 'switch' on all platforms.The conditional
accessibilityRole={isIOS ? 'switch' : 'none'}breaks TalkBack support on Android by removing all semantic information. React Native Switch components requireaccessibilityRole='switch'on both iOS and Android for proper assistive technology support.Apply this diff to fix the accessibility role on all three switches:
- accessibilityRole={isIOS ? 'switch' : 'none'} + accessibilityRole='switch'Also applies to: 100-100, 108-108
app/views/SelectedUsersView/index.tsx (1)
73-74: Address incomplete dependency array and verify empty-string fallback behavior.Two issues need attention:
Operator choice (lines 73-74): The
||operator treats empty string as falsy. If a caller explicitly passesbuttonText=""to hide the button label, it will now fall back to'Next'instead. If this is intentional across all call sites, document it; otherwise use??(nullish coalescing) to preserve empty strings.Incomplete dependencies (line 87): The
useLayoutEffectreferencestitle(line 72),showSkipText(line 68 viahandleButtonTitle), andshowButton(line 79), but these are missing from the dependency array. If any of these props change at runtime, the header won't update correctly.Based on past review comments, this issue was previously flagged but remains unaddressed.
Apply this diff to fix the dependency array:
- }, [navigation, users.length, maxUsers, buttonText, nextAction]); + }, [navigation, users.length, maxUsers, buttonText, nextAction, title, showSkipText, showButton]);For the operator choice, verify with call sites whether empty string is a valid intentional value. If yes, keep
||; if no, use??:- const buttonTextHeader = buttonText || I18n.t('Next'); - const nextActionHeader = nextAction || (() => {}); + const buttonTextHeader = buttonText ?? I18n.t('Next'); + const nextActionHeader = nextAction ?? (() => {});Also applies to: 87-87
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
app/stacks/MasterDetailStack/index.tsx(0 hunks)app/stacks/MasterDetailStack/types.ts(1 hunks)app/stacks/types.ts(1 hunks)app/views/AccessibilityAndAppearanceView/index.tsx(2 hunks)app/views/SelectedUsersView/index.tsx(4 hunks)
💤 Files with no reviewable changes (1)
- app/stacks/MasterDetailStack/index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- app/stacks/MasterDetailStack/types.ts
🧰 Additional context used
🧬 Code graph analysis (1)
app/views/SelectedUsersView/index.tsx (2)
app/stacks/types.ts (2)
ChatsStackParamList(22-188)NewMessageStackParamList(242-264)app/stacks/MasterDetailStack/types.ts (1)
ModalStackParamList(30-204)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: ESLint and Test / run-eslint-and-test
- GitHub Check: format
🔇 Additional comments (1)
app/views/SelectedUsersView/index.tsx (1)
40-40: Good default value forshowSkipText.Defaulting
showSkipTexttotruemaintains backward compatibility for existing callers while allowing new callers to opt out by explicitly passingfalse.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
.maestro/tests/room/discussion.yaml (1)
81-85: Consider adding a wait for the action sheet to dismiss after channel selection.After tapping
multi-select-item-${output.room.name}(line 82), the action sheet animates closed. The immediate tap onmulti-select-discussion-name(line 84) could be flaky if the action sheet overlay is still present. If you observe intermittent failures here, consider adding a wait:- tapOn: id: 'multi-select-item-${output.room.name}' +- extendedWaitUntil: + notVisible: + id: 'action-sheet' + timeout: 5000 - tapOn: id: 'multi-select-discussion-name'
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
.maestro/tests/room/discussion.yaml(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: ESLint and Test / run-eslint-and-test
- GitHub Check: format
🔇 Additional comments (1)
.maestro/tests/room/discussion.yaml (1)
42-69: LGTM! The SelectedUsersView flow is well-structured.The test correctly validates the new user selection sequence: search → select → verify chip → submit → navigate to discussion view. Each step has appropriate waits and assertions.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/views/AccessibilityAndAppearanceView/index.tsx (1)
86-108: AddaccessibilityStateto communicate switch state to screen readers.The three switch items now have
accessibilityRole='switch', but they're missingaccessibilityState={{ checked: <value> }}. Without this, VoiceOver and TalkBack won't announce whether each switch is on or off, breaking a core accessibility requirement outlined in MA-118.Apply this diff to add the missing state:
<List.Item testID='accessibility-autoplay-gifs-switch' title='Autoplay_gifs' right={renderAutoplayGifs} onPress={toggleAutoplayGifs} accessibilityRole='switch' + accessibilityState={{ checked: autoplayGifs }} /> <List.Separator /> <List.Item testID='accessibility-mentions-with-at-symbol-switch' title='Mentions_With_@_Symbol' right={renderMentionsWithAtSymbolSwitch} onPress={toggleMentionsWithAtSymbol} accessibilityRole='switch' + accessibilityState={{ checked: mentionsWithAtSymbol }} /> <List.Separator /> <List.Item testID='accessibility-rooms-with-hashtag-symbol-switch' title='Rooms_With_#_Symbol' right={renderRoomsWithHashTagSwitch} onPress={toggleRoomsWithHashTag} accessibilityRole='switch' + accessibilityState={{ checked: roomsWithHashTagSymbol }} />
♻️ Duplicate comments (1)
app/views/AccessibilityAndAppearanceView/index.tsx (1)
122-132: Remove duplicateListPickersection.Lines 122–132 are an exact duplicate of the
ListPickersection on lines 111–121 (only trivial difference:??vs||in the value prop). This duplication will render the picker twice in the UI and was already flagged in a previous review.Remove the duplicate section entirely:
</List.Section> - <List.Section> - <List.Separator /> - <ListPicker - onChangeValue={value => { - setAlertDisplayType(value); - }} - title={I18n.t('A11y_appearance_show_alerts_as')} - value={alertDisplayType ?? 'TOAST'} - /> - <List.Separator /> - </List.Section> </List.Container>
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
app/views/AccessibilityAndAppearanceView/index.tsx(1 hunks)app/views/CreateDiscussionView/SelectUsers.tsx(0 hunks)app/views/CreateDiscussionView/interfaces.ts(0 hunks)app/views/DisplayPrefsView.tsx(9 hunks)
💤 Files with no reviewable changes (2)
- app/views/CreateDiscussionView/interfaces.ts
- app/views/CreateDiscussionView/SelectUsers.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:14.049Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
📚 Learning: 2025-12-17T15:56:14.049Z
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:14.049Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
Applied to files:
app/views/DisplayPrefsView.tsxapp/views/AccessibilityAndAppearanceView/index.tsx
🧬 Code graph analysis (1)
app/views/AccessibilityAndAppearanceView/index.tsx (1)
__mocks__/react-native-mmkv.js (4)
value(102-102)value(126-126)value(150-150)value(174-177)
🔇 Additional comments (3)
app/views/DisplayPrefsView.tsx (3)
86-86: LGTM! Ensures single focus stop for switch control.Setting
accessible={false}on the Switch component correctly hides it from the accessibility tree, allowing the parent List.Item to handle all accessibility interactions as a unified control. This aligns with the MA-118 objective to group labels and controls into a single focus stop.Based on learnings, this pattern prevents duplicate focus stops and ensures screen readers announce the switch state through the List.Item's accessibility props.
102-102: LGTM! Typo fix across all accessibility labels.The spelling correction from
additionalAcessibilityLabeltoadditionalAccessibilityLabelfixes the prop name across all List.Item components (Display, Sort_by, and Group_by sections).Also applies to: 112-112, 121-121, 135-135, 145-145, 159-159, 169-169, 179-179
122-122: LGTM! Completes accessibility role declaration for switch control.Adding
accessibilityRole='switch'to the Avatars List.Item correctly identifies the control type for screen readers, consistent with the radio and checkbox roles declared on other preference items in this view.
diegolmello
left a comment
There was a problem hiding this comment.
Minor detail and just to make sure, have you run e2e tests?
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/containers/List/ListItem.tsx (1)
166-167: Critical: Fix dependency array to use correct variable names.The dependency array still references the old misspelled property names (
additionalAcessibilityLabel,additionalAcessibilityLabelCheck) instead of the corrected names (additionalAccessibilityLabel,additionalAccessibilityLabelCheck). This causes a compilation error and breaks memoization.🔎 Proposed fix
}, [ accessibilityLabel, title, subtitle, translateTitle, translateSubtitle, - additionalAcessibilityLabel, - additionalAcessibilityLabelCheck + additionalAccessibilityLabel, + additionalAccessibilityLabelCheck ]);
🧹 Nitpick comments (1)
app/containers/List/ListItem.tsx (1)
138-138: Consider fixing the function name typo for consistency.The function is named
handleAcessibilityLabel(with the old typo) while all the properties it uses have been corrected toAccessibility. Renaming tohandleAccessibilityLabelwould improve consistency.🔎 Proposed fix
- const handleAcessibilityLabel = useMemo(() => { + const handleAccessibilityLabel = useMemo(() => { let label = ''; if (accessibilityLabel) { return accessibilityLabel;And update the usage on line 175:
- accessibilityLabel={handleAcessibilityLabel} + accessibilityLabel={handleAccessibilityLabel}
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
app/containers/Chip/__snapshots__/Chip.test.tsx.snapis excluded by!**/*.snap
📒 Files selected for processing (3)
app/containers/ActionSheet/Provider.tsxapp/containers/Chip/index.tsxapp/containers/List/ListItem.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- app/containers/Chip/index.tsx
- app/containers/ActionSheet/Provider.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
📚 Learning: 2025-12-17T15:56:22.578Z
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
Applied to files:
app/containers/List/ListItem.tsx
🪛 GitHub Actions: Format Code with Prettier
app/containers/List/ListItem.tsx
[error] 138-161: Compilation issues: Existing manual memoization could not be preserved. useMemo/dependency handling indicated by the tool. Potentially incorrect dependency array for handleAcessibilityLabel.
[error] 166-166: 'additionalAcessibilityLabel' is not defined. Possibly a typo vs 'additionalAccessibilityLabel'.
🔇 Additional comments (2)
app/containers/List/ListItem.tsx (2)
105-105: LGTM! Typo fix improves consistency.The correction from
additionalAcessibilityLabel→additionalAccessibilityLabelandadditionalAcessibilityLabelCheck→additionalAccessibilityLabelCheckproperly fixes the spelling throughout the property declarations and usage.Also applies to: 107-107, 127-128, 149-157
248-248: LGTM! Export enables composition.Exporting
IListItemallows other components (like the newListRadio) to extend this interface, which supports the accessibility improvements and component reusability goals of this PR.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
app/containers/List/ListItem.tsx (1)
138-138: Consider renaming the internal function for consistency.While the prop names were corrected to
additionalAccessibilityLabel, the internal functionhandleAcessibilityLabelstill uses the old spelling. For consistency, consider renaming it tohandleAccessibilityLabel.🔎 Proposed rename
Line 138:
- const handleAcessibilityLabel = useMemo(() => { + const handleAccessibilityLabel = useMemo(() => {Line 175:
- accessibilityLabel={handleAcessibilityLabel} + accessibilityLabel={handleAccessibilityLabel}Also applies to: 175-175
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
app/containers/List/ListItem.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
📚 Learning: 2025-12-17T15:56:22.578Z
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
Applied to files:
app/containers/List/ListItem.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: E2E Build Android / android-build
- GitHub Check: E2E Build iOS / ios-build
- GitHub Check: ESLint and Test / run-eslint-and-test
- GitHub Check: format
🔇 Additional comments (3)
app/containers/List/ListItem.tsx (3)
105-107: Typo fixes improve consistency.The spelling corrections (
Acessibility→Accessibility) properly align the property names with standard naming conventions.
149-157: State-in-label pattern correctly handles iOS VoiceOver limitations.The logic appropriately includes "Checked"/"Unchecked" or "Enabled"/"Disabled" in the accessibility label text when
additionalAccessibilityLabelis boolean. This aligns with the established pattern for handling iOS VoiceOver issues withaccessibilityRole="radio"andaccessibilityState.Based on learnings, this approach ensures screen readers correctly announce the selection state on iOS.
248-248: Interface export enables external type usage.Adding the
exportkeyword allows other components to import and use theIListItemtype, which is necessary for the broader refactoring introducingList.Radioand other list components.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
.maestro/tests/assorted/join-from-directory.yaml (3)
29-30: Consider reducing the animation timeout.A 10-second timeout for
waitForAnimationToEndis quite long. If this was added to address test flakiness, consider whether a shorter duration (e.g., 2-3 seconds) would suffice, which would improve test execution time.
100-103: Consider adding state-change verification and initial state setup.The test now relies on the accessibility label format 'Users unselected', which makes it more brittle. Additionally:
- The test assumes 'Users' starts unselected but doesn't explicitly verify or set the initial filter state.
- After tapping 'Users unselected' (line 103), there's no verification that the filter actually changed state (e.g., checking for 'Users selected' or verifying the directory content changed).
This could lead to flakiness if:
- The label format changes (e.g., to '☐ Users' or 'Users (unselected)')
- Previous tests or saved state leave 'Users' already selected
- The tap doesn't successfully change the filter
Consider adding verification steps
After line 103, add a step to verify the filter changed:
- extendedWaitUntil: visible: text: 'Users selected' timeout: 5000Or verify the filter menu closed and directory content reflects the Users filter.
148-151: Same concerns as the 'Users unselected' change above.This has the same brittleness and missing verification issues as the 'Users unselected' change in lines 100-103. After tapping 'Teams unselected' (line 151), consider adding verification that the filter state changed to 'Teams selected' or that the directory content reflects the Teams filter.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
.maestro/tests/assorted/join-from-directory.yaml
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: E2E Build iOS / ios-build
- GitHub Check: E2E Build Android / android-build
- GitHub Check: ESLint and Test / run-eslint-and-test
- GitHub Check: format
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
app/views/CreateDiscussionView/index.tsx (1)
49-51: Stale state issue remains unresolved.The previous review comment about state synchronization is still applicable. The local
usersstate (line 69) is initialized from ReduxselectedUsersonce at mount but never synchronizes with subsequent Redux updates. If a user navigates away to add more users and returns, the UI will show stale data.Please refer to the previous review comment for recommended solutions (use Redux state directly or add a
useEffectto sync).Also applies to: 98-100, 116, 181
Also applies to: 53-53, 63-64, 69-69
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
app/containers/List/__snapshots__/List.test.tsx.snapis excluded by!**/*.snap
📒 Files selected for processing (5)
app/containers/List/List.stories.tsxapp/containers/SelectedUsers/index.tsxapp/containers/ServerItem/index.tsxapp/views/CreateChannelView/index.tsxapp/views/CreateDiscussionView/index.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- app/views/CreateChannelView/index.tsx
- app/containers/List/List.stories.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
📚 Learning: 2025-12-17T15:56:22.578Z
Learnt from: OtavioStasiak
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6499
File: app/containers/ServerItem/index.tsx:34-36
Timestamp: 2025-12-17T15:56:22.578Z
Learning: In the Rocket.Chat React Native codebase, for radio button components on iOS, include the selection state ("Selected"/"Unselected") in the accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}, because iOS VoiceOver has known issues with accessibilityRole="radio" + accessibilityState that prevent correct state announcement.
Applied to files:
app/containers/ServerItem/index.tsxapp/views/CreateDiscussionView/index.tsx
🧬 Code graph analysis (1)
app/views/CreateDiscussionView/index.tsx (2)
app/reducers/selectedUsers.ts (1)
ISelectedUser(4-11)app/actions/selectedUsers.ts (1)
removeUser(25-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: ESLint and Test / run-eslint-and-test
- GitHub Check: format
🔇 Additional comments (8)
app/containers/ServerItem/index.tsx (4)
5-5: LGTM! Import style matches maintainer preference.The namespace import
import * as Listis the preferred style as suggested in previous reviews, and it supports the refactored icon usage.
34-36: Excellent! Correctly implements iOS VoiceOver workaround.The implementation correctly includes the selection state ("Selected"/"Unselected") directly in the
accessibilityLabelinstead of usingaccessibilityState={{ checked: hasCheck }}. This approach works around the known iOS VoiceOver issues withaccessibilityRole="radio"+accessibilityState, ensuring proper state announcement for screen reader users on iOS.The use of computed constants (
iconName,iconColor) also improves code readability.Based on learnings, this is the correct approach for iOS compatibility.
44-46: LGTM! Accessibility props properly configured.The accessibility implementation is correct:
accessibilityRole='radio'provides proper semantic identificationaccessibilityLabelincludes both identifying information and stateaccessibilityHintguides user interaction- Correctly avoids
accessibilityStateper iOS VoiceOver compatibility requirementsThis addresses the PR objectives (MA-120: correct radio button role in menus).
73-74: LGTM! Clean refactoring to List.Icon.The refactoring from a dedicated Radio component to
List.Iconimproves consistency across the codebase. The icon appearance (radio-checked/unchecked with appropriate colors) is maintained through the computed constants, and this aligns with the PR objective to consolidate components and reduce code duplication.app/views/CreateDiscussionView/index.tsx (4)
30-31: LGTM!The new imports correctly support the integration of the SelectedUsers component and proper typing for user data.
145-146: LGTM!The background color update to
surfaceTintprovides consistent theming across the view.
176-176: Good catch on the accessibility typo!Correcting
additionalAcessibilityLabeltoadditionalAccessibilityLabelimproves code correctness and aligns with proper accessibility implementation.
181-181: SelectedUsers integration looks correct.The component is properly integrated with appropriate props. However, note that the
removeUsercallback signature matches the interface issue identified inapp/containers/SelectedUsers/index.tsx(where the interface should be updated to reflect thatonPressreceives an ID string, not the full item).Once the interface fix in SelectedUsers is applied, verify this usage still compiles correctly.
Proposed changes
Issue(s)
https://rocketchat.atlassian.net/browse/MA-181
https://rocketchat.atlassian.net/browse/MA-120
https://rocketchat.atlassian.net/browse/MA-179
https://rocketchat.atlassian.net/browse/MA-178
https://rocketchat.atlassian.net/browse/MA-118
How to test or reproduce
Screenshots
Types of changes
Checklist
Further comments
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.