MenuButton
The MenuButton component is a ToggleButton that, when toggled on, displays a Menu with options.
Guidelines
When to use menu buttons
MenuButton serves for accessing additional actions or settings associated with the button that triggers the menu.
Use the MenuButton component when the space is limited and you need to provide users with a set of related actions or options within a single button.
Avoid using MenuButton when there are only one or two actions to display. In that case, consider using separate buttons instead. Additionally, refrain from using MenuButton if the actions displayed are essential and require immediate visibility without additional interaction.
Specifications
MenuButton includes the following items:
- ToggleButton
A single button to display the menu. It can include an icon, a clear label, or both. - Menu
A selectable list of related actions or options that can optionally contain start icons.
Component limitations
The MenuButton will display a minimum of 2 actions or options. There is no limit on the number of visible menu items; a scrollbar will appear if the menu size exceeds manageable limits.
The Menu inside the MenuButton is set to a base size of @size-1600
(equivalent to 256px
in the default Codex theme). When space is constrained, this can shrink down to a minimum width of @size-800
(equivalent to 128px
in the default Codex theme).
Position of the menu
When the menu is presented in LTR direction, Bottom-Right is the default position, while Bottom-Left is the default position in RTL direction. As an alternative to these base positions, the following ones can also be used to fit the space or scroll in the screen:
- Bottom-Right
- Bottom-Left
- Top-Right
- Top-Left
- Right-Top
- Right-Bottom
- Left-Top
- Left-Bottom
Refer to the MenuButton component in Codex Figma.
Types
Since MenuButton uses a ToggleButton and a Menu, it can use any of their available properties, such as displaying text on the button or including icons in the menu.
Interaction states
The Menu appears once the ToggleButton is selected.
- Default
- Hover
- Active
- Focus
- Toggled-on focus
- Disabled
Best practices
Consider the following recommendations when working with menu buttons.
- Use MenuButton to display additional actions within a single button.
- Use the MenuButton as a selector for choosing an option. In such cases, use a Select instead.
- Enhance the visual representation of text by incorporating icons into menu items.
- Use thumbnails within the menus of MenuButton, as they could make the menu excessively large.
- Use the
color-destructive
in case an action within the Menu is destructive.
- Include more than one destructive action within the menu.
Keyboard navigation
Key | Function |
---|---|
Enter | If the focus is placed on the button, it opens and closes the menu. If the focus is placed in any of the options within the displayed menu, it activates that option. |
Space | If the focus is placed on the button, it opens and closes the menu. |
Down arrow / Up arrow | If the menu is displayed, it navigates through menu options. |
Esc | It closes the menu when it is open. |
Home | Optionally, it moves the focus to the first option. Optionally, in a single-select listbox, selection may also move with focus. Supporting this key is strongly recommended for lists with more than five options. |
End | Optionally, it moves the focus to the last option. Optionally, in a single-select listbox, selection may also move with focus. Supporting this key is strongly recommended for lists with more than five options. |
Demos
Configurable
- Edit Configuration
- View Phab Ticket
- Contact Owner
- Turn Instrument Off
- Delete Instrument
Name | Value |
---|---|
Props | |
disabled | |
Slots | |
default | |
View | |
Reading direction |
Basic usage
MenuButton has two required props: selected
and menuItems
. The selected
prop needs to be bound with v-model
.
Customize the content within the toggle button by using the default slot. You can pass in text and/or a Icon to the slot content.
- Edit
- Download
- Share
- Delete
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
>
More options
</cdx-menu-button>
</template>
<script>
import { defineComponent, ref } from 'vue';
import { CdxMenuButton } from '@wikimedia/codex';
const menuItems = [
{ label: 'Edit', value: 'edit' },
{ label: 'Download', value: 'download' },
{ label: 'Print', value: 'print', disabled: true },
{ label: 'Share', value: 'share' },
{ label: 'Delete', value: 'delete', action: 'destructive' }
];
export default defineComponent( {
name: 'MenuButtonBasic',
components: { CdxMenuButton },
setup() {
const selection = ref( null );
return {
selection,
menuItems
};
}
} );
</script>
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
>
More options
</cdx-menu-button>
</template>
<script>
const { defineComponent, ref } = require( 'vue' );
const { CdxMenuButton } = require( '@wikimedia/codex' );
const menuItems = [
{ label: 'Edit', value: 'edit' },
{ label: 'Download', value: 'download' },
{ label: 'Print', value: 'print', disabled: true },
{ label: 'Share', value: 'share' },
{ label: 'Delete', value: 'delete', action: 'destructive' }
];
module.exports = defineComponent( {
name: 'MenuButtonBasic',
components: { CdxMenuButton },
setup() {
const selection = ref( null );
return {
selection,
menuItems
};
}
} );
</script>
Icon-only
When using an icon-only toggle button, add an aria-label
.
To add an icon, insert the Icon component in the slot content. Refer to the Icon component and the overview of icons to learn more about using icons.
- Keyboard shortcuts
- Leave feedback
- Read the user guide
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
aria-label="Help options"
>
<cdx-icon :icon="cdxIconHelp" />
</cdx-menu-button>
</template>
<script>
import { defineComponent, ref } from 'vue';
import { CdxMenuButton, CdxIcon } from '@wikimedia/codex';
import { cdxIconHelp } from '@wikimedia/codex-icons';
const menuItems = [
{ label: 'Keyboard shortcuts', value: 'keyboard' },
{ label: 'Leave feedback', value: 'feedback' },
{ label: 'Read the user guide', value: 'read' }
];
export default defineComponent( {
name: 'MenuButtonWithIconOnly',
components: { CdxMenuButton, CdxIcon },
setup() {
const selection = ref( null );
return {
selection,
menuItems,
cdxIconHelp
};
}
} );
</script>
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
aria-label="Help options"
>
<cdx-icon :icon="cdxIconHelp"></cdx-icon>
</cdx-menu-button>
</template>
<script>
const { defineComponent, ref } = require( 'vue' );
const { CdxMenuButton, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconHelp } = require( './icons.json' );
const menuItems = [
{ label: 'Keyboard shortcuts', value: 'keyboard' },
{ label: 'Leave feedback', value: 'feedback' },
{ label: 'Read the user guide', value: 'read' }
];
module.exports = defineComponent( {
name: 'MenuButtonWithIconOnly',
components: { CdxMenuButton, CdxIcon },
setup() {
const selection = ref( null );
return {
selection,
menuItems,
cdxIconHelp
};
}
} );
</script>
With icon and text
- Keyboard shortcuts
- Leave feedback
- Read the user guide
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
>
<cdx-icon :icon="cdxIconHelp" />
Help
</cdx-menu-button>
</template>
<script>
import { defineComponent, ref } from 'vue';
import { CdxMenuButton, CdxIcon } from '@wikimedia/codex';
import { cdxIconHelp } from '@wikimedia/codex-icons';
const menuItems = [
{ label: 'Keyboard shortcuts', value: 'keyboard' },
{ label: 'Leave feedback', value: 'feedback' },
{ label: 'Read the user guide', value: 'read' }
];
export default defineComponent( {
name: 'MenuButtonWithIconAndText',
components: { CdxMenuButton, CdxIcon },
setup() {
const selection = ref( null );
return {
selection,
menuItems,
cdxIconHelp
};
}
} );
</script>
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
>
<cdx-icon :icon="cdxIconHelp"></cdx-icon>
Help
</cdx-menu-button>
</template>
<script>
const { defineComponent, ref } = require( 'vue' );
const { CdxMenuButton, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconHelp } = require( './icons.json' );
const menuItems = [
{ label: 'Keyboard shortcuts', value: 'keyboard' },
{ label: 'Leave feedback', value: 'feedback' },
{ label: 'Read the user guide', value: 'read' }
];
module.exports = defineComponent( {
name: 'MenuButtonWithIconAndText',
components: { CdxMenuButton, CdxIcon },
setup() {
const selection = ref( null );
return {
selection,
menuItems,
cdxIconHelp
};
}
} );
</script>
Menu items with icons
You can add icons to individual menu items by including the icon
property in the list of menuItems
. Be sure to import
or require
each icon, then add the icon name from this list of icons as the icon
property's value.
- Function call
- Literal String
- Reference
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
aria-label="Change input type"
>
<cdx-icon :icon="cdxIconEllipsis" />
</cdx-menu-button>
</template>
<script>
import { defineComponent, ref } from 'vue';
import { CdxMenuButton, CdxIcon } from '@wikimedia/codex';
import { cdxIconEllipsis, cdxIconFunction, cdxIconLiteral, cdxIconInstance } from '@wikimedia/codex-icons';
const menuItems = [
{ label: 'Function call', value: 'function call', icon: cdxIconFunction },
{ label: 'Literal String', value: 'literal string', icon: cdxIconLiteral },
{ label: 'Reference', value: 'reference', icon: cdxIconInstance }
];
export default defineComponent( {
name: 'MenuButtonAndMenuItemsWithIcons',
components: { CdxMenuButton, CdxIcon },
setup() {
const selection = ref( null );
return {
selection,
menuItems,
cdxIconEllipsis
};
}
} );
</script>
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
aria-label="Change input type"
>
<cdx-icon :icon="cdxIconEllipsis"></cdx-icon>
</cdx-menu-button>
</template>
<script>
const { defineComponent, ref } = require( 'vue' );
const { CdxMenuButton, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconEllipsis, cdxIconFunction, cdxIconLiteral, cdxIconInstance } = require( './icons.json' );
const menuItems = [
{ label: 'Function call', value: 'function call', icon: cdxIconFunction },
{ label: 'Literal String', value: 'literal string', icon: cdxIconLiteral },
{ label: 'Reference', value: 'reference', icon: cdxIconInstance }
];
module.exports = defineComponent( {
name: 'MenuButtonAndMenuItemsWithIcons',
components: { CdxMenuButton, CdxIcon },
setup() {
const selection = ref( null );
return {
selection,
menuItems,
cdxIconEllipsis
};
}
} );
</script>
Menu with configurable scroll
You can use the menuConfig
prop to set a visible menu item limit. Refer to the MenuConfig
type for more configuration options.
- New file
- Open file
- Save file
- Edit
- Cut
- Copy
- Paste
- Download as PDF
- Printable version
- Share
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
:menu-config="menuConfig"
aria-label="More actions"
>
<cdx-icon :icon="cdxIconEllipsis" />
</cdx-menu-button>
</template>
<script>
import { defineComponent, ref } from 'vue';
import { CdxMenuButton, CdxIcon } from '@wikimedia/codex';
import { cdxIconEllipsis } from '@wikimedia/codex-icons';
const menuConfig = { visibleItemLimit: 5 };
const menuItems = [
{ label: 'New file', value: 'new' },
{ label: 'Open file', value: 'open' },
{ label: 'Save file', value: 'save' },
{ label: 'Edit', value: 'edit' },
{ label: 'Cut', value: 'cut' },
{ label: 'Copy', value: 'copy' },
{ label: 'Paste', value: 'paste' },
{ label: 'Download as PDF', value: 'download' },
{ label: 'Printable version', value: 'print' },
{ label: 'Share', value: 'share' }
];
export default defineComponent( {
name: 'MenuButtonWithScroll',
components: { CdxMenuButton, CdxIcon },
setup() {
const selection = ref( null );
return {
selection,
menuItems,
menuConfig,
cdxIconEllipsis
};
}
} );
</script>
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
:menu-config="menuConfig"
aria-label="More actions"
>
<cdx-icon :icon="cdxIconEllipsis"></cdx-icon>
</cdx-menu-button>
</template>
<script>
const { defineComponent, ref } = require( 'vue' );
const { CdxMenuButton, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconEllipsis } = require( './icons.json' );
const menuConfig = { visibleItemLimit: 5 };
const menuItems = [
{ label: 'New file', value: 'new' },
{ label: 'Open file', value: 'open' },
{ label: 'Save file', value: 'save' },
{ label: 'Edit', value: 'edit' },
{ label: 'Cut', value: 'cut' },
{ label: 'Copy', value: 'copy' },
{ label: 'Paste', value: 'paste' },
{ label: 'Download as PDF', value: 'download' },
{ label: 'Printable version', value: 'print' },
{ label: 'Share', value: 'share' }
];
module.exports = defineComponent( {
name: 'MenuButtonWithScroll',
components: { CdxMenuButton, CdxIcon },
setup() {
const selection = ref( null );
return {
selection,
menuItems,
menuConfig,
cdxIconEllipsis
};
}
} );
</script>
Triggering events on selection
It is possible for a MenuButton to trigger an immediate action when the user selects an item, instead of or in addition to storing their choice as a persistent value. In this demo, the component listens for @update:selected
events from the MenuButton component, handles the selection change with the onSelect
method, displays a temporary message using the Message component, and resets the selection after a few seconds.
- Edit Configuration
- View Phab Ticket
- Contact Owner
- Turn Instrument Off
- Delete Instrument
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
aria-label="Choose an option"
@update:selected="onSelect"
>
<cdx-icon :icon="cdxIconEllipsis" />
</cdx-menu-button>
<cdx-message
v-if="isMessageVisible"
type="notice"
:auto-dismiss="true"
:display-time="3000"
:fade-in="true"
>
{{ messageContent }}
</cdx-message>
</template>
<script>
import { defineComponent, ref } from 'vue';
import { CdxMenuButton, CdxMessage, CdxIcon } from '@wikimedia/codex';
import { cdxIconEllipsis } from '@wikimedia/codex-icons';
const isMessageVisible = ref( false );
const messageContent = ref( '' );
const menuItems = [
{ label: 'Edit Configuration', value: 'edit configuration' },
{ label: 'View Phab Ticket', value: 'view phab ticket' },
{ label: 'Contact Owner', value: 'contact owner' },
{ label: 'Turn Instrument Off', value: 'turn instrument off' },
{ label: 'Delete Instrument', value: 'delete instrument', action: 'destructive' }
];
export default defineComponent( {
name: 'MenuButtonSelection',
components: { CdxMenuButton, CdxMessage, CdxIcon },
setup() {
const selection = ref( null );
function onSelect( newSelection ) {
// Trigger a temporary message that shows the current selection.
messageContent.value = `You chose ${ newSelection }.`;
isMessageVisible.value = true;
setTimeout( () => {
// Auto-dismiss the message.
isMessageVisible.value = false;
}, 3000 );
selection.value = null;
}
return {
selection,
menuItems,
isMessageVisible,
messageContent,
onSelect,
cdxIconEllipsis
};
}
} );
</script>
<template>
<cdx-menu-button
v-model:selected="selection"
:menu-items="menuItems"
aria-label="Choose an option"
@update:selected="onSelect"
>
<cdx-icon :icon="cdxIconEllipsis"></cdx-icon>
</cdx-menu-button>
<cdx-message
v-if="isMessageVisible"
type="notice"
:auto-dismiss="true"
:display-time="3000"
:fade-in="true"
>
{{ messageContent }}
</cdx-message>
</template>
<script>
const { defineComponent, ref } = require( 'vue' );
const { CdxMenuButton, CdxMessage, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconEllipsis } = require( './icons.json' );
const isMessageVisible = ref( false );
const messageContent = ref( '' );
const menuItems = [
{ label: 'Edit Configuration', value: 'edit configuration' },
{ label: 'View Phab Ticket', value: 'view phab ticket' },
{ label: 'Contact Owner', value: 'contact owner' },
{ label: 'Turn Instrument Off', value: 'turn instrument off' },
{ label: 'Delete Instrument', value: 'delete instrument', action: 'destructive' }
];
module.exports = defineComponent( {
name: 'MenuButtonSelection',
components: { CdxMenuButton, CdxMessage, CdxIcon },
setup() {
const selection = ref( null );
function onSelect( newSelection ) {
// Trigger a temporary message that shows the current selection.
messageContent.value = `You chose ${ newSelection }.`;
isMessageVisible.value = true;
setTimeout( () => {
// Auto-dismiss the message.
isMessageVisible.value = false;
}, 3000 );
selection.value = null;
}
return {
selection,
menuItems,
isMessageVisible,
messageContent,
onSelect,
cdxIconEllipsis
};
}
} );
</script>
Other features
The MenuButton component has an internal ToggleButton and Menu. You can use the following features from those components in the MenuButton component:
Vue usage
Props
Prop name | Description | Type | Default |
---|---|---|---|
selected (required) | Value of the current selection. Must be bound with v-model:selected . | string|number|null | |
menuItems (required) | Menu items and/or menu group definitions. Menu groups and individual menu items will be output in the order they appear here. | (MenuButtonItemData|MenuGroupData)[] | |
menuConfig | Configuration for various menu features. All properties default to false. See the MenuConfig type. | MenuConfig | {} |
disabled | Whether the dropdown is disabled. | boolean | false |
footer | Interactive footer item. This is a special menu item which is pinned to the bottom of the menu. When scrolling is enabled within the menu, the footer item will always be visible at the bottom of the menu. When scrolling is not enabled, the footer item will simply appear as the last menu item. The footer item is selectable, like other menu items. | MenuItemData | null |
Events
Event name | Properties | Description |
---|---|---|
update:selected | selected string|number - The new selected value | When the selected value changes. |
Slots
Name | Description | Bindings |
---|---|---|
default | MenuButton content | |
menu-item | Display of an individual item in the menu | menu-item MenuItemData - The current menu item |