Skip to content

fix(ui): close only the open Select on Escape inside a Drawer#9176

Merged
alexcarpenter merged 2 commits into
mainfrom
esc-closes-checkout-drawer
Jul 16, 2026
Merged

fix(ui): close only the open Select on Escape inside a Drawer#9176
alexcarpenter merged 2 commits into
mainfrom
esc-closes-checkout-drawer

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Pressing Escape while a Select was open inside a Drawer (for example the payment method picker in Checkout) dismissed the entire Drawer instead of just the Select. Two issues were at play: the Select never applied floating-ui's getFloatingProps() to its floating element, so its Escape handler only lived as a native document listener that the Drawer's synthetic stopPropagation() prevented from firing; and Drawer.Root was not a FloatingTree node, so nested floating elements were not recognized as its children. The Select now wires up its interaction props (handling Escape itself and stopping propagation) and the Drawer roots a floating tree, so Escape now closes only the open Select and a second Escape closes the Drawer. To test: open the Checkout drawer, open the payment method Select, press Escape, and confirm only the Select closes.

BEFORE

before.mov

AFTER

after.mov

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • Bug Fixes

    • Pressing Escape while a Select menu is open inside a Drawer now closes only the Select menu.
    • Pressing Escape again dismisses the Drawer as expected.
    • Improved keyboard interaction for nested floating components and drawers.
  • Tests

    • Added coverage verifying Select and Drawer dismissal behavior.

@changeset-bot

changeset-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1531723

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@clerk/ui Patch
@clerk/chrome-extension Patch
@clerk/swingset Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jul 16, 2026 3:39pm
swingset Ready Ready Preview, Comment Jul 16, 2026 3:39pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 646a7a4d-034f-4db2-9be5-c09e9d70f567

📥 Commits

Reviewing files that changed from the base of the PR and between de00526 and 1531723.

📒 Files selected for processing (1)
  • packages/ui/src/elements/__tests__/Drawer.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui/src/elements/tests/Drawer.test.tsx

📝 Walkthrough

Walkthrough

Updates Floating UI nesting so Escape closes a Select inside a Drawer without dismissing the Drawer, with an integration test and changeset documenting the fix.

Changes

Drawer Select Escape handling

Layer / File(s) Summary
Root Drawer in the floating tree
packages/ui/src/elements/Drawer.tsx
Drawer roots create or join a FloatingTree, register a node, and wrap drawer content in FloatingNode.
Scope Select Escape handling
packages/ui/src/elements/Select.tsx, packages/ui/src/elements/__tests__/Drawer.test.tsx, .changeset/drawer-select-escape.md
Select options apply floating interaction props, and the test verifies Escape closes the Select without dismissing the Drawer; the changeset records the patch release.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

I’m a bunny in a Drawer wide,
I tap Escape—my Select slips aside.
The Drawer stays still,
As floating trees will,
And carrots remain safely inside.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: Escape now closes only the Select inside a Drawer.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9176

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9176

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9176

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9176

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9176

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9176

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9176

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9176

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9176

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9176

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9176

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9176

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9176

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9176

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9176

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9176

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9176

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9176

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9176

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9176

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9176

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9176

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9176

commit: 1531723

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-16T15:39:50.210Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 0
🔴 Breaking changes 0
🟡 Non-breaking changes 0
🟢 Additions 0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 1531723.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/elements/Drawer.tsx (1)

112-121: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the floating tree node ID in sync.

floatingProps.nodeId can override the generated ID passed to useFloating, but FloatingNode still uses the original ID. That splits tree registration and can break nested dismiss/focus behavior. Omit nodeId from floatingProps, or derive one effective ID and use it for both.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/elements/Drawer.tsx` around lines 112 - 121, Update the
Drawer component’s useFloating/FloatingNode setup to use one effective node ID:
prevent floatingProps.nodeId from overriding the generated nodeId, or derive the
override-aware ID and pass that same value to both useFloating and FloatingNode.
Preserve the existing nested dismiss and focus behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui/src/elements/__tests__/Drawer.test.tsx`:
- Around line 61-64: Extend the Escape-key test around the nested Select and
Drawer behavior by triggering a second Escape after confirming the Select is
dismissed, then assert that onOpenChange is called with false. Keep the existing
assertion that the first Escape does not dismiss the Drawer.
- Around line 19-20: Replace the as any casts in the
ClerkInstanceContext.Provider and EnvironmentProvider fixtures within
Drawer.test.tsx with properly typed Clerk and environment mocks. Ensure both
provider values satisfy their respective context types while preserving the
existing test data and exposing future provider shape changes to TypeScript.

---

Outside diff comments:
In `@packages/ui/src/elements/Drawer.tsx`:
- Around line 112-121: Update the Drawer component’s useFloating/FloatingNode
setup to use one effective node ID: prevent floatingProps.nodeId from overriding
the generated nodeId, or derive the override-aware ID and pass that same value
to both useFloating and FloatingNode. Preserve the existing nested dismiss and
focus behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ce80d4d-4e47-4a06-97a4-a275a4c5e08f

📥 Commits

Reviewing files that changed from the base of the PR and between bcbdda6 and de00526.

📒 Files selected for processing (4)
  • .changeset/drawer-select-escape.md
  • packages/ui/src/elements/Drawer.tsx
  • packages/ui/src/elements/Select.tsx
  • packages/ui/src/elements/__tests__/Drawer.test.tsx

Comment on lines +19 to +20
<ClerkInstanceContext.Provider value={{ value: { client: {}, user: {} } as any }}>
<EnvironmentProvider value={{ displayConfig: { applicationName: 'TestApp' } } as any}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Drawer.test.tsx ==\n'
cat -n packages/ui/src/elements/__tests__/Drawer.test.tsx

printf '\n== Search ClerkInstanceContext / EnvironmentProvider definitions ==\n'
rg -n "ClerkInstanceContext|EnvironmentProvider|displayConfig|applicationName|value: \{\s*client" packages/ui/src packages -g '!**/dist/**' -g '!**/build/**'

printf '\n== Type outlines for likely provider files ==\n'
fd -a "ClerkInstanceContext" packages/ui/src packages || true
fd -a "EnvironmentProvider" packages/ui/src packages || true

Repository: clerk/javascript

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== packages/ui/src/contexts/EnvironmentContext.tsx ==\n'
cat -n packages/ui/src/contexts/EnvironmentContext.tsx

printf '\n== packages/ui/src/test/create-fixtures.tsx (relevant slice) ==\n'
sed -n '1,220p' packages/ui/src/test/create-fixtures.tsx

printf '\n== Search for reusable clerk/environment test fixtures in packages/ui ==\n'
rg -n "create-fixtures|environmentMock|ClerkInstanceContext.Provider|EnvironmentProvider value=" packages/ui/src -g '!**/dist/**' -g '!**/build/**'

Repository: clerk/javascript

Length of output: 20221


Use typed fixtures for these providers. Replace the as any casts with a typed Clerk/environment mock so ClerkInstanceContext and EnvironmentProvider shape changes surface here instead of being hidden by the test setup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/elements/__tests__/Drawer.test.tsx` around lines 19 - 20,
Replace the as any casts in the ClerkInstanceContext.Provider and
EnvironmentProvider fixtures within Drawer.test.tsx with properly typed Clerk
and environment mocks. Ensure both provider values satisfy their respective
context types while preserving the existing test data and exposing future
provider shape changes to TypeScript.

Source: Coding guidelines

Comment thread packages/ui/src/elements/__tests__/Drawer.test.tsx
@alexcarpenter
alexcarpenter merged commit 2632d88 into main Jul 16, 2026
53 checks passed
@alexcarpenter
alexcarpenter deleted the esc-closes-checkout-drawer branch July 16, 2026 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants