Skip to content

Comments

[drawer] Prevent dismiss through swipe when the component is controlled#4133

Open
flaviendelangle wants to merge 6 commits intomui:masterfrom
flaviendelangle:drawer-controlled-open
Open

[drawer] Prevent dismiss through swipe when the component is controlled#4133
flaviendelangle wants to merge 6 commits intomui:masterfrom
flaviendelangle:drawer-controlled-open

Conversation

@flaviendelangle
Copy link
Member

@flaviendelangle flaviendelangle commented Feb 18, 2026

@flaviendelangle flaviendelangle self-assigned this Feb 18, 2026
@flaviendelangle flaviendelangle added type: bug It doesn't behave as expected. component: drawer Changes related to the drawer component. labels Feb 18, 2026
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 18, 2026

commit: 5536648

@mui-bot
Copy link

mui-bot commented Feb 18, 2026

Bundle size report

Bundle Parsed size Gzip size
@base-ui/react 🔺+253B(+0.06%) 🔺+47B(+0.03%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@netlify
Copy link

netlify bot commented Feb 18, 2026

Deploy Preview for base-ui ready!

Name Link
🔨 Latest commit 5536648
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/6998056ad81ec7000924fe60
😎 Deploy Preview https://deploy-preview-4133--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@flaviendelangle flaviendelangle marked this pull request as ready for review February 18, 2026 13:20
Comment on lines 89 to 95
// In controlled mode, don't update internal state.
// The parent must update the `open` prop for the state to change.
// Cancel the event so callers (e.g. DrawerViewport.onDismiss) clean up properly.
if (this.state.openProp !== undefined) {
eventDetails.cancel();
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems too broad and is likely to break other cases. Indeed that's what Codex found:

PR #4133 review findings (2 regressions)

Summary

This PR fixes controlled Drawer swipe-dismiss cleanup and adds a test, but introduces two controlled-mode regressions that should be addressed before merge.

Details

  1. [P1] Controlled Dialog/Drawer no longer emit openchange

    • DialogStore.setOpen now cancels+returns in controlled mode before openchange emit.
    • FloatingFocusManager uses openchange to compute close interaction type and return-focus behavior.
    • Impact: controlled finalFocus={(type) => ...} and outside-press focus return behavior can become stale/incorrect.
  2. [P2] Controlled Drawer with snap points can restore previous snap point after swipe-close

    • Forced cancel in controlled mode makes Drawer swipe dismiss always follow the canceled-cleanup branch.
    • That branch restores the pending snap point, which can conflict with the default snap-point reset on close.
    • Impact: if parent accepts close (open=false), reopening may start at the old snap point instead of default.

Copy link
Member Author

Choose a reason for hiding this comment

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

Aren't we doing to much magic with the controlled value here then ?
I would love to just have an internal state.open and if it's controlled then I don't update it and nothing changes 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

In the current architecture state.open is still used by internal selectors/consumers beyond rendering.

If we stop updating state.open in controlled mode, we regress at least:

  1. trigger open state (data-popup-open via isOpenedByTrigger), and
  2. DialogHandle.isOpen (it reads store.state.open).

For this PR, I would keep the Drawer swipe fix localized in DrawerViewport, similar to the "workaround" I posted in the issue

PR #4133 updated review findings (2 regressions)

Summary

The latest update fixes the earlier openchange emission issue, but introduces two new controlled-mode regressions.

Details

  1. [P1] Controlled Dialog/Drawer triggers lose open state

    • DialogStore.setOpen now avoids updating internal state.open in controlled mode.
    • Trigger-open state is still computed from state.open (isOpenedByTrigger), and DialogTrigger uses that to set trigger open/data-popup-open.
    • Impact: in standard controlled usage (open + onOpenChange={setOpen}), popup can be open while trigger appears closed (data-popup-open missing).
  2. [P2] DialogHandle.isOpen becomes stale in controlled mode

    • DialogHandle.isOpen returns store.state.open.
    • With the new controlled-mode behavior, state.open may no longer track effective open state.
    • Impact: handle.isOpen can report incorrect values (commonly false while actually open).

@atomiks
Copy link
Contributor

atomiks commented Feb 20, 2026

Codex still found an issue with the latest change, but it resolved the broader issues with the DialogStore

PR #4133 latest review findings (2 issues)

Summary

The latest update narrows the fix to DrawerViewport, but one key regression appears to remain in controlled + snap points swipe-close behavior, and the tests still miss that path.

Details

  1. [P1] Controlled swipe-close is treated as canceled even when parent accepts close

    • In DrawerViewport.onDismiss, dismiss is force-canceled when !dismissEventDetails.isCanceled && store.select('open').
    • In controlled mode, store.select('open') is typically still true immediately after setOpen(false, ...) (before parent re-render), so this often cancels even accepted closes.
    • Canceled cleanup restores pendingSwipeCloseSnapPointRef, which can override the close-reset done in DrawerRoot.
    • Impact: controlled drawer with snapPoints can reopen at the previous snap point after a swipe close that parent actually accepted.
  2. [P2] Test coverage still misses accepted controlled close with snap points

    • Added test covers only “controlled and always open” (parent refuses close), with no snapPoints.
    • Missing: controlled + snapPoints + parent sets open=false on swipe.
    • That exact path is where the remaining regression is likely.

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

Labels

component: drawer Changes related to the drawer component. type: bug It doesn't behave as expected.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[drawer] Dismissible trough swipe in controlled mode

3 participants