MenuList

A list of commands, rendered as a semantic <ul role="menu">.

A list of commands, rendered as a semantic <ul role="menu">. It owns the keyboard: the arrow keys, Home and End move focus between its rows, skipping disabled ones, and typing jumps to a row by its label. Its rows are MenuItems, which inside a MenuList render as menu rows (menuitem, menuitemradio, menuitemcheckbox) rather than Select options, and MenuDividers between groups.

A MenuList renders in place - a menu embedded in a panel or a sidebar. It moves focus between its rows, but doesn't move focus into itself unless shouldAutoFocus is set.

import { MenuDivider, MenuList } from '@soroush.tech/design-system/Menu'
import { MenuItem } from '@soroush.tech/design-system/MenuItem'

;<MenuList aria-label="Branch" onSelect={(value) => run(value)}>
  <MenuItem value="new" shortcut="Control+B">
    New branch...
  </MenuItem>
  <MenuItem value="checkout" disabled note="Coming later">
    Checkout tag or revision...
  </MenuItem>
  <MenuDivider />
  <MenuItem value="main" role="menuitemradio" selected>
    main
  </MenuItem>
  <MenuItem value="feat/menu" role="menuitemradio">
    feat/menu
  </MenuItem>
</MenuList>

Demos

For usage examples, visit the component demo page: Menu

Import

import { MenuList } from '@soroush.tech/design-system/Menu'

Props

PropTypeDefaultDescription
childrenReactNode-MenuItem rows and MenuDividers.
onSelect(value: string | number) => void-Fired with a row's value when a row that has one is chosen.
shouldWrapbooleantrueCarry the arrow keys past the last row to the first, and back.
shouldAutoFocusbooleanfalseFocus the checked row, or the first enabled one, on mount; the list itself when it has none.
colorkeyof Theme['palette']-Accent color for every row - its focus shading. A row's own color wins.
textColorkeyof Theme['text']-Base text color for every row. A row's own textColor wins.
sizekeyof Theme['sizes']-Density token for every row. A row's own size wins.
denseboolean-Compact vertical padding for every row. A row's own dense wins.
onKeyDown(event) => void-Fired for every key pressed in the list, after its own handling.
aria-labelstring-Names the menu.
aria-labelledbystring-Names the menu after another element.
idstring-DOM id.
classNamestring-Class on the list.

Plus the styled-system space (m, p, ...) and layout (width, maxHeight, ...) props. The list has 0.5 vertical padding and no margin by default.

Keyboard

KeyDoes
ArrowDown / ArrowUpFocus the next / previous enabled row, wrapping unless shouldWrap is off
Home / EndFocus the first / last enabled row
A printable keyFocus the next row whose label starts with it; keys typed within 500ms build a prefix
Enter / SpaceChoose the focused row

Notes

  • Rows are found in the DOM by their nearest role="menu", so a list nested inside another (a submenu) keeps its rows and its keys to itself.
  • The pointer moves focus as it goes over a row, so the row under the pointer and the row the keys move are always the same one, and one row is highlighted at a time.
  • Type-ahead matches a row's label only - not its icon, shortcut or note.

Source code

If this page does not answer your question, have a look at the implementation of the component for more detail.