Menu
An accessible dropdown menu for the common dropdown menu button design pattern. Menu uses roving tabIndex for focus management.
React
Hover Design System exports the following components for rendering menus:
import { Menu, MenuItem, MenuItemOption, MenuGroup, MenuOptionGroup, MenuDivider,} from '@hoverinc/design-system-react-web';
Menu
– The wrapper component provides context, state, and focus management, requires atrigger
prop.MenuItem
– The trigger that handles menu selection. Must be a direct child of aMenuList
.MenuGroup
– A wrapper to group related menu items.MenuDivider
– A visual separator for menu items and groups.MenuOptionGroup
– A wrapper for checkable menu items (radio and checkbox).MenuItemOption
– The checkable menu item, to be used withMenuOptionGroup
.
Usage
Trigger
The trigger
prop accepts any system component. If you're passing a custom
component that composes a system component, it must accept a ref
.
Menu container styles
Use menuListProps
to pass props to the div
that contains MenuItem
s. This
container composes Box
so you can pass all Box
props to change its style.
Accessing the internal state
To access the internal state of the Menu
, use a function as children
or
trigger
(commonly known as a render prop). You'll get access to the internal
state isOpen
and method onClose
.
Letter navigation
When focus is on the trigger
or within the list and you type a letter key, a
search begins. Focus will move to the first MenuItem
that starts with the
letter you typed.
Example with images
Adding icons and commands
You can add icon to each MenuItem
by passing the icon
prop. To add a
commands (or hotkeys) to menu items, you can use the command
prop.
Lazily mounting items
By default, the Menu
component renders all children of MenuList
to the DOM,
meaning that invisible menu items are still rendered but are hidden by styles.
If you want to defer rendering of each children of the menu list until that menu
is open, you can use the isLazy
prop. This is useful if your Menu
needs to
be extra performant, or make network calls on mount that should only happen when
the component is displayed.
Rendering the Menu in a Portal
To render menus in a Portal
, pass the inPortal
prop.
Groups
To group related MenuItem
s, use the MenuGroup
component and pass it a
title
for the group name.
Items as links
To render a MenuItem
as a link, use the attributes as
and href
.
Option groups
You can compose a menu for table headers to help with sorting and filtering
options. Use the MenuOptionGroup
and MenuItemOption
components.
<Menu inPortal trigger={<Button iconBefore={iconFilter}>Filter</Button>}> <MenuOptionGroup defaultValue="asc" title="Order" type="radio"> <MenuItemOption value="asc">Ascending</MenuItemOption> <MenuItemOption value="desc">Descending</MenuItemOption> </MenuOptionGroup> <MenuDivider /> <MenuOptionGroup title="Country" type="checkbox"> <MenuItemOption value="email">Email</MenuItemOption> <MenuItemOption value="phone">Phone</MenuItemOption> <MenuItemOption value="country">Country</MenuItemOption> </MenuOptionGroup></Menu>
With arrow
Set hasArrow
to display an arrow pointing from the menu to the trigger.
Accessibility
Keyboard Interaction
Key | Action |
---|---|
Enter or Space | When MenuButton receives focus, opens the menu and places focus on the first menu item. |
ArrowDown | When MenuButton receives focus, opens the menu and moves focus to the first menu item. |
ArrowUp | When MenuButton receives focus, opens the menu and moves focus to the last menu item. |
Escape | When the menu is open, closes the menu and sets focus to the MenuButton . |
Tab | no effect |
Home | When the menu is open, moves focus to the first item. |
End | When the menu is open, moves focus to the last item. |
A-Z or a-z | When the menu is open, moves focus to the next menu item with a label that starts with the typed character if such an menu item exists. |
ARIA roles
For MenuButton
:
role
is set tobutton
.aria-haspopup
is set tomenu
.- When the menu is displayed,
aria-expanded
is set totrue
. aria-controls
is set to theid
of theMenuList
.
For MenuList
:
role
is set tomenu
.aria-orientation
is set tovertical
.
For MenuItem
:
role
is set tomenuitem
.- Gets one of these roles
menuitem
/menuitemradio
/menuitemcheckbox
.
React Native
Menu is built with Tamagui and provides a dropdown interface with trigger, dropdown, items, and dividers for iOS and Android platforms.
Usage
import { iconChevronDown, iconTrash } from '@hoverinc/icons/native';import React, { useState } from 'react';import { Heading, Icon, Menu, XStack,} from '@hoverinc/design-system-react-native';const App = () => { const [open, setOpen] = useState(false); return ( <Menu onOpenChange={() => setOpen(!open)} open={open} placement="bottom-end" > <Menu.Trigger> <XStack alignItems="center" gap="$100" pressStyle={{ opacity: 0.5 }}> <Heading size="xs">Homeowner</Heading> <Icon icon={iconChevronDown} size="tiny" /> </XStack> </Menu.Trigger> <Menu.Dropdown> <Menu.Item isDisabled>Invited to scan</Menu.Item> <Menu.Divider /> <Menu.Item isSelected>Selected Item</Menu.Item> <Menu.Item onPress={() => setOpen(false)}>Send reminder</Menu.Item> <Menu.Item onPress={() => setOpen(false)}>Copy link</Menu.Item> <Menu.Item color="$dangerSecondary" iconAfter={iconTrash} onPress={() => setOpen(false)} > Sign Out </Menu.Item> </Menu.Dropdown> </Menu> );};
Components
The Menu component exports the following sub-components:
Menu
– The main wrapper component that provides context and state managementMenu.Trigger
– The trigger element that opens the menuMenu.Dropdown
– The container for menu itemsMenu.Item
– Individual menu items with support for selection, disabled state, icons, and custom colorsMenu.Divider
– Visual separator between menu items
Menu Item States
Menu items support several states and configurations:
// Basic menu item<Menu.Item>Basic Item</Menu.Item>// Selected state<Menu.Item isSelected>Selected Item</Menu.Item>// Disabled state<Menu.Item isDisabled>Disabled Item</Menu.Item>// With icon after<Menu.Item iconAfter={iconChevronRight}>Item with Icon</Menu.Item>// With custom color<Menu.Item color="$dangerSecondary">Danger Item</Menu.Item>
Placement
The menu can be positioned relative to its trigger using the placement
prop:
<Menu placement="bottom-start">...</Menu><Menu placement="top-end">...</Menu><Menu placement="left">...</Menu><Menu placement="right">...</Menu>
Controlled vs Uncontrolled
The Menu component supports both controlled and uncontrolled usage:
// Controlledconst [open, setOpen] = useState(false);<Menu open={open} onOpenChange={setOpen}>...</Menu>// Uncontrolled<Menu defaultOpen={false}>...</Menu>