Skip to content

Conversation

@pasqualevitiello
Copy link
Contributor

@pasqualevitiello pasqualevitiello commented Dec 19, 2025


Summary by cubic

Fixes CommandDialog not closing when pressing Esc and updates the footer hint to show Esc, aligning with CUI-100. Also closes the palette when navigating to a page.

  • Bug Fixes

    • Esc now closes the dialog; footer updated to Esc.
    • CommandMenu items use Next.js Link and close on navigate.
  • Refactors

    • Removed CommandInputContext and initialFocus; use autoFocus on the input.
    • Simplified Command defaults (inline, open); shortcuts now use in Command/Menu.

Written for commit 04ac29e. Summary will update automatically on new commits.

Fixes: #607

@vercel
Copy link

vercel bot commented Dec 19, 2025

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

Project Deployment Review Updated (UTC)
coss-com Ready Ready Preview, Comment Dec 20, 2025 11:09am
coss-com-ui Ready Ready Preview, Comment Dec 20, 2025 11:09am
1 Skipped Deployment
Project Deployment Review Updated (UTC)
coss-com-origin Skipped Skipped Dec 20, 2025 11:09am

@github-actions
Copy link

github-actions bot commented Dec 19, 2025

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "fix CommandDialog not closing on ESC". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 5 files

Prompt for AI agents (all 1 issue)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/ui/src/components/command.tsx">

<violation number="1" location="packages/ui/src/components/command.tsx:160">
P2: The conditional `{...(dialogOnOpenChange ? { open } : {})}` ignores the user-provided `open` prop when `Command` is used standalone (outside `CommandDialog`). This changes the default behavior since `open={true}` was previously always passed. Consider always passing the `open` prop to preserve backward compatibility.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

keepHighlight={keepHighlight}
open={open}
onOpenChange={shouldHandleEscape ? handleOpenChange : undefined}
{...(dialogOnOpenChange ? { open } : {})}
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 19, 2025

Choose a reason for hiding this comment

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

P2: The conditional {...(dialogOnOpenChange ? { open } : {})} ignores the user-provided open prop when Command is used standalone (outside CommandDialog). This changes the default behavior since open={true} was previously always passed. Consider always passing the open prop to preserve backward compatibility.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/ui/src/components/command.tsx, line 160:

<comment>The conditional `{...(dialogOnOpenChange ? { open } : {})}` ignores the user-provided `open` prop when `Command` is used standalone (outside `CommandDialog`). This changes the default behavior since `open={true}` was previously always passed. Consider always passing the `open` prop to preserve backward compatibility.</comment>

<file context>
@@ -100,13 +128,36 @@ function Command({
       keepHighlight={keepHighlight}
-      open={open}
+      onOpenChange={shouldHandleEscape ? handleOpenChange : undefined}
+      {...(dialogOnOpenChange ? { open } : {})}
       {...props}
     /&gt;
</file context>
Suggested change
{...(dialogOnOpenChange ? { open } : {})}
open={open}

✅ Addressed in 9a12843

@pasqualevitiello pasqualevitiello changed the title fix CommandDialog not closing on ESC fix: CommandDialog not closing on ESC Dec 19, 2025
Copy link

@aarongarciah aarongarciah left a comment

Choose a reason for hiding this comment

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

Seems to work fine

onOpenChange,
...props
}: CommandDialogPrimitive.Root.Props) {
const handleClose = React.useCallback(

Choose a reason for hiding this comment

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

Is handleClose necessary? Is there a need to override the eventDetails parameter?

keepHighlight={keepHighlight}
open={open}
onOpenChange={shouldHandleEscape ? handleOpenChange : undefined}
{...(dialogOnOpenChange ? { open } : {})}

Choose a reason for hiding this comment

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

I find this easier to read.

Suggested change
{...(dialogOnOpenChange ? { open } : {})}
open={dialogOnOpenChange ? open : undefined}

className,
)}
data-slot="command-dialog-popup"
initialFocus={inputRef}

Choose a reason for hiding this comment

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

Not directly related to this PR but, is this ref really needed? Without it, the command input is still focused when the dialog opens. Is it to prevent the focus going anywhere else if the user includes other interactive element before the input inside the dialog? Could autoFocus work in that case? autoFocus would be problematic when using Command standalone without a dialog, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aarongarciah I introduced it to make the input autofocus on touch devices, because it's not working by default. Do you think I should use autoFocus instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tbh I don't think we'd need a standalone Command

onOpenChange,
...props
}: React.ComponentProps<typeof Autocomplete>) {
const { dialogOnOpenChange } = React.useContext(CommandContext);

Choose a reason for hiding this comment

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

There's an alternative solution to avoid this piece of context. It's a bit of a hack but simplifies the implementation. The idea is to use a hidden Dialog close button and click it programatically. It's hacky because afaik the Command component can be used standalone without a dialog wrapping it. Just leaving this as an alternative.

const closeRef = React.useRef<HTMLButtonElement>(null);

const handleOpenChange = React.useCallback<
  NonNullable<typeof onOpenChange>
>(
  (nextOpen, eventDetails) => {
    if (eventDetails.reason === "escape-key") {
      closeRef.current?.click();
    }
    onOpenChange?.(nextOpen, eventDetails);
  },
  [onOpenChange],
);

// ...

<>
  <Autocomplete
    ...
  />
  {/* Hidden close button for programmatic closing on Escape */}
  <CommandDialogPrimitive.Close ref={closeRef} hidden />
</>

{...props}
>
<CommandInputContext.Provider value={{ inputRef }}>
<CommandContext.Provider value={{ ...context, inputRef }}>

Choose a reason for hiding this comment

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

I'm not used to see contexts being used like this i.e. nest them to later append something to the value. Seems like there could be two different contexts? One for the dialog and another one for the input. Is there a reason to join them under the same context?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aarongarciah Not sure why I overcomplicated it so much :D Now I have a much cleaner component which seems to be working properly on both touch and desktop. I think this component should be designed to be always used inside a dialog, and not as "standalone" Command. That said, I still have a concern: with this implementation, the CommandDialog should always be used as a controlled component, otherwise you can't close the dialog on item click. Do you think it should work like that?

Copy link

@aarongarciah aarongarciah Dec 22, 2025

Choose a reason for hiding this comment

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

Much nicer, yeah! You could use the hidden Dialog.Close hack to achieve an uncontrolled component. AFAIK it's the only way of making it uncontrolled since there's no access to Base UI components' internal store/context currently.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Precious piece of advice, thank you!

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 10 files

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.

CommandDialog should close when pressing Esc

3 participants