Action Menu
This versatile menu can be used in combination with buttons which may offer multiple contextual actions (e.g. save, export). The menu is typically as wide as the longest item in the menu and at least as wide as the button that triggers it.
To save space and group options, use an Action menu in combination with a button to render a list of actions. You can also use an Action menu to show a list of different options for the same action. For example, an export button can open an Action menu with a list of file formats.
The trigger for a Action menu is another component, usually a Button.
Example
<ActionMenu items={[ { label: 'Accept', onClick: () => {}, iconName: 'Check' }, { label: 'Decline', onClick: () => {}, iconName: 'Cross' }, { type: 'divider' }, { label: 'Abort', onClick: () => {} } ]} > {/* The trigger can be anything. We use a button here */} <Button label="Click to open" /> </ActionMenu>
If ESLint's react/jsx-key rule is active make sure to set a key on the menu items!
Usage Rules
Do:
- Group menu items that belong to a context inside an Action menu
- Use short and clear labels. Keep it between one to three words
- Use a line to separate destructive items (e.g. delete) from the others or to group similar items
Don't:
- Use an ActionMenu if it would only contain one item
- Mix menu items with and without icons in the same group
- Mix menu items with and without descriptions in the same group
Variants
Data-testid
You can use the dataTestId prop to access data-testid for the action menu items and submenu items.
<ActionMenu items={[ { label: 'Accept', onClick: () => {}, iconName: 'Check', dataTestId: 'data-test-id-accept' }, { label: 'Decline', onClick: () => {}, iconName: 'Cross', dataTestId: 'data-test-id-decline' }, { type: 'submenu', label: 'Export', iconName: 'Download', submenuItems: [ { label: 'PNG', onClick: () => {}, dataTestId: 'data-test-id-png' }, { label: 'PDF', onClick: () => {}, dataTestId: 'data-test-id-pdf' }, { label: 'CSV', onClick: () => {}, dataTestId: 'data-test-id-csv' } ] } ]} > {/* The trigger can be anything. We use a button here */} <Button label="Click to open" /> </ActionMenu>
Description
Menu items can have a description with further information about the action
<ActionMenu items={[ { label: 'Save', description: 'Save the file', onClick: () => {} }, { label: 'Delete', description: 'Delete the file', onClick: () => {} } ]} > <Button label="Options" /> </ActionMenu>
Disabled
You can disable Menu item by setting the disabled property to true.
<ActionMenu items={[ { label: 'Duplicate', onClick: () => {}, iconName: 'Copy' }, { label: 'Edit', onClick: () => {}, iconName: 'Edit', disabled: true }, { label: 'Export', onClick: () => {}, iconName: 'Download' }, { label: 'Delete', onClick: () => {}, iconName: 'Trash' } ]} > <Button label="Options" /> </ActionMenu>
Grouping
The item with divider type can be used to visually group Menu items.
You can specify a label for the group by providing the label property. This will visually assign the next following menu items to a group with a provided label.
<ActionMenu items={[ { type: 'divider', label: 'Options' }, { label: 'Duplicate', onClick: () => {}, iconName: 'Copy' }, { label: 'Edit', onClick: () => {}, iconName: 'Edit' }, { label: 'Delete', onClick: () => {}, iconName: 'Trash' }, { type: 'divider', label: 'Export as' }, { label: 'PNG', onClick: () => {} }, { label: 'PDF', onClick: () => {} }, { label: 'CSV', onClick: () => {} } ]} > <Button label="Click to open" /> </ActionMenu>
Icons
Menu items can have an Icon next to label to emphasize an action. All available icons can be found here.
<ActionMenu items={[ { label: 'Save', description: 'Save the file', onClick: () => {}, iconName: 'Check' }, { label: 'Delete', description: 'Delete the file', onClick: () => {}, iconName: 'Cross' } ]} > <Button label="Options" /> </ActionMenu>
Item Checkbox
The item with checkbox type can be used as a checkbox. You can use the property isSelected to set the checked state of item.
function MyActionMenu() { const initialItems = [ { type: 'divider', label: 'Languages' }, { label: 'English', type: 'checkbox', description: 'Default language', isSelected: true, disabled: true }, { label: 'German', type: 'checkbox', isSelected: true }, { label: 'Spain', type: 'checkbox' }, { type: 'divider'}, { label: 'Japanese', type: 'checkbox' }, { label: 'Chinese', type: 'checkbox' } ].map((item, index) =>{ return {...item, onClick: () => handleClick(index)} }) const [items, setItems] = useState(initialItems); const [isOpen, setIsOpen] = useState(false); const handleClick = (index) => { items[index] = {...items[index], isSelected: !items[index].isSelected }; console.log('index', index, items); setItems([...items]); setIsOpen(true) } return ( <ActionMenu items={items} placement="right-start" isHoverOpen isOpen={isOpen} setIsOpen={setIsOpen} offset={[0, 0]} > <IconButton iconName="Globe" aria-label="languages" /> </ActionMenu> ) }
Item Link
The item with link type can be used as a link. You can also specify a href with to property. An <a> with a href attribute will be rendered by default but this can be changed using the as property.
<ActionMenu items={[ { label: 'Accept', onClick: () => {} }, { label: 'Abort', onClick: () => {} }, { type: 'divider' }, { label: 'Goto Adjust', type: 'link', to: 'https://adjust.com' } /* override default <a> with Link from react-router-dom { label: 'Goto Intro', type: 'link', as: Link, to: '/foo' }, */ ]} > <Button label="Click to open" /> </ActionMenu>
Offset
The prop offset lets you displace a menu element from its reference element. The default value is [0, 4].
<ActionMenu items={[ { label: 'Accept', onClick: () => {}, iconName: 'Check' }, { label: 'Decline', onClick: () => {}, iconName: 'Cross' }, { label: 'Add', onClick: () => {}, iconName: 'Plus' }, ]} placement="right-end" offset={[0, 12]} > <Button label="Click to open" /> </ActionMenu>
Open trigger
As the default, Action menu is opened by clicking. But it is also opened by hovering with prop isHoverOpen.
<div style={{display: 'flex', gap: '24px'}}> <ActionMenu items={[ { type: 'divider', label: 'Options' }, { label: 'Mail', onClick: () => {}, iconName: 'Mail' }, { label: 'Duplicate', onClick: () => {}, iconName: 'Copy', disabled: true }, { label: 'Edit', onClick: () => {}, iconName: 'Edit', disabled: true }, { label: 'Delete', onClick: () => {}, iconName: 'Trash' } ]} > <Button label="Click to open" /> </ActionMenu> <ActionMenu items={[ { type: 'divider', label: 'Options' }, { label: 'Mail', onClick: () => {}, iconName: 'Mail' }, { label: 'Duplicate', onClick: () => {}, iconName: 'Copy', }, { label: 'Edit', onClick: () => {}, iconName: 'Edit', }, { label: 'Delete', onClick: () => {}, iconName: 'Trash', disabled: true } ]} isHoverOpen > <Button label="Hover to open" /> </ActionMenu> </div>
Open state
- The implementation of
ToastProviderandToastContexthas been moved tomfe-shellandmfe-shared-context, respectively. - Please import
ToastContextfrommfe-shared-context. We have wrapped all the mfe apps withToastProviderat root level inmfe-shell. - All
mfeapps are wrapped withToastProviderfrommfe-shellso you don't need to wrap your app one more time.
You can update the open state manually by using the isOpen and setIsOpen props.
function ActionMenuFnc() { const MyActionMenu = () => { const [isOpen, setIsOpen] = useState(false); const { showToast } = useContext(ToastContext); return ( <ActionMenu items={[ { label: 'Accept', onClick: () => { setIsOpen(false); showToast({ title: 'Action Menu is closed!' }); } }, { label: 'Decline', onClick: () => { setIsOpen(true); showToast({ title: 'Action Menu is opening!' }); } } ]} isOpen={isOpen} setIsOpen={setIsOpen} > {/* The trigger can be anything. We use a button here */} <Button label="Click to open" onClick={() => setIsOpen(true)} /> </ActionMenu> ); }; return ( <ToastProvider> <MyActionMenu /> </ToastProvider> ); }
Placement
We also have support for placing the menu, can be accessed using placement prop, default to bottom-start.
<ActionMenu items={[ { label: 'Accept', onClick: () => {}, iconName: 'Check' }, { label: 'Decline', onClick: () => {}, iconName: 'Cross' }, { type: 'divider' }, { label: 'Abort', onClick: () => {} } ]} placement="top-start" > <Button label="Open" /> </ActionMenu>
Submenu
To group related menu items a submenu can be provided.
Top level menu items have an additional submenuItems property which takes a list of menu items.
Please note that there can only be one level of submenus.
<ActionMenu items={[ { label: 'Duplicate', onClick: () => {}, iconName: 'Copy' }, { label: 'Edit', onClick: () => {}, iconName: 'Edit' }, { type: 'submenu', label: 'Export', iconName: 'Download', submenuItems: [ { label: 'PNG', onClick: () => {} }, { label: 'PDF', onClick: () => {} }, { label: 'CSV', onClick: () => {} } ] }, { label: 'Delete', onClick: () => {}, iconName: 'Trash' } ]} > <Button label="Options" /> </ActionMenu>
Tooltip
A user can provide Tooltip to menuitem and set the position as well.
<ActionMenu items={[ { label: 'Duplicate', onClick: () => {}, iconName: 'Copy', disabled: true, tooltipContent: "Disabled Menu Tooltip" }, { label: 'Edit', onClick: () => {}, iconName: 'Edit', tooltipContent: <span>Enabled Menu Node Element Tooltip with different position</span>, tooltipPosition: 'left' }, { type: 'submenu', label: 'Export', iconName: 'Download', submenuItems: [ { label: 'PNG', onClick: () => {}, tooltipContent: <span>enabled custom position tooltip</span>, tooltipPosition: 'left' }, { label: 'PDF', onClick: () => {} }, { label: 'CSV', onClick: () => {}, tooltipContent: <span>disabled sub item</span>, disabled: true } ] }, { label: 'Delete', onClick: () => {}, iconName: 'Trash', tooltipContent: <span>Checkbox type tooltip</span>, position: 'left', } ]} > <Button label="Options" /> </ActionMenu>
Tooltip for ActionMenu with type checkbox
function MyActionMenu() { const initialItems = [ { type: 'divider', label: 'Languages' }, { label: 'English', type: 'checkbox', description: 'Default language', isSelected: true, disabled: true, tooltipContent: <span>Disabled Menu Node Element Tooltip</span>, }, { label: 'German', type: 'checkbox', isSelected: true }, { label: 'Spain', type: 'checkbox' }, { type: 'divider'}, { label: 'Japanese', type: 'checkbox', tooltipContent: <span>Enabled Menu Node Element Tooltip with different position</span>, tooltipPosition: 'left' }, { label: 'Chinese', type: 'checkbox' } ].map((item, index) =>{ return {...item, onClick: () => handleClick(index)} }) const [items, setItems] = useState(initialItems); const [isOpen, setIsOpen] = useState(false); const handleClick = (index) => { items[index] = {...items[index], isSelected: !items[index].isSelected }; console.log('index', index, items); setItems([...items]); setIsOpen(true) } return ( <ActionMenu items={items} placement="right-start" isHoverOpen isOpen={isOpen} setIsOpen={setIsOpen} offset={[0, 0]} > <Button label="Language Options" /> </ActionMenu> ) }
Props
ActionMenu
| Name | Type | Default |
|---|---|---|
items * Array of menu items. See the structure of the ItemType |
| — |
children * Trigger for the menu.
Ensure that the element looks like it can be clicked and that it's focusable (e.g. a Button) |
| — |
disabled if true menu will not open |
| false |
isOpen Open state, if true menu will open |
| — |
onShow The callback will be executed after the menu has opened |
| — |
onHide The callback will be executed after the menu has closed |
| — |
placement The menu placement relatively to the trigger element |
| — |
setIsOpen The callback that allows to set open state e.g. when clicking on trigger element |
| — |
isHoverOpen The prop that allows to open by hovering on the element |
| false |
offset Allows to displace a menu element from its reference element. |
| — |
css Add custom styles to this component. Use with caution. Learn more here: Link |
| — |
inputFieldCss |
| — |
data-{foo} Data attributes can be used by testing libraries to retrieve components or assert their existence |
| — |
| * - the prop is required. |
Structure of the MenuItemType interface
| Name | Type | Default |
|---|---|---|
formatMenuItem |
| — |
hoverDisabled Whether a different style should not be applied on hover |
| — |
iconName Name of the Icon that would appear on the right. Please refer to the full list of icons here: Link | "AddValue" | "Adjust" | "AIAgent" | "AppGrid" | "ArrowCircleLeft" | "ArrowCircleRight" | "ArrowDown"... | — |
label Label of the menu item |
| — |
description In case the label is not sufficient you can add a short description |
| — |
onClick This handler will be called if the menu item is pressed (either through click or keyboard events) |
| — |
disabled Use this prop if the item should not be interactive |
| false |
indentOnRight If set Check icon will be positioned on the right. Internal use only! |
| false |
dataTestId A test id to be used by testing libraries. It will be added as a "data-testid" attribute on the root of the menu item |
| — |
tooltipContent The content of the tooltip |
| — |
tooltipPosition The position of the tooltip |
| "top" |
type Dividing items show a separating line and can be used to form menu item groups.
submenu items can be used to display a submenu.
Note that these types of items cannot be clicked. |
| — |
| * - the prop is required. |
Structure of the SubmenuItemType interface
| Name | Type | Default |
|---|---|---|
itemIndex * |
| — |
hideSubmenu * |
| — |
submenuItems Array of submenu items |
| — |
iconName Name of the Icon that would appear on the right. Please refer to the full list of icons here: Link | "AddValue" | "Adjust" | "AIAgent" | "AppGrid" | "ArrowCircleLeft" | "ArrowCircleRight" | "ArrowDown"... | — |
label Label of the menu item |
| — |
description In case the label is not sufficient you can add a short description |
| — |
onClick This handler will be called if the menu item is pressed (either through click or keyboard events) |
| — |
disabled Use this prop if the item should not be interactive |
| false |
dataTestId A test id to be used by testing libraries. It will be added as a "data-testid" attribute on the root of the menu item |
| — |
tooltipContent The content of the tooltip |
| — |
tooltipPosition The position of the tooltip |
| — |
type Dividing items show a separating line and can be used to form menu item groups.
submenu items can be used to display a submenu.
Note that these types of items cannot be clicked. |
| — |
| * - the prop is required. |