Skip to content

feat(a11y): adjust the name and role of the multi-select#6499

Merged
diegolmello merged 120 commits intodevelopfrom
feat-a11y-select-discussion
Dec 30, 2025
Merged

feat(a11y): adjust the name and role of the multi-select#6499
diegolmello merged 120 commits intodevelopfrom
feat-a11y-select-discussion

Conversation

@OtavioStasiak
Copy link
Copy Markdown
Contributor

@OtavioStasiak OtavioStasiak commented Jul 23, 2025

Proposed changes

  • Adjust the accessibilityRole of the switch and radio buttons.
  • Create a ListRadio component to avoid repeating code.
  • Fix a small typo (wrong accessibility props).
  • Create a Channel.

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

  • Open the app;
  • Turn screen reader on;
  • Navigate through ThemeView, LanguageView, DefaultBrowserView, MediaAutoDownloadView;
  • Create a channel;
  • Create a discussion;

Screenshots

Before After
Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 28 31 Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 20 02
Before After
Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 31 53 Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 40 31
Before After
Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 32 44 Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 22 10
Before After
Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 34 03 Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 21 51
Before After
Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 37 08 Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 22 20
Before After
Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 38 16 Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 22 49
Before After
Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 39 39 Simulator Screenshot - iPhone 16 - 2025-11-21 at 18 22 30
SelectedUsersList Tablet Portrait SelectedUsersList Tablet Landscape
Simulator Screenshot - iPad Pro 11-inch (M4) - 2025-12-16 at 13 29 46 Simulator Screenshot - iPad Pro 11-inch (M4) - 2025-12-16 at 13 29 40

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

Summary by CodeRabbit

  • New Features

    • Radio-style selection UI added across multiple lists
    • New SelectedUsers component for chip-based selected-user display
  • Improvements

    • Streamlined user-selection flows for discussions/channels
    • Enhanced accessibility: proper semantic roles and iOS state announcements
    • Replaced several list item UIs with unified radio components
  • Bug Fixes

    • Corrected accessibility label prop name typos across many screens

✏️ Tip: You can customize this high-level summary in your review settings.

@OtavioStasiak OtavioStasiak changed the title feat(a11y): adjust name and role of multi select feat(a11y): adjust the name and role of the multi-select Jul 23, 2025
@OtavioStasiak OtavioStasiak had a problem deploying to official_android_build July 23, 2025 18:10 — with GitHub Actions Failure
@OtavioStasiak OtavioStasiak temporarily deployed to experimental_ios_build July 23, 2025 18:10 — with GitHub Actions Inactive
@OtavioStasiak OtavioStasiak temporarily deployed to experimental_android_build July 23, 2025 18:10 — with GitHub Actions Inactive
@OtavioStasiak OtavioStasiak had a problem deploying to upload_experimental_android July 23, 2025 18:44 — with GitHub Actions Failure
@github-actions
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown

iOS Build Available

Rocket.Chat Experimental 4.63.0.87803

@OtavioStasiak OtavioStasiak temporarily deployed to experimental_ios_build August 5, 2025 17:59 — with GitHub Actions Inactive
@OtavioStasiak OtavioStasiak temporarily deployed to experimental_android_build August 5, 2025 17:59 — with GitHub Actions Inactive
@OtavioStasiak OtavioStasiak had a problem deploying to upload_experimental_android August 5, 2025 19:01 — with GitHub Actions Error
@github-actions
Copy link
Copy Markdown

github-actions bot commented Aug 5, 2025

@github-actions
Copy link
Copy Markdown

github-actions bot commented Aug 5, 2025

iOS Build Available

Rocket.Chat Experimental 4.64.0.100081

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 for nextAction across parameter lists.

The nextAction field 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.tsx lines 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 () => void is 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: Change

Apply the same fix to ModalStackParamList in app/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 require accessibilityRole='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:

  1. Operator choice (lines 73-74): The || operator treats empty string as falsy. If a caller explicitly passes buttonText="" 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.

  2. Incomplete dependencies (line 87): The useLayoutEffect references title (line 72), showSkipText (line 68 via handleButtonTitle), and showButton (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.

📥 Commits

Reviewing files that changed from the base of the PR and between 108bc3f and cbedddf.

📒 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 for showSkipText.

Defaulting showSkipText to true maintains backward compatibility for existing callers while allowing new callers to opt out by explicitly passing false.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 on multi-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.

📥 Commits

Reviewing files that changed from the base of the PR and between 735cb0e and 1dba8d1.

📒 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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: Add accessibilityState to communicate switch state to screen readers.

The three switch items now have accessibilityRole='switch', but they're missing accessibilityState={{ 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 duplicate ListPicker section.

Lines 122–132 are an exact duplicate of the ListPicker section 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 1dba8d1 and b325b0b.

📒 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.tsx
  • app/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 additionalAcessibilityLabel to additionalAccessibilityLabel fixes 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.

Copy link
Copy Markdown
Member

@diegolmello diegolmello left a comment

Choose a reason for hiding this comment

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

Minor detail and just to make sure, have you run e2e tests?

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 to Accessibility. Renaming to handleAccessibilityLabel would 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.

📥 Commits

Reviewing files that changed from the base of the PR and between f191af6 and 5bf54c9.

⛔ Files ignored due to path filters (1)
  • app/containers/Chip/__snapshots__/Chip.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • app/containers/ActionSheet/Provider.tsx
  • app/containers/Chip/index.tsx
  • app/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 additionalAcessibilityLabeladditionalAccessibilityLabel and additionalAcessibilityLabelCheckadditionalAccessibilityLabelCheck properly 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 IListItem allows other components (like the new ListRadio) to extend this interface, which supports the accessibility improvements and component reusability goals of this PR.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 function handleAcessibilityLabel still uses the old spelling. For consistency, consider renaming it to handleAccessibilityLabel.

🔎 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 5bf54c9 and 974dc9e.

📒 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 (AcessibilityAccessibility) 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 additionalAccessibilityLabel is boolean. This aligns with the established pattern for handling iOS VoiceOver issues with accessibilityRole="radio" and accessibilityState.

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 export keyword allows other components to import and use the IListItem type, which is necessary for the broader refactoring introducing List.Radio and other list components.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 waitForAnimationToEnd is 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:

  1. The test assumes 'Users' starts unselected but doesn't explicitly verify or set the initial filter state.
  2. 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: 5000

Or 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.

📥 Commits

Reviewing files that changed from the base of the PR and between fdb45af and 363d7f5.

📒 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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 users state (line 69) is initialized from Redux selectedUsers once 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 useEffect to 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 363d7f5 and 19ddc06.

⛔ Files ignored due to path filters (1)
  • app/containers/List/__snapshots__/List.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • app/containers/List/List.stories.tsx
  • app/containers/SelectedUsers/index.tsx
  • app/containers/ServerItem/index.tsx
  • app/views/CreateChannelView/index.tsx
  • app/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.tsx
  • app/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 List is 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 accessibilityLabel instead of using accessibilityState={{ checked: hasCheck }}. This approach works around the known iOS VoiceOver issues with accessibilityRole="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 identification
  • accessibilityLabel includes both identifying information and state
  • accessibilityHint guides user interaction
  • Correctly avoids accessibilityState per iOS VoiceOver compatibility requirements

This 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.Icon improves 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 surfaceTint provides consistent theming across the view.


176-176: Good catch on the accessibility typo!

Correcting additionalAcessibilityLabel to additionalAccessibilityLabel improves 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 removeUser callback signature matches the interface issue identified in app/containers/SelectedUsers/index.tsx (where the interface should be updated to reflect that onPress receives an ID string, not the full item).

Once the interface fix in SelectedUsers is applied, verify this usage still compiles correctly.

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.

3 participants