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

Menu

Menu is a combination of a 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 using 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.

<Menu>
<Menu.Target>Open</Menu.Target>
<Menu.Popper>
<Menu.Card>
<Menu.List>
<Menu.Item data-id="first">First Item</Menu.Item>
<Menu.Item data-id="second">Second Item</Menu.Item>
</Menu.List>
</Menu.Card>
</Menu.Popper>
</Menu>

Props

Props extend from . If a model is passed, props from MenuModelConfig are ignored.

NameTypeDescriptionDefault
childrenReactNode

The contents of the Menu. Can be Menu children or any valid elements.

model

Optional 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(
  model: ,
  elemProps: TProps
) => HTML Attributes

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.

Menu.Target

Menu.Target is similar to all 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 . Any Canvas Kit component should work with an as.

An example changing to a

<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

Props extend from . Changing the as prop will change the element interface.

NameTypeDescriptionDefault
childrenReactNode
asReact.ElementType

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.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

refReact.Ref

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

Optional 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(
  model: ,
  elemProps: TProps
) => HTML Attributes

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.

useMenuTarget

(
  unknown {    "kind": "unknown",    "value": "unknown",    "text": "useMenuTargetBase"   },
  
)

Menu.Card

The menu card is a non-semantic element used to give the dropdown menu its distinct visual cue that the dropdown menu is floating above other content. A menu card usually contains a menu list, but can also contain other elements like a header or footer.

Layout Component

Menu.Card supports all props from thelayout component.

Props

Props extend from div. Changing the as prop will change the element interface.

NameTypeDescriptionDefault
childrenReactNode

Children of the Card. Should contain a <Card.Body> and an optional <Card.Heading>

asReact.ElementType

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.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

div
refReact.Ref

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

Optional 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(
  model: ,
  elemProps: TProps
) => HTML Attributes

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.

useMenuCard

(
  model: ,
  elemProps: {},
  ref: React.Ref
) => {}

Menu.List

The menu list follows the Collections API. A list can either contain static items or a render prop and items to the model.

const MyComponent = () => {
const model = useMenuModel({
items: [
{ id: 'first', text: 'First Item' },
{ id: 'second', text: 'Second Item' },
]
})
return (
<Menu model={model}>
<Menu.List>
{(item) => <Menu.Item name={item.id}>{item.text}</Menu.Item>}
</Menu.List>
</Menu>
)
}

Layout Component

Menu.List supports all props from thelayout component.

Props

Props extend from div. Changing the as prop will change the element interface.

NameTypeDescriptionDefault
children ReactNode | ((item: T) => ReactNode)

The label text of the MenuList.

asReact.ElementType

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.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

div
refReact.Ref

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

Optional 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(
  model: ,
  elemProps: TProps
) => HTML Attributes

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.

useMenuList

(
  model: ,
  elemProps: {},
  ref: React.Ref
) => {
  role: string;
  aria-labelledby: string;
}

Menu.Item

A menu item has an optional name prop that identifies the item in the menu list and will be passed to the optional onSelect callback of the menu model. A menu item can contain any HTML. If more complex HTML is provided, consider

Props

Props extend from button. Changing the as prop will change the element interface.

NameTypeDescriptionDefault
indexnumber

Optionally pass index to menu item. This should be done if Menu.Item components were created via a Array::map function. This index will ensure keyboard navigation works even if items are inserted out of order.

childrenReactNode

The label text of the MenuItem.

data-idstring

The name of the menu item. This name will be used in the onSelect callback in the model. If this property is not provided, it will default to a string representation of the the zero-based index of the Tab when it was initialized.

aria-disabledboolean
asReact.ElementType

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.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

button
refReact.Ref

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

Optional 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(
  model: ,
  elemProps: TProps
) => HTML Attributes

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.

useMenuItem

(
  (
    model: ,
    elemProps: {
      data-id: string;
    },
    ref: React.Ref
  ) => {
    ref: (instance:  | null) => void;
    role: string;
    onClick:  ((event: SyntheticEvent) => void) | undefined;
  },
  ,
  ,
  
)

Menu.Divider

Props

Props extend from hr. Changing the as prop will change the element interface.

NameTypeDescriptionDefault
childrenReact.ReactNode
asReact.ElementType

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.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

hr
refReact.Ref

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).

Menu.TargetContext

A Menu.TargetContext is the same as a , except it adds a context event handler instead of a click handler to trigger context menus.

Props

Props extend from . Changing the as prop will change the element interface.

NameTypeDescriptionDefault
childrenReactNode
asReact.ElementType

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.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

refReact.Ref

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

Optional 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(
  model: ,
  elemProps: TProps
) => HTML Attributes

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.

useMenuTargetContext

(
  unknown {    "kind": "unknown",    "value": "unknown",    "text": "useMenuTargetBase"   },
  
)

Menu.Popper

The "Popper" of a menu. The popper will appear around the . It renders a div element that is portalled to the document.body which is controlled by the . The PopupStack is not part of React. This means no extra props given to this component will be forwarded to the div element, but the ref will be forwarded.

Props

Props extend from div. Changing the as prop will change the element interface.

NameTypeDescriptionDefault
anchorElement <Element> | Element | null

The reference element used to position the Popper. Popper content will try to follow the anchorElement if it moves and will reposition itself if there is no longer room in the window.

children ((props: {
    placement: ;
  }) => ReactNode)
| ReactNode

The content of the Popper. If a function is provided, it will be treated as a Render Prop and pass the placement chosen by PopperJS. This placement value is useful if your popup needs to animate and that animation depends on the direction of the content in relation to the anchorElement.

getAnchorClientRect() => 

When provided, this optional callback will be used to determine positioning for the Popper element instead of calling getBoundingClientRect on the anchorElement prop. Use this when you need complete control over positioning. When this prop is specified, it is safe to pass null into the anchorElement prop. If null is passed into the anchorElement prop, an owner will not be provided for the PopupStack.

openboolean

Determines if Popper content should be rendered. The content only exists in the DOM when open is true

true
placement

The placement of the Popper contents relative to the anchorElement. Accepts auto, top, right, bottom, or left. Each placement can also be modified using any of the following variations: -start or -end.

fallbackPlacements[]

Define fallback placements by providing a list of in array (in order of preference). The default preference is following the order of top, right, bottom, and left. Once the initial and opposite placements are not available, the fallback placements will be in use. Use an empty array to disable the fallback placements.

onPlacementChange(placement: ) => void

A callback function that will be called whenever PopperJS chooses a placement that is different from the provided placement preference. If a placement preference doesn't fit, PopperJS will choose a new one and call this callback.

popperOptions<PopperOptions>

The additional options passed to the Popper's popper.js instance.

portalboolean

If false, render the Popper within the DOM hierarchy of its parent. A non-portal Popper will constrained by the parent container overflows. If you set this to false, you may experience issues where you content gets cut off by scrollbars or overflow: hidden

true
popperInstanceRefRef

Reference to the PopperJS instance. Useful for making direct method calls on the popper instance like update.

asReact.ElementType

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.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

div
refReact.Ref

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

Optional 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(
  model: ,
  elemProps: TProps
) => HTML Attributes

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.

usePopupPopper

Adds the necessary props to a component. Used by the subcomponent.

(
  model: ,
  elemProps: {},
  ref: React.Ref
) => {
  open: boolean;
  anchorElement: <>;
  ref: (instance:  | null) => void;
  onPlacementChange: (placement: ) => void;
}

Model

useMenuModel

useMenuModel (config: ):

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
          • it should have aria-disabled=true
          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 fourth item is clicked
          • it should not close the menu
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN the fourth item is clicked
          • it should have aria-expanded set to true
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN the fourth item is clicked
          • it should not select the fourth 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
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN up arrow key is pressed
          • AND THEN the enter key is pressed
          • it should not close the menu
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN up arrow key is pressed
          • AND THEN the enter key is pressed
          • it should have aria-expanded set to true
          the Basic story is rendered
          • the "Open Menu" button is clicked
          • AND THEN up arrow key is pressed
          • AND THEN the enter key is pressed
          • it should not select the fourth item
          the MenuWithFallbackPlacements example is rendered
          • check the fallback placements the preferred placement is set to top
          • it should show the fallback placement: bottom
          the MenuWithFallbackPlacements example is rendered
          • check the fallback placements the preferred placement is set to right
          • it should show the fallback placement: left
          the MenuWithFallbackPlacements example is rendered
          • check the fallback placements the preferred placement is set to right
          • it should show the fallback placement: bottom
          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