Menu Primitive
An unstyled and accessible basis upon which to build Menus.
const BasicMenu = () => {const menu = useMenuPrimitiveState();return (<><MenuPrimitiveButton {...menu}>Preferences</MenuPrimitiveButton><MenuPrimitive {...menu} aria-label="Preferences" style={{backgroundColor: '#fff', zIndex:10}}><MenuPrimitiveItem {...menu}>Settings</MenuPrimitiveItem><MenuPrimitiveItem {...menu} disabled>Extensions</MenuPrimitiveItem><MenuPrimitiveSeparator {...menu} /><MenuPrimitiveItem {...menu}>Keyboard shortcuts</MenuPrimitiveItem></MenuPrimitive></>);};render(<BasicMenu />)
The menu primitive is an unstyled functional version of a menu. It can be used to build a component following the WAI-ARIA Menu pattern. Our Menu is built on top of this primitive.
The purpose of providing these unstyled primitives is to cater for instances when the styled Menu provided by Paste doesn’t meet the requirements needed to solve a unique or individual customer problem. At that point you are welcome to fallback to this functional primitive to roll your own styled menu while still providing a functional and accessible experience to your customers. However, we strongly recommend reviewing your design proposal with the Design Systems team before doing so.
This primitive should be used to compose all custom menus to ensure accessibility and upgrade paths.
Before you roll your own menus...
We strongly suggest that all components built on top of this primitive get reviewed by the Design Systems team and go through the UX Review process to ensure an excellent experience for our customers.
const BasicMenu = () => {const menu = useMenuPrimitiveState();return (<><MenuPrimitiveButton {...menu}>Preferences</MenuPrimitiveButton><MenuPrimitive {...menu} aria-label="Preferences" style={{backgroundColor: '#fff', zIndex:10}}><MenuPrimitiveItem {...menu}>Settings</MenuPrimitiveItem><MenuPrimitiveItem {...menu} disabled>Extensions</MenuPrimitiveItem><MenuPrimitiveSeparator {...menu} /><MenuPrimitiveItem {...menu}>Keyboard shortcuts</MenuPrimitiveItem></MenuPrimitive></>);};render(<BasicMenu />)
const PreferencesMenu = React.forwardRef((props, ref) => {const menu = useMenuPrimitiveState();return (<><MenuPrimitiveButton ref={ref} {...menu} {...props}>Preferences</MenuPrimitiveButton><MenuPrimitive {...menu} aria-label="Preferences" style={{backgroundColor: '#fff', zIndex:10}}><MenuPrimitiveItem {...menu}>Settings</MenuPrimitiveItem><MenuPrimitiveItem {...menu} disabled>Extensions</MenuPrimitiveItem><MenuPrimitiveSeparator {...menu} /><MenuPrimitiveItem {...menu}>Keyboard shortcuts</MenuPrimitiveItem></MenuPrimitive></>);});const SubMenu = () => {const menu = useMenuPrimitiveState();return (<><MenuPrimitiveButton {...menu}>Code</MenuPrimitiveButton><MenuPrimitive {...menu} aria-label="Code" style={{backgroundColor: '#fff', zIndex:10}}><MenuPrimitiveItem {...menu}>About Visual Studio Code</MenuPrimitiveItem><MenuPrimitiveItem {...menu}>Check for Updates...</MenuPrimitiveItem><MenuPrimitiveSeparator {...menu} /><MenuPrimitiveItem {...menu} as={PreferencesMenu} /></MenuPrimitive></>);};render(<SubMenu />)
You can provide custom styling to the primitive menu by utilizing the as
prop on each component.
The menu primitive does not come with any styling, and thus you could mix the functionality of it with another component by using the as prop. By doing so, you can get styling from another component, and menu functionality from this primitive.
Because these are not styled, rendering any of them as another component you can mix the functionality of two components together. Styling from one, menu functionlity from the primitive component.
In the example below, we import the Paste Button import {Button} from '@twilio-paste/button';
and use it as the Menu Button via the as
prop. We also use Box
from Paste to style the menu and menu items.
const StyledMenu = React.forwardRef(({children, ...props}, ref) => (<Box backgroundColor="colorBackgroundBody" borderColor="colorBorderWeak" borderStyle="solid" borderWidth="borderWidth10" borderRadius="borderRadius30" outline="none" padding="space20" zIndex="zIndex10" ref={ref} {...props}>{children}</Box>));const StyledMenuItem = React.forwardRef(({children, ...props}, ref) => (<Box borderRadius="borderRadius30" padding="space30" minWidth="size40" outline="none" _hover={{cursor: 'pointer'}} _focus={{backgroundColor: 'colorBackgroundPrimaryWeakest'}} _disabled={{cursor: 'not-allowed'}} ref={ref} {...props}>{children}</Box>));const CustomMenu = () => {const menu = useMenuPrimitiveState();return (<><MenuPrimitiveButton {...menu} as={Button}>Preferences</MenuPrimitiveButton><MenuPrimitive {...menu} aria-label="Preferences" as={StyledMenu}><MenuPrimitiveItem {...menu} as={StyledMenuItem}>Settings</MenuPrimitiveItem><MenuPrimitiveItem {...menu} disabled as={StyledMenuItem}>Extensions</MenuPrimitiveItem><MenuPrimitiveSeparator {...menu} /><MenuPrimitiveItem {...menu} as={StyledMenuItem}>Keyboard shortcuts</MenuPrimitiveItem></MenuPrimitive></>);};render(<CustomMenu />)
This package is a wrapper around the Reakit Menu. If you’re wondering why we wrapped that package into our own, we reasoned that it would be best for our consumers’ developer experience. For example:
- If we want to migrate the underlying nuts and bolts in the future, Twilio products that depend on this primitive would need to replace all occurrences of
import … from ‘x-package’
toimport … from ‘@some-new/package’
. By wrapping it in@twilio-paste/x-primitive
, this refactor can be avoided. The only change would be a version bump in the ‘package.json` file for the primitive. - We can more strictly enforce semver and backwards compatibility than some of our dependencies.
- We can control when to provide an update and which versions we allow, to help reduce potential bugs our consumers may face.
- We can control which APIs we expose. For example, we may chose to enable or disable usage of certain undocumented APIs.
We've chosen not to include MenuBar for now
Reakit includes a menubar which we are not exposing. The reason being is that a menubar is directly aligned to the desktop software menubar you see at the top of your screen. These have limited use cases on the web unless you're building a desktop replacement like Figma or Google Docs. We may reconsider this in the future if we see a good use for this.
yarn add @twilio-paste/menu-primitive - or - yarn add @twilio-paste/core
This props list is a scoped version of the properties available from the Reakit Menu package. For a full list, visit the Reakit.
Prop | Type | Description | Default |
---|---|---|---|
baseId | string | ID that will serve as a base for all the items IDs. | |
rtl | boolean | ||
orientation | horizontal, vertical, undefined | ||
currentId | string, null, undefined | The current focused item id . | |
loop | boolean, horizontal, vertical | ||
wrap | boolean, horizontal, vertical | ||
visible | boolean | Whether it's visible or not. | |
animated | number, boolean | ||
placement | auto-start, auto, auto-end, top-start, top, top-end, right-start, right, right-end, bottom-end, bottom, bottom-start, left-end, left, left-start | ||
gutter | number, undefined | Offset between the reference and the popover on the main axis. Should not be combined with unstable_offset . |
Prop | Type | Description | Default |
---|---|---|---|
hideOnClickOutside | boolean, undefined | When enabled, user can hide the dialog by clicking outside it. | |
disabled | boolean, undefined | Same as the HTML attribute. |
Prop | Type | Description | Default |
---|---|---|---|
size | string, number, undefined |
Prop | Type | Description | Default |
---|---|---|---|
disabled | boolean, undefined | Same as the HTML attribute. |
Prop | Type | Description | Default |
---|---|---|---|
disabled | boolean, undefined | Same as the HTML attribute. |
No props to show
Prop | Type | Description | Default |
---|---|---|---|
disabled | boolean, undefined | Same as the HTML attribute. | |
id | string, undefined | Same as the HTML attribute. | |
onClick | () => void | Same as the HTML attribute. | |
href | string, undefined | Same as the HTML attribute. |
Prop | Type | Description | Default |
---|---|---|---|
disabled | boolean, undefined | Same as the HTML attribute. | |
value | string, number, undefined | Checkbox's value is going to be used when multiple checkboxes share the same state. Checking a checkbox with value will add it to the state array. | |
checked | boolean, undefined | Checkbox's checked state. If present, it's used instead of state . | |
id | string, undefined | Same as the HTML attribute. | |
name | string | MenuItemCheckbox's name as in menu.values . |
Prop | Type | Description | Default |
---|---|---|---|
disabled | boolean, undefined | Same as the HTML attribute. | |
value | string, number, undefined | Checkbox's value is going to be used when multiple checkboxes share the same state. Checking a checkbox with value will add it to the state array. | |
checked | boolean, undefined | Checkbox's checked state. If present, it's used instead of state . | |
id | string, undefined | Same as the HTML attribute. | |
name | string | MenuItemRadio's name as in menu.values . |
Changelog
e238ce11a
#3618 Thanks @SiTaggart! - [Menu primitve] Updated dev depenedencies to include typescript and tsx for running build scripts
dc8deca8a
#3505 Thanks @nkrantz! - [Menu Primitive] new exports for use in the Account Switcher package: MenuPrimitiveInitialState and MenuPrimitiveStateReturn
733709127
#3395 Thanks @SiTaggart! - Modified the compile target of our JavaScript bundles fromnode
tobrowser
to minimize the risk of clashing with RequireJS. This is marked as a major out of an abundance of caution. You shouldn't need to do anything but we wanted you to be aware of the change on the off chance it has unintended consequences
- Updated dependencies [
733709127
]:- @twilio-paste/reakit-library@2.0.0
5958ffb5b
#3230 Thanks @SiTaggart! - [Menu Primitive]: Corrected the name of previously undocumented exports for Radio and Checkbox Menu Items
- Updated dependencies [
3c89fd83d
]:- @twilio-paste/reakit-library@1.0.0
a4c9e70b0
#2763 Thanks @shleewhite! - Update ESLint rules, which changed some formatting.
ae9dd50f
#2466 Thanks @TheSisb! - [All packages] Update our ESBuild version and remove minification of identifiers in our production builds.
12a5e83e
#2449 Thanks @shleewhite! - Made a slight improvement to the TypeScript typings of several packages for better interoperability.
73c596919
#2269 Thanks @SiTaggart! - Fixed a regression with the compilation script that caused incompatible ESM module importing of JSON files.
c867e3f48
#2237 Thanks @SiTaggart! - Updated a build dependency (esbuild) which changes the output of our builds slightly, without materially changing anything about the code.
b7675915
#1985 Thanks @TheSisb! - For debugging purposes we now ship afilename.debug.js
unminified version of each component or library in Paste.
ed5c0a49c
#1965 Thanks @shleewhite! - Upgrade Paste to use React 17 by default, but maintain React 16 support for consumers.
01baddcd
#1925 Thanks @shleewhite! - Add displayNames to all components
0eded1fd
#1319 Thanks @SiTaggart! - Change internal dependencies to have minor range matching on version numbers
ac38757f
#1228 Thanks @SiTaggart! - Bump status of the component to production
a12acb61
#1158 Thanks @richbachman! - Pinned all twilio-paste package versions in order to keep them in sync with core when they are updated by changesets.
All notable changes to this project will be documented in this file. See Conventional Commits for commit guidelines.
0.2.1 (2021-01-25)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.14 (2021-01-15)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.13 (2021-01-14)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.12 (2020-10-23)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.11 (2020-10-13)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.10 (2020-09-22)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.9 (2020-09-15)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.8 (2020-09-03)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.7 (2020-07-01)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.6 (2020-07-01)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.5 (2020-06-25)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.4 (2020-06-16)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.3 (2020-06-10)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.2 (2020-06-09)
Note: Version bump only for package @twilio-paste/menu-primitive
0.1.1 (2020-06-01)
Note: Version bump only for package @twilio-paste/menu-primitive
- menu-primitive: create the menu primitive component (40a2b8b)