Skip to content

feat: add onRequestClose to ModalBottomSheet - #70

Open
DanyKrk wants to merge 55 commits into
software-mansion-labs:mainfrom
DanyKrk:onRequestClose
Open

feat: add onRequestClose to ModalBottomSheet#70
DanyKrk wants to merge 55 commits into
software-mansion-labs:mainfrom
DanyKrk:onRequestClose

Conversation

@DanyKrk

@DanyKrk DanyKrk commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds an Android-only onRequestClose callback to ModalBottomSheet, following React Native Modal semantics as closely as practical.

Android system Back, committed predictive Back gestures, and complete, unmodified physical-keyboard Escape presses now emit the same close-request event. The callback only reports the request: it does not change the controlled index, select a detent, or dismiss the sheet. The consumer decides whether and how to close it.

Behavior

  • Emits exactly one close request for an eligible Back action or completed Escape press.
  • Keeps the consumer in control of the resulting state change and transition.
  • Consumes further Back and Escape input while a visible sheet animates closed, preventing duplicate requests until it settles.
  • Routes portal requests according to presentation order, with the topmost active portal owning the request while closed or unresolved portals are skipped.
  • Preserves host input behavior when the active presentation has no onRequestClose handler.

Inline sheets, iOS, accessibility dismiss actions, scrim taps, drag gestures, and programmatic index changes keep their existing behavior.

Implementation

  • Adds the typed onRequestClose prop, Fabric direct event, and native handler-presence signal.
  • Introduces shared Back and Escape request resolution and presentation tracking.
  • Coordinates portal ownership across multiple sheets, window roots, lifecycle changes, and predictive Back sequences.
  • Applies the same onRequestClose semantics to portal and nativeOverlay presentations.
  • Adds an example screen covering closing, no-op, and omitted handlers in both presentation modes.

Test coverage

  • Adds 64 JVM and Robolectric tests covering request decisions, Escape sequences, predictive Back, portal ordering, lifecycle transitions, overlay behavior, presentation tracking, and the JS/native event bridge.
  • Adds 5 Android instrumentation tests covering Escape routing through focused views and Back handling while a sheet animates closed
  • Adds Android test commands and contributor guidance in TESTING.md.
  • Runs Android unit tests and instrumentation assembly in the main check job.
  • Runs instrumentation tests on an API 36 emulator with hardware-keyboard input enabled.

DanyKrk added 26 commits August 20, 2026 19:15
  Register Back and Escape handlers lazily when onRequestClose is first
  provided, then preserve their ordering by toggling eligibility instead
  of re-registering them.

  Reset registrations at structural host boundaries and add coverage for
  lifecycle changes, multiple sheets, predictive Back, and handler removal.
  Supersede unfinished presses on a new initial key-down and retain
  short-lived tombstones so late events cannot finish the replacement.
Publish portal ownership and emission eligibility as a single state snapshot.
Keep handlers stable while inactive portals yield new Back and Escape requests
without retargeting input already in progress.

Expand lifecycle, ownership, and controlled-dismissal coverage and update docs.
- remove redundant request-close handling updates
- replace reflection-based assertions with explicit test snapshots
- focus Robolectric coverage on platform-specific SDK paths
- simplify bottom sheet rendering
…andler

Forward Back to the host Activity and leave Escape in normal key routing when onRequestClose is omitted. Keep predictive Back actions pinned for the full gesture and ignore commits delivered after overlay teardown.

Expand unit and instrumented coverage and update docs and examples.
Keep eligibility loss terminal for the current Escape sequence, even if the handler or modal state is restored before key-up. Reuse the shared dispatcher state for portal and native-overlay handling.
Keep portal and native-overlay presentations as close-input boundaries while visible close animations settle, consuming Back and Escape without
emitting another onRequestClose.

Unify request, consume, and pass-through transport states, stabilize predictive Back handling, and update tests, examples, and documentation.
@DanyKrk
DanyKrk marked this pull request as draft August 20, 2026 17:31
@DanyKrk DanyKrk changed the title On request close Add onRequestClose prop Aug 20, 2026
DanyKrk added 22 commits August 24, 2026 16:07
- track closing presentation through a production state tracker
- verify controllers through observable input routing and window flags
- cover close-animation ownership with an instrumentation test
Rename request-close state, routing, lifecycle, and presentation symbols
to reflect their actual responsibilities. Align the native bridge, tests,
and documentation with the updated terminology.
Restore the host's current interaction state after the dialog's final window configuration so an already-open sheet does not remain invisible, unfocusable, or untouchable when moved into a native overlay.

Cover the restored window flags and wait for real window focus before testing hardware Escape routing.
- focus public docs on observable onRequestClose behavior
- clarify native routing comments and portal demo copy
- generalize Android testing instructions
@DanyKrk
DanyKrk marked this pull request as ready for review September 1, 2026 12:12
Comment thread src/ModalBottomSheet.tsx Outdated
Comment thread android/build.gradle
Comment on lines +77 to +78
implementation "androidx.activity:activity:1.8.2"
implementation "androidx.core:core:1.8.0"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Are 1.8.2 and 1.8.0 intended as minimum compatibility floors? They’re substantially behind the latest stable releases: 1.13.0 and 1.19.0, respectively. If this is intentional, let’s document it; otherwise, it would be good to update.

@@ -0,0 +1,93 @@
@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This seems redundant.

@@ -0,0 +1,378 @@
@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The OVERRIDE_DEPRECATION suppression is needed only for NoOpInstrumentedEventDispatcher.onCatalystInstanceDestroyed (line 377). Could we keep DEPRECATION here, explain that it’s for the legacy-architecture fixture, and move the OVERRIDE_DEPRECATION suppression onto the override of the aforementioned function?

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.

[Feature request] Add onRequestClose callback to normalize Android back and ESC key handling

2 participants