Menu

Menus display a list of up to 15 options when launched by an action or UI element like an icon or button.

GitHubStorybook
yarn add @workday/canvas-kit-react
Sources
GitHubStorybook
Install
yarn add @workday/canvas-kit-react

Menu (Main) vs. Menu (Preview)

We recommend you use the Menu in the Main package (@workday/canvas-kit-react) documented here on this page. The Menu in the Preview package (@workday/canvas-kit-preview-react) has been marked for soft-deprecation in Canvas Kit v8 and will be removed in v9.

Menu (Preview) Documentation

Anatomy

Image of a Popup Menu sample showing different possible states.

  1. Container: Rectangular container that houses the icon and text for menu list items.
  2. Text: Text for indicating where the link leads to when the menu is clicked on. Text can overflow to the next line but avoid going over more than two lines of text.
  3. Icons: Icons are optional and can be positioned before or after the text.

Usage Guidance

  • Popup Menus can appear next to, in front of, above, or below the element that launched them, such as Dropdown Buttons, Dropdown Icons, icon only Primary/Secondary/Tertiary Button variants or by right-clicking a contextual item.
  • Popup Menus should overlap and visually look like they are in front of other UI elements. They should always be positioned within the viewable areas of the screen and be 8px away from the element that launched them.
  • Popup Menus should always contain a list of menu selections, which are options users can choose from. The list should be scannable, kept as concise as possible, and written in title case instead of sentences.
  • Consider how important each option is. The list of options should be sorted in a logical order, such as alphabetical, chronological, order of importance, and so on.

When to Use

  • In most cases, as with Overflow Menus, where there aren't enough space on screen to show all the actions, there could be between 1-7 items to choose from. However, there shouldn't be more than 15 items listed at once on a single Popup Menu.
  • When users must make a single selection from the list of options.

When to Use Something Else

  • Consider using a Switch if the only options are yes or no.
  • For a list between 2 to 7 predefined options, consider using a Radio input to select one option or Checkboxes to select multiple options. Radio and Checkbox groups display all options upfront and do not require the user to interact with the input to view the list of options.
  • Use a Prompt when the number of list items is large or unknown. Prompts have search capabilities and folders which provide users with the means to browse options. Prompts can be configured to support single or multi-select.

Examples

Basic Example

Menu is typically triggered by an action such as pressing a button. The Menu comes with a Target subcomponent and a Popup.

Selected:

Menu will automatically focus on the cursor item (first item by default). The Menu uses a menu model which composes a list model and a popup model and sets up accessibility features for you.

Menu follows the Actions Menu pattern using roving tabindex. Below is table of supported keyboard shortcuts and associated actions.

KeyAction
Enter or SpaceActivates the menu item and then closes the menu
EscapeCloses the menu
Up ArrowMoves focus to the previous menu item – if focus is on first menu item, it moves focus to the last menu item
Down ArrowMoves focus to the next menu item – if focus is on last menu item, it moves focus to the first menu item
HomeMoves focus to the first menu item
EndMoves focus to the last menu item

Context Menu

Selected:

Icons

Selected:

Component API

Usage

Menu is a combination of as popup and a list. It usually has some type of target element that expands/collapses the menu and a menu role and and several menuitem roles. Focus is managed uses roving tabindex for maximum compatibility. A Menu can have two modes: single and multiple. This mode determines both how many items can be selected as well as the default behavior when a menuitem is clicked. For the single mode, selecting a menuitem will select and close the menu. For the multiple mode, clicking a menuitem will toggle selection and will not close the menu.

Props

NameTypeDefaultDescription
children*ReactNodeThe contents of the Menu. Can be `Menu` children or any valid elements.
mode"multiple" | "single" | undefinedDetermines the default selection manager used as well as if the menu closes when an item is selected
returnFocusRefRefObject<any> | undefinedOptional reference to an element that should receive focus when a popup is hidden. If left blank, focus will return to the `targetRef`
initialFocusRefRefObject<any> | undefinedOptional reference to an element that should receive focus when a popup is shown. If left blank, focus will be moved to the first focusable element inside the popup.
idstring | undefined
initialVisibilityVisibility | undefined
initialSelectedIdsSelectedIds | undefined
initialUnselectedIdsstring[] | undefined
selectionSelectionManager | undefined
initialCursorIdstring | undefined
columnCountnumber | undefined
navigationNavigationManager | undefined
getId((item: any) => string) | undefined
getTextValue((item: any) => string) | undefined
nonInteractiveIdsstring[] | undefined
orientationOrientation | undefined
defaultItemHeightnumber | undefined
shouldVirtualizeboolean | undefined
itemsany[] | undefined
model{ state: { stackRef: RefObject<HTMLDivElement>; targetRef: RefObject<HTMLButtonElement>; initialFocusRef: RefObject<any> | undefined; ... 16 more ...; mode: "multiple" | "single"; }; events: { ...; }; selection: SelectionManager; navigation: NavigationManager; getId: (item: any) => string; } | undefinedOptional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context.
elemPropsHook(<TProps>(model: { state: { stackRef: RefObject<HTMLDivElement>; targetRef: RefObject<HTMLButtonElement>; initialFocusRef: RefObject<any> | undefined; ... 16 more ...; mode: "multiple" | "single"; }; events: { ...; }; selection: SelectionManager; navigation: NavigationManager; getId: (item: any) => string; }, elemPr...Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one.

Usage

Like all compound components, the Menu component is a container component. It is responsible for putting a MenuModel in the React context for all the menu subcomponents. You can either pass this component model config or a "hoisted" model.

With model config:

<Menu mode="multiple">{/* Child components */}</Menu>

With a hoisted model:

const model = useMenuModel({
mode: 'multiple',
});
// we now have access to `model.state` and `model.events`
<Menu model={model}>{/* Child components */}</Menu>;

Usage

Menu.Target is similar to all Popup.Target types. The component only provides behavior and no styling. The as prop is used to determine which component is rendered. This component should forward the ref and apply any additional props directly to an element. The default as is a SecondaryButton. Any Canvas Kit component should work with an as.

An example changing to a PrimaryButton

<Menu.Target as={PrimaryButton}>Primary Button Text</Menu.Target>

This element will apply aria-haspopup and aria-expanded to inform screen readers there's a popup associated with the element.

Props

Undocumented props are spread to the element provided by the as which is a SecondaryButton by default.

NameTypeDefaultDescription
refReact.Ref<any>Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If `as` is set to an element, it will be that element. If `as` is a component, the reference will be to that component (or element if the component uses `React.forwardRef`).
model{ state: { stackRef: RefObject<HTMLDivElement>; targetRef: RefObject<HTMLButtonElement>; initialFocusRef: RefObject<any> | undefined; ... 16 more ...; mode: "multiple" | "single"; }; events: { ...; }; selection: SelectionManager; navigation: NavigationManager; getId: (item: any) => string; } | undefinedOptional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context.
elemPropsHook(<TProps>(model: { state: { stackRef: RefObject<HTMLDivElement>; targetRef: RefObject<HTMLButtonElement>; initialFocusRef: RefObject<any> | undefined; ... 16 more ...; mode: "multiple" | "single"; }; events: { ...; }; selection: SelectionManager; navigation: NavigationManager; getId: (item: any) => string; }, elemPr...Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one.
as"symbol" | "object" | "small" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | ... 156 more ... | React.ComponentType<any>Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using `React.forwardRef` and spread extra props to a root element.

Specifications

GivenWhenThen
the Basic story is rendered
    • it should pass axe checks
    the Basic story is rendered
      • it should have aria-haspopup set to true
      the Basic story is rendered
        • it should have aria-expanded set to false
        the Basic story is rendered
          • it should not show a menu
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • it should set aria-expanded to true
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • it should show menu
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • it should transfer focus to the first menu item
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN escape key is pressed
          • it should have aria-expanded set to false
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN escape key is pressed
          • it should not show a menu
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN escape key is pressed
          • return focus to the "Open Menu" button
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN down arrow key is pressed
          • it should transfer focus to the second item
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN down arrow key is pressed
          • AND THEN the enter key is pressed
          • it should have aria-expanded set to false
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN down arrow key is pressed
          • AND THEN the enter key is pressed
          • it should not show a menu
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN down arrow key is pressed
          • AND THEN the enter key is pressed
          • it should select the second item
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN the second item is clicked
          • it should have aria-expanded set to false
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN the second item is clicked
          • it should not show a menu
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN the second item is clicked
          • it should select the second item
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN the tab key is pressed
          • it should have aria-expanded set to false
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN the tab key is pressed
          • it should not show a menu
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN up arrow key is pressed
          • it should focus on the last option
          Source: Menu.spec.ts

          Accessibility Guidelines

          • When the Menu is displayed, the focus should move onto the first option in the Menu.
          • The Menu as a whole should only receive one focus stop on each list item.
          • Up and down arrows must be used to navigate the Menu.

          Content Guidelines

          • When writing Menu list items, refer to the Menus section of the Content Style Guide.

          Can't Find What You Need?

          Check out our FAQ section which may help you find the information you're looking for.

          FAQ Section