Skip to content

feat: Migrate to react-native-true-sheet#6970

Merged
diegolmello merged 82 commits into
developfrom
test.poc-a11y-bottom-shet
Mar 24, 2026
Merged

feat: Migrate to react-native-true-sheet#6970
diegolmello merged 82 commits into
developfrom
test.poc-a11y-bottom-shet

Conversation

@OtavioStasiak

@OtavioStasiak OtavioStasiak commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

Use react-native-true-sheet instead of the Discord bottom sheet, because react-native-true-sheet works as expected in accessibility (a11y) terms.

PS: GestureHandlerRootView defaults to flex: 1, which causes it to expand and consume all available height inside the sheet, pushing the scrollable content area to zero. Setting flex: 0 pins the view to its intrinsic content size (only as tall as its children), so TrueSheet can correctly distribute space between the fixed header zone and the scrollable content below.

Issue(s)

https://rocketchat.atlassian.net/browse/CORE-1809

How to test or reproduce

  • Open the app;
  • Open the action sheet in places such as Change Workspace, message actions, and server history;

Screenshots

iPhone portrait iPhone landscape
Simulator Screenshot - iPhone 16 - 2026-02-10 at 11 55 07 Simulator Screenshot - iPhone 16 - 2026-02-10 at 16 56 49
iPad portrait iPad landscape
Simulator Screenshot - iPad (A16) - 2026-02-10 at 14 28 13 Simulator Screenshot - iPad (A16) - 2026-02-10 at 14 28 22
iPad mini portrait iPad mini landscape
Simulator Screenshot - iPad mini (A17 Pro) - 2026-02-10 at 17 15 26 Simulator Screenshot - iPad mini (A17 Pro) - 2026-02-10 at 17 03 52
Android portrait Android landscape
Screenshot_1770752018 Screenshot_1770753454
Android tablet portrait Android tablet landscape
Screenshot_20260210-183407_RocketChat Experimental Screenshot_20260210-183420_RocketChat Experimental
Android update bottom sheet iOS update bottom sheet
Screenshot_1773246324 Simulator Screenshot - iPhone 16 - 2026-03-11 at 13 17 20

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

    • Action sheet handle is tappable to dismiss
    • Action sheets expose a full-container display mode and detent-driven sizing/scroll behavior
  • Improvements

    • Automatic keyboard dismissal and haptic feedback on sheet show/hide
    • More accurate layout, min/max height, and scrolling behavior for action sheet content
    • Emoji picker rendering improved when shown inside action sheets
  • Tests

    • Stability improvements: animation waits and id-based test selectors added
  • Refactor

    • Action sheet rendering and internals reworked for more predictable presentation

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Migrates ActionSheet from @discord/bottom-sheet to @lodev09/react-native-true-sheet, introduces useActionSheetDetents for detent/maxHeight/scroll behavior, simplifies input handling, adds testIDs for E2E, updates styles/layouts, and adjusts related tests and mocks.

Changes

Cohort / File(s) Summary
Core ActionSheet Migration
app/containers/ActionSheet/ActionSheet.tsx, app/containers/ActionSheet/useActionSheetDetents.ts, app/containers/ActionSheet/Handle.tsx, app/containers/ActionSheet/BottomSheetContent.tsx, app/containers/ActionSheet/Provider.tsx, app/containers/ActionSheet/styles.ts
Replace BottomSheet with TrueSheet; add useActionSheetDetents hook and HANDLE_HEIGHT; refactor visibility/state/height handling, add onDidDismiss lifecycle, make Handle tappable, add fullContainer/contentMinHeight/scrollEnabled support, and adjust layout styles.
Dependency & Mocking Updates
package.json, jest.setup.js, patches/@discord+bottom-sheet+4.6.1.patch, patches/@lodev09+react-native-true-sheet+3.7.3.patch
Remove @discord/bottom-sheet and @gorhom/bottom-sheet; add @lodev09/react-native-true-sheet; update Jest mock to provide TrueSheet/TrueSheetProvider; remove old bottom-sheet patch and alter true-sheet native prop setters.
BottomSheet Content & Input Simplification
app/containers/ActionSheet/BottomSheetContent.tsx, app/containers/TextInput/FormTextInput.tsx, app/containers/ActionSheet/ActionSheetContentWithInputAndSubmit/index.tsx
BottomSheetContent now supports FlatList fallback, fullContainer, contentMinHeight, scrollEnabled; FormTextInput drops BottomSheetTextInput and always uses TextInput; remove platform-specific bottomSheet props from action-sheet inputs.
EmojiPicker BottomSheet Integration
app/containers/EmojiPicker/index.tsx, app/containers/EmojiPicker/EmojiCategory.tsx, app/containers/EmojiPicker/PressableEmoji.tsx, app/containers/EmojiPicker/interfaces.ts, app/containers/EmojiPicker/EmojiSearch.tsx
Introduce bottomSheet prop across EmojiPicker API; compute Android-specific bottom padding when used in sheet; replace Pressable with Touch for emoji items; propagate prop through scenes and interfaces.
TestIDs & Message Actions
app/containers/MessageActions/index.tsx, various views using action sheets (app/views/RoomView/*, app/views/DirectoryView/*, app/views/RoomMembersView/*, app/views/RoomView/index.tsx)
Add testID attributes to action sheet options and propagate fullContainer/enableContentPanningGesture/bottomSheet options where used; convert some containers to List.Container.
E2E Test Adjustments
.maestro/tests/*, .maestro/tests/teams/utils/*, .maestro/tests/accessibilityAndAppearance/*, .maestro/tests/room/*
Replace text-based selectors with id-based selectors (action-sheet-handle, message-actions-*), convert swipes to taps on handle, add hideKeyboard and waitForAnimationToEnd steps to stabilize animations.
Hook & UI Layout Tweaks
app/lib/hooks/useVideoConf/*, app/containers/List/ListContainer.tsx, app/containers/UIKit/*, app/containers/SupportedVersions/styles.ts, app/views/DirectoryView/Options.tsx
Add orientation-aware action sheet heights and snaps, extend ListContainer props to ScrollViewProps, reduce flex in some containers, and apply enableContentPanningGesture:false in several showActionSheet calls.
Tests & New Unit Test
app/containers/ActionSheet/useActionSheetDetents.test.tsx, jest.setup.js
Add tests validating detents normalization and detent/maxHeight/scrollEnabled outcomes; update Jest mock for TrueSheet.
Miscellaneous
tsconfig.json, small UI/gesture refactors (app/views/RoomsListView/*, app/containers/UIKit/MultiSelect/*, others)`
Minor formatting, small hook/effect changes (useLayoutEffect), and style adjustments.

Sequence Diagram

sequenceDiagram
    participant UI as Caller (Component)
    participant AS as ActionSheet (wrapper)
    participant Det as Detents Hook
    participant TS as TrueSheet
    participant CB as options.onClose / onDidDismiss

    UI->>AS: showActionSheet(options)
    AS->>Det: compute detents/maxHeight/scrollEnabled
    Det-->>AS: detents, maxHeight, scrollEnabled
    AS->>TS: present(detents, maxHeight, header, content)
    TS->>TS: render sheet with detents
    User->>TS: interact (tap handle / content pan)
    TS->>AS: onDidDismiss()
    AS->>CB: call options.onClose() (if provided)
    AS->>AS: reset internal state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Migrate to react-native-true-sheet' accurately summarizes the main change—replacing the Discord bottom sheet with react-native-true-sheet for improved accessibility.
Linked Issues check ✅ Passed The PR implements the core objective from CORE-1809 to migrate ActionSheet to react-native-true-sheet, replacing BottomSheet with TrueSheet, updating dependencies, and refactoring related components and tests.
Out of Scope Changes check ✅ Passed Changes are primarily focused on the migration (ActionSheet, dependencies, patches) with necessary supporting updates to EmojiPicker, FormTextInput, and test files. Minor style adjustments align with the new sheet behavior.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can use TruffleHog to scan for secrets in your code with verification capabilities.

Add a TruffleHog config file (e.g. trufflehog-config.yml, trufflehog.yml) to your project to customize detectors and scanning behavior. The tool runs only when a config file is present.

@Rohit3523

Copy link
Copy Markdown
Member

@coderabbitai review

@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat Experimental 4.71.0.108387

@github-actions

Copy link
Copy Markdown

Android Build Available

Rocket.Chat Experimental 4.71.0.108388

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNRgU8smap7h_FKwR62mmLrOcJbQlCmtKGuvGhddKUzMvPGcqsxMTltpt8tN4eKDTgQP_TeqYemQaHy6rgnx

@github-actions

Copy link
Copy Markdown

Android Build Available

Rocket.Chat Experimental 4.71.0.108391

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNSA9JzEZnuZulrBCGIcRzcLC_QqlIdb8v9pub2sNaNUuWM4P03rpeGfzqFEc0hb92peceCybxSqXQP97k9-

@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat Experimental 4.71.0.108392

@diegolmello diegolmello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Besides these comments, I'm missing unit/integration tests, so I built this Cursor plan to understand how we could add them.
Feel free to copy/paste to your fav AI and ask it to create the tests for you.
Just make sure to review and confirm the tests make sense.


name: ActionSheet test strategy
overview: Define a maintainable unit/integration-focused test plan for the true-sheet migration in ActionSheet, including Storybook coverage and a clear breakdown into independent work items for future AI agents.
todos:

  • id: track-b-lifecycle-wrapper
    content: Define and implement ActionSheet lifecycle/wrapper integration tests with fake timers and dismissal callback assertions.
    status: pending
  • id: track-c-options-interactions
    content: Add BottomSheetContent/Item/Handle interaction and accessibility tests for options behavior and disabled paths.
    status: pending
  • id: track-d-detents-expansion
    content: Expand detents hook tests to cover orientation overrides, boundary clamping, and dynamic-height branches.
    status: pending
  • id: track-e-input-submit
    content: Add integration tests for ActionSheetContentWithInputAndSubmit required-input logic and submit/cancel flows.
    status: pending
  • id: track-a-provider-contract
    content: Add Provider contract tests for show/hide refs and hook API safety.
    status: pending
  • id: track-f-storybook-baseline
    content: Create ActionSheet stories and snapshot tests for key visual/structural states.
    status: pending
    isProject: false

ActionSheet Test Plan (True-Sheet Migration)

Scope And Goal

Build durable test coverage for @app/containers/ActionSheet so regressions are caught early while keeping tests readable, meaningful, and maintainable.

Primary files in scope:

Supporting infra and patterns:

Should We Test true-sheet Itself?

Short answer: not directly in unit/integration Jest tests.

What makes sense:

  • Test our wrapper contract with true-sheet (props passed, imperative methods called, callbacks wired, timing behavior).
  • Test our business/UI logic around options, callbacks, disabled states, orientation snaps, and close lifecycle.

What does not make sense in Jest/RNTL:

  • Native drag physics, animation smoothness, detent snapping transitions, backdrop tap internals, keyboard-native layout interplay.
  • These depend on native runtime and should be validated by E2E/manual checks (separate track, not part of this unit/integration plan).

User Stories To Protect

  1. As a user, opening an ActionSheet shows the expected content and keyboard is dismissed.
  2. As a user, closing ActionSheet via handle/cancel/back/native dismiss reliably closes once and runs onClose once.
  3. As a user, I can select options with correct enabled/disabled behavior and visual semantics.
  4. As a user, the sheet size/detents are appropriate for content size and orientation.
  5. As a user, custom content mode behaves predictably (layout measurement, full container behavior).
  6. As a user, accessibility labels/roles make controls understandable.
  7. As a developer, showActionSheet and hideActionSheet APIs are stable and safe across rapid state changes.

Edge Cases To Include

  • Rapid show -> hide -> show timing around the 200ms present timer.
  • onClose callback identity snapshot (latest callback should fire on dismiss).
  • options=[] versus options undefined behavior differences.
  • Duplicate option titles (current keyExtractor risk) documented with an assertion-level test note.
  • Disabled item press emits toast event and does not call action.
  • portraitSnaps / landscapeSnaps override behavior.
  • Android back-handler consumes back only when visible.
  • iOS fullContainer + snaps min-height behavior in content mode.

RNTL + Storybook Strategy

  • Use RNTL integration-style tests for behavior and callback contracts.
  • Use Storybook snapshot tests as broad visual-regression baseline for key ActionSheet states.
  • Add targeted interaction assertions in RNTL where snapshots are insufficient (callbacks, timing, dismiss flow).

Proposed Test Workstreams (Independent Future AI Tasks)

Track A: Provider Contract Tests

Target files:

Cases:

  • showActionSheetRef/hideActionSheetRef delegate to provider methods.
  • useActionSheet returns stable API in provider scope.
  • Calling show/hide without mounted sheet is safe (no throw).

Track B: ActionSheet Lifecycle And True-Sheet Wrapper Tests

Target files:

Cases:

  • show schedules present after 200ms (fake timers).
  • hide dismisses and cancels pending present timer.
  • onDidDismiss triggers current onClose callback exactly once.
  • Android back handling closes when visible and returns consume boolean.
  • Wrapper props passed to mocked TrueSheet: detents, dimmed, draggable, scrollable, maxHeight.

Track C: Options Rendering + Interaction Tests

Target files:

Cases:

  • Options list renders labels, subtitles, and accessory nodes.
  • Cancel footer appears only with hasCancel; press closes sheet.
  • Disabled item emits toast event and does not run onPress.
  • Handle press calls hideActionSheet.
  • Accessibility labels/roles/hints are present and readable.

Track D: Detents Hook Expansion

Target files:

Cases:

  • Orientation-specific snaps precedence (portraitSnaps/landscapeSnaps).
  • Clamping/sorting/normalization around min/max detents.
  • Dynamic content detent from measured height (no options mode).
  • Scroll enablement threshold and stable behavior at boundaries.

Track E: Input-And-Submit Integration Tests

Target files:

Cases:

  • Confirm button disabled state with required inputs.
  • onButtonPress and onChangeText handlers receive expected payload.
  • Cancel path hides sheet; submit path behavior remains explicit and verified.
  • Async submit path and timeout-driven hide behavior are deterministic with fake timers.

Track F: Storybook Coverage For ActionSheet States

Target files:

  • New story file: app/containers/ActionSheet/ActionSheet.stories.tsx
  • New snapshot test: app/containers/ActionSheet/ActionSheet.stories.test.tsx
  • Reuse .rnstorybook/generateSnapshots.tsx

Stories to add:

  • Option list: default, destructive option, disabled option.
  • Custom children mode.
  • hasCancel enabled state.
  • Compact vs long-content variants.

Suggested Test Architecture Rules (Maintainability)

  • Favor user-facing assertions over implementation internals.
  • Use helper builders for options payloads to reduce fixture noise.
  • Keep one behavior focus per test; descriptive names with “given/when/then” wording.
  • Reuse existing global mocks from jest.setup.js; avoid introducing redundant native mocks.
  • Use fake timers only for timer-driven behavior; restore real timers per suite.

Execution Order

  1. Track B and Track C first (highest regression risk).
  2. Track D and Track E second (logic depth and form behavior).
  3. Track A and Track F last (API hardening + visual baseline).

Validation Checklist For Completion

  • New/updated tests pass with yarn test.
  • No brittle timer/race failures across reruns.
  • Storybook snapshots are intentional and reviewed.
  • Each new suite documents the user story it protects in test titles.

Behavior Map

flowchart TD
  userAction[UserOrAPITrigger] --> showCall[showActionSheet]
  showCall --> schedulePresent[SchedulePresent200ms]
  schedulePresent --> trueSheetPresent[TrueSheetPresent]
  trueSheetPresent --> sheetVisible[SheetVisible]
  sheetVisible --> closePath[HandleCancelBackOrHide]
  closePath --> trueSheetDismiss[TrueSheetDismiss]
  trueSheetDismiss --> didDismiss[onDidDismiss]
  didDismiss --> onCloseCallback[InvokeOnCloseCallback]
Loading

Comment thread app/containers/ActionSheet/useActionSheetDetents.ts
Keyboard.dismiss();
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
onCloseSnapshotRef.current = options.onClose;
presentTimerRef.current = setTimeout(() => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This timeout to open the action sheet is hurting the UX.
It causes the impression the app is not fast enough.

It also creates a potential race condition on onClose, since it's being changed 200ms before the action sheet is actually displayed.

Comment thread patches/@lodev09+react-native-true-sheet+3.7.3.patch

@diegolmello diegolmello left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Tests/stories aren't quite there.
Let's remove them and keep the rest of the code, which seems fine.

@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat Experimental 4.71.0.108407

@github-actions

Copy link
Copy Markdown

Android Build Available

Rocket.Chat Experimental 4.71.0.108406

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNRCrHsChPPjTJFDUezCdGLrhUeS36Gqdb1AY7ZDSF5CpfQHDE8i07FIV5X7829IoMOhZhuFMBpA3LbJc_B2

@OtavioStasiak OtavioStasiak left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

LGTM! Tested on both platforms.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants