Passer au contenu principal

CommandMenu

Implemented

Portal command palette with keyboard navigation and shortcut registration. Real implementation; the organisms/commandmenu stories twin was removed per ADR-035.

Categorymolecules
Sourcecomponents/molecules/CommandMenu/CommandMenu.tsx
AI HintUse for atomic UI elements. Compose into molecules for complex interactions.

Command palette with search.

CommandMenu

Feedback & Overlay — Family 4

Command palette with search.

Usage

CommandMenu is a controlled component — pair open with a piece of local state and wire onOpenChange so Escape and the backdrop dismiss it. Keyboard navigation (arrows, Enter, Escape) and focus management are built in.

import { CommandMenu, Button } from '@epasslive/design-system'

const [open, setOpen] = useState(false)

<Button variant="primary" onClick={() => setOpen(true)}>Open</Button>
<CommandMenu
  open={open}
  onOpenChange={setOpen}
  groups={[
    {
      id: 'navigation',
      label: 'Navigation',
      items: [{ id: 'home', label: 'Go to home', onSelect: () => setOpen(false) }],
    },
  ]}
/>

i18n and focus hooks

Localize the palette chrome with labels and the filter input with inputAriaLabel / inputPlaceholder. Use onFocusTrap / onRestoreFocus to save and restore the trigger element’s focus across the open/close cycle (e.g. when opening from a global ⌘K shortcut with no trigger button).

const triggerRef = useRef<HTMLButtonElement | null>(null)

<CommandMenu
  open={open}
  onOpenChange={setOpen}
  onFocusTrap={() => { triggerRef.current = document.activeElement as HTMLButtonElement | null }}
  onRestoreFocus={() => { triggerRef.current?.focus() }}
  labels={{ openMenu: 'Menu de commandes', noResults: 'Aucun résultat' }}
  inputAriaLabel="Rechercher une commande"
  inputPlaceholder="Tapez une commande…"
  groups={groups}
/>

When to use CommandMenu vs SearchSelect vs Menu

Use CommandMenu for global, power-user actions — a ⌘K-style palette aggregating navigation shortcuts and commands across the whole app, opened from anywhere. Use SearchSelect when the user must pick one option within a form from a searchable list (its value feeds form state). Use Menu for short, contextual action lists anchored to a trigger in the page. Rule of thumb: form field → SearchSelect; anchored context actions → Menu; global palette → CommandMenu.

Props

Prop
Type
Default
Description
open*
boolean
—

Controls visibility of the palette.

onOpenChange*
(open: boolean) => void
—

Called on Escape, backdrop click, or after selecting an item.

groups*
CommandMenuGroup[]
—

Grouped list of commands rendered in the palette.

placeholder
string
'Type a command…'

Search-input placeholder text.

inputPlaceholder
string
—

Explicit placeholder alias for the filter input (falls back to `placeholder`).

emptyMessage
string
'No results'

Rendered when the filtered list is empty.

labels
CommandMenuLabels
—

Localizable chrome strings — openMenu (dialog name), noResults, closeLabel (backdrop button), listLabel (listbox).

inputAriaLabel
string
'Search commands'

Accessible name for the filter input (aria-label).

onFocusTrap
() => void
—

Fired when the dialog opens — save document.activeElement before focus moves to the input.

onRestoreFocus
() => void
—

Fired when the dialog closes — restore the saved focus target.

id
string
—

Stable id prefix for the dialog and list elements (defaults to `cmd-<useId>`).

Props

Preview

CommandMenu

Prop
Type
Required
Default
Description
open
boolean; onOpenChange: (open: boolean) => void; groups: CommandMenuGroup[]
Required
—

Whether the element is open/expanded.

placeholder
string; emptyMessage?: string; id?: string
Optional
'Type a command…'

Placeholder text shown when the value is empty.

inputAriaLabel
string
Optional
'Search commands'

Accessible name for the filter input (§126 Wave 3 #6) — previously hardcoded.

inputPlaceholder
string
Optional
(placeholder)

Placeholder for the filter input — explicit alias of placeholder.

onFocusTrap
() => void
Optional
—

Fired when the dialog opens and focus moves into the input — wrapper hook for saving document.activeElement.

onRestoreFocus
() => void
Optional
—

Fired when the dialog closes — wrapper hook for restoring saved focus. Tab is trapped inside the dialog while open.

labels
CommandMenuLabels
Optional
—

Localizable chrome strings — dialog name (openMenu) + noResults (D1 merge 2 — CommandMenu replacement).

Quick use:<CommandMenu />

Usage

Import

tsxSnippet
import { CommandMenu } from "@epasslive/design-system"

Example 1

tsxSnippet
<CommandMenu />

Anti-patterns

— Do not compose atoms directly — use molecules instead

— Avoid inline styles on atoms

Design System v4.1.0 | ePassLive