Improve documentation

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-08-17 10:27:43 +02:00
parent bb2a623fc1
commit b08e11c62e
4 changed files with 689 additions and 255 deletions
File diff suppressed because one or more lines are too long
+184 -42
View File
@@ -3,15 +3,26 @@ import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { MenuSnippet } from '@/snippets/stories-snippets';
import {
menuRootPropDefs,
usage,
preview,
submenu,
icons,
sections,
separators,
links,
autocomplete,
autocompleteListbox,
autocompleteListboxMultiple,
menuTriggerPropDefs,
menuPositionerPropDefs,
submenuTriggerPropDefs,
menuPropDefs,
menuListBoxPropDefs,
menuAutocompletePropDefs,
menuAutocompleteListboxPropDefs,
menuItemPropDefs,
menuUsageSnippet,
menuDefaultSnippet,
menuTriggerSnippet,
menuHoverSnippet,
menuSubmenuSnippet,
menuListBoxItemPropDefs,
menuSectionPropDefs,
menuSeparatorPropDefs,
} from './menu.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -25,68 +36,199 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
<Snippet
align="center"
py={4}
preview={<MenuSnippet story="Default" />}
code={menuDefaultSnippet}
preview={<MenuSnippet story="Preview" />}
code={preview}
/>
## Usage
<CodeBlock code={menuUsageSnippet} />
<CodeBlock code={usage} />
### Triggers
- `MenuTrigger` is a wrapper component that combines a button or other trigger element with a menu displayed in a popover.
- `SubmenuTrigger` is a wrapper component that combines a `MenuItem` with a menu displayed in a popover.
### Containers
- `Menu` is a container component that contains a list of menu items or sections.
- `MenuListBox` is a container component that contains a list of menu items or sections.
- `MenuAutocomplete` is a container component that contains a list of menu items or sections.
- `MenuAutocompleteListbox` is a container component that contains a list of menu items or sections.
### Items
- `MenuItem` is an individual interactive item in the menu.
- `MenuListBoxItem` is an individual interactive item in the menu list box.
### Separators
- `MenuSeparator` is a component that renders a horizontal line to separate menu items.
- `MenuSection` is a component that renders a section in the menu.
## API reference
### Menu.Root
### MenuTrigger
Groups all parts of the menu. Doesn't render its own HTML element.
<PropsTable data={menuRootPropDefs} />
### Menu.Trigger
The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend
using the `render` prop to render a custom trigger.
<CodeBlock code={menuTriggerSnippet} />
`MenuTrigger` accepts exactly two children: the first child should be the trigger element, and second child should be
one of the menu containers containing the menu.
<PropsTable data={menuTriggerPropDefs} />
### Menu.Positioner
### SubmenuTrigger
Positions the menu popup against the trigger. Renders a `<div>` element.
The `SubmenuTrigger` accepts exactly two children: the first child should be the `MenuItem` which triggers opening
of the submenu, and second child should be one of the menu containers containing the submenu.
<PropsTable data={menuPositionerPropDefs} />
<PropsTable data={submenuTriggerPropDefs} />
### Menu.Item
### Menu
An individual interactive item in the menu. Renders a `<div>` element.
`Menu` is a container component that contains a list of menu items or sections.
<PropsTable data={menuPropDefs} />
### MenuListBox
`MenuListBox` is a container component that contains a list of menu items or sections.
<PropsTable data={menuListBoxPropDefs} />
### MenuAutocomplete
`MenuAutocomplete` is a container component that contains a list of menu items or sections.
<PropsTable data={menuAutocompletePropDefs} />
### MenuAutocompleteListbox
`MenuAutocompleteListbox` is a container component that contains a list of menu items or sections.
<PropsTable data={menuAutocompleteListboxPropDefs} />
### MenuItem
`MenuItem` is an individual interactive item in the menu.
<PropsTable data={menuItemPropDefs} />
### MenuListBoxItem
`MenuListBoxItem` is an individual interactive item in the menu list box.
<PropsTable data={menuListBoxItemPropDefs} />
### MenuSection
`MenuSection` is a component that renders a section in the menu.
<PropsTable data={menuSectionPropDefs} />
### MenuSeparator
`MenuSeparator` is a component that renders a horizontal line to separate menu items.
<PropsTable data={menuSeparatorPropDefs} />
## Examples
### Open on hover
To create a menu that opens on hover, add the `openOnHover` prop to the Root part.
You can additionally configure how quickly the menu opens on hover using the `delay` prop.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="OpenOnHover" />}
code={menuHoverSnippet}
/>
### Nested navigation
You can nest menus to create a more complex navigation structure.
You can nest menus to create a more complex navigation structure. It is important to use the `placement` prop to ensure
the submenu is displayed in the correct position. The best practice is to use the `right top` placement for the submenu.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="Submenu" />}
code={menuSubmenuSnippet}
preview={<MenuSnippet story="PreviewSubmenu" />}
code={submenu}
/>
### With icons
You can use the `iconStart` prop to add an icon to the menu item.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="PreviewIcons" />}
code={icons}
/>
### With links
You can use the `href` prop to add a link to the menu item. This is using our router provider under the hood
to work for both internal and external links.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="PreviewLinks" />}
code={links}
/>
### With sections
You can use the `MenuSection` component to add a section to the menu.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="PreviewSections" />}
code={sections}
/>
### With separators
You can use the `MenuSeparator` component to add a separator to the menu.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="PreviewSeparators" />}
code={separators}
/>
### With autocomplete
You can use the `MenuAutocomplete` component to add a autocomplete to the menu.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="PreviewAutocompleteMenu" />}
code={autocomplete}
/>
### With list box
You can use the `MenuListBox` component to add a list box to the menu.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="PreviewAutocompleteListbox" />}
code={autocompleteListbox}
/>
### With list box with multiple selection
You can use the `MenuListBox` component to add a list box to the menu. You can also use the `selectionMode` prop to
allow multiple selection.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="PreviewAutocompleteListboxMultiple" />}
code={autocompleteListboxMultiple}
/>
<Theming component="Menu" />
+282 -192
View File
@@ -1,242 +1,332 @@
import {
classNamePropDefs,
stylePropDefs,
renderPropDefs,
type PropDef,
} from '@/utils/propDefs';
export const menuRootPropDefs: Record<string, PropDef> = {
defaultOpen: {
const placementValues = [
'bottom',
'bottom left',
'bottom right',
'bottom start',
'bottom end',
'top',
'top left',
'top right',
'top start',
'top end',
'left',
'left top',
'left bottom',
'start',
'start top',
'start bottom',
'right',
'right top',
'right bottom',
'end',
'end top',
'end bottom',
];
export const menuTriggerPropDefs: Record<string, PropDef> = {
isOpen: {
type: 'boolean',
default: 'false',
},
open: {
defaultOpen: {
type: 'boolean',
},
onOpenChange: {
type: 'enum',
values: ['(open, event) => void'],
},
closeParentOnEsc: {
type: 'boolean',
default: 'true',
},
modal: {
type: 'boolean',
default: 'true',
},
onOpenChangeComplete: {
type: 'enum',
values: ['(open) => void'],
},
disabled: {
type: 'boolean',
default: 'false',
},
openOnHover: {
type: 'boolean',
values: ['(isOpen: boolean) => void'],
},
};
export const submenuTriggerPropDefs: Record<string, PropDef> = {
delay: {
type: 'number',
default: '100',
default: '200',
},
loop: {
type: 'boolean',
default: 'true',
},
orientation: {
};
export const menuPropDefs: Record<string, PropDef> = {
disabledKeys: {
type: 'enum',
values: ['horizontal', 'vertical'],
default: 'vertical',
values: ['Iterable<Key>'],
},
selectionMode: {
type: 'enum',
values: ['none', 'single', 'multiple'],
},
selectedKeys: {
type: 'enum',
values: ['all', 'Iterable<Key>'],
},
defaultSelectedKeys: {
type: 'enum',
values: ['all', 'Iterable<Key>'],
},
placement: {
type: 'enum',
values: placementValues,
},
...classNamePropDefs,
...stylePropDefs,
};
export const menuTriggerPropDefs: Record<string, PropDef> = {
...renderPropDefs,
export const menuListBoxPropDefs: Record<string, PropDef> = {
disabledKeys: {
type: 'enum',
values: ['Iterable<Key>'],
},
selectionMode: {
type: 'enum',
values: ['none', 'single', 'multiple'],
},
selectedKeys: {
type: 'enum',
values: ['all', 'Iterable<Key>'],
},
defaultSelectedKeys: {
type: 'enum',
values: ['all', 'Iterable<Key>'],
},
placement: {
type: 'enum',
values: placementValues,
},
...classNamePropDefs,
...stylePropDefs,
};
export const menuPositionerPropDefs: Record<string, PropDef> = {
align: {
export const menuAutocompletePropDefs: Record<string, PropDef> = {
placement: {
type: 'enum',
values: ['start', 'center', 'end'],
default: 'center',
values: placementValues,
},
alignOffset: {
...classNamePropDefs,
...stylePropDefs,
};
export const menuAutocompleteListboxPropDefs: Record<string, PropDef> = {
placement: {
type: 'enum',
values: ['number', '(data) => number'],
default: '0',
},
side: {
type: 'enum',
values: ['bottom', 'inline-end', 'inline-start', 'left', 'right', 'top'],
default: 'bottom',
},
sideOffset: {
type: 'enum',
values: ['number', '(data) => number'],
default: '0',
},
arrowPadding: {
type: 'number',
default: '5',
},
anchor: {
type: 'enum',
values: [
'React.Ref',
'Element',
'VirtualElement',
'(() => Element | VirtualElement | null)',
'null',
],
},
collisionBoundary: {
type: 'enum',
values: ['clipping-ancestors', 'Element', 'Element[]', 'Rect'],
default: 'clipping-ancestors',
},
collisionPadding: {
type: 'enum',
values: ['number', 'Rect'],
default: '5',
},
sticky: {
type: 'boolean',
default: 'false',
},
positionMethod: {
type: 'enum',
values: ['absolute', 'fixed'],
default: 'absolute',
},
trackAnchor: {
type: 'boolean',
default: 'true',
values: placementValues,
},
...classNamePropDefs,
...stylePropDefs,
};
export const menuItemPropDefs: Record<string, PropDef> = {
label: {
id: {
type: 'enum',
values: ['Key'],
},
value: {
type: 'string',
},
onClick: {
textValue: {
type: 'string',
},
isDisabled: {
type: 'boolean',
},
href: {
type: 'string',
},
onAction: {
type: 'enum',
values: ['(event) => void'],
},
closeOnClick: {
type: 'boolean',
default: 'true',
},
disabled: {
type: 'boolean',
default: 'false',
},
...classNamePropDefs,
...stylePropDefs,
};
export const menuUsageSnippet = `import { Menu } from '@backstage/ui';
export const menuListBoxItemPropDefs: Record<string, PropDef> = {
id: {
type: 'enum',
values: ['Key'],
},
value: {
type: 'string',
},
textValue: {
type: 'string',
},
isDisabled: {
type: 'boolean',
},
...classNamePropDefs,
...stylePropDefs,
};
<Menu.Root>
<Menu.Trigger />
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Item />
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>`;
export const menuSectionPropDefs: Record<string, PropDef> = {
title: {
type: 'string',
},
...classNamePropDefs,
...stylePropDefs,
};
export const menuDefaultSnippet = `<Menu.Root>
<Menu.Trigger
render={props => (
<Button
{...props}
size="medium"
variant="secondary"
iconEnd="chevron-down"
>
Menu
</Button>
)}
/>
<Menu.Portal>
<Menu.Positioner sideOffset={8} align="start">
<Menu.Popup>
<Menu.Item>Settings</Menu.Item>
<Menu.Item>Invite new members</Menu.Item>
<Menu.Item>Download app</Menu.Item>
<Menu.Item>Log out</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>`;
export const menuSeparatorPropDefs: Record<string, PropDef> = {
...classNamePropDefs,
...stylePropDefs,
};
export const menuTriggerSnippet = `<Menu.Trigger render={props => <Button {...props} />} />`;
export const usage = `import { MenuTrigger, Menu, MenuItem } from '@backstage/ui';
export const menuHoverSnippet = `<Menu.Root openOnHover delay={100}>
<Menu.Trigger
render={props => (
<Button
{...props}
size="medium"
variant="secondary"
iconEnd="chevron-down"
>
Menu
</Button>
)}
/>
<Menu.Portal>
<Menu.Positioner sideOffset={8} align="start">
<Menu.Popup>
<Menu.Item>Settings</Menu.Item>
<Menu.Item>Invite new members</Menu.Item>
<Menu.Item>Download app</Menu.Item>
<Menu.Item>Log out</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>`;
<MenuTrigger>
<Button>Menu</Button>
<Menu>
<MenuItem id="apple">Apple</MenuItem>
<MenuItem id="banana">Banana</MenuItem>
<MenuItem id="blueberry">Blueberry</MenuItem>
<MenuSeparator />
<SubmenuTrigger>
<MenuItem>Vegetables</MenuItem>
<Menu>
<MenuItem id="carrot">Carrot</MenuItem>
<MenuItem id="tomato">Tomato</MenuItem>
<MenuItem id="potato">Potato</MenuItem>
</Menu>
</SubmenuTrigger>
</Menu>
</MenuTrigger>`;
export const menuSubmenuSnippet = `<Menu.Trigger
render={props => (
<Button
{...props}
size="small"
variant="secondary"
iconEnd={<Icon name="chevron-down" />}
export const preview = `<MenuTrigger>
<Button>Menu</Button>
<Menu>
{options.map(option => (
<MenuItem key={option.value}>{option.label}</MenuItem>
))}
</Menu>
</MenuTrigger>`;
export const submenu = `<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuItem>Edit</MenuItem>
<MenuItem>Duplicate</MenuItem>
<SubmenuTrigger>
<MenuItem>Submenu</MenuItem>
<Menu placement="right top">
<MenuItem>Edit</MenuItem>
<MenuItem>Duplicate</MenuItem>
<MenuItem>Rename</MenuItem>
<MenuSeparator />
<MenuItem>Share</MenuItem>
<MenuItem>Move</MenuItem>
<MenuSeparator />
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
</Menu>
</SubmenuTrigger>
</Menu>
</MenuTrigger>`;
export const icons = `<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuItem iconStart={<RiFileCopyLine />}>Copy</MenuItem>
<MenuItem iconStart={<RiEdit2Line />}>Rename</MenuItem>
<MenuItem iconStart={<RiChat1Line />}>Send feedback</MenuItem>
</Menu>
</MenuTrigger>`;
export const sections = `<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuSection title="My Account">
<MenuItem iconStart={<RiUserLine />}>Profile</MenuItem>
<MenuItem iconStart={<RiSettingsLine />}>Settings</MenuItem>
</MenuSection>
<MenuSection title="Support">
<MenuItem iconStart={<RiQuestionLine />}>Help Center</MenuItem>
<MenuItem iconStart={<RiCustomerService2Line />}>
Contact Support
</MenuItem>
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
</MenuSection>
</Menu>
</MenuTrigger>`;
export const separators = `<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuItem>Edit</MenuItem>
<MenuItem>Duplicate</MenuItem>
<MenuItem>Rename</MenuItem>
<MenuSeparator />
<MenuItem>Share</MenuItem>
<MenuItem>Move</MenuItem>
<MenuSeparator />
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
</Menu>
</MenuTrigger>`;
export const links = `<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuItem href="/home">Internal link</MenuItem>
<MenuItem href="https://www.google.com" target="_blank">
External link
</MenuItem>
<MenuItem href="mailto:test@test.com">Email link</MenuItem>
</Menu>
</MenuTrigger>`;
export const autocomplete = `<MenuTrigger isOpen>
<Button aria-label="Menu">Menu</Button>
<MenuAutocomplete placeholder="Filter">
<MenuItem>Create new file...</MenuItem>
<MenuItem>Create new folder...</MenuItem>
<MenuItem>Assign to...</MenuItem>
<MenuItem>Assign to me</MenuItem>
<MenuItem>Change status...</MenuItem>
<MenuItem>Change priority...</MenuItem>
<MenuItem>Add label...</MenuItem>
<MenuItem>Remove label...</MenuItem>
</MenuAutocomplete>
</MenuTrigger>`;
export const autocompleteListbox = `const [selected, setSelected] = useState<Selection>(
new Set(['blueberry']),
);
<Flex direction="column" gap="2" align="start">
<Text>Selected: {Array.from(selected).join(', ')}</Text>
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<MenuAutocompleteListbox
selectedKeys={selected}
onSelectionChange={setSelected}
>
Menu
</Button>
)}
/>
<Menu.Portal>
<Menu.Positioner sideOffset={8} align="start">
<Menu.Popup>
<Menu.Item>Settings</Menu.Item>
<Menu.Item>Invite new members</Menu.Item>
<Menu.Item>Download app</Menu.Item>
<Menu.Item>Log out</Menu.Item>
<Menu.Root>
<Menu.SubmenuTrigger>Submenu</Menu.SubmenuTrigger>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Item>Submenu Item 1</Menu.Item>
<Menu.Item>Submenu Item 2</Menu.Item>
<Menu.Item>Submenu Item 3</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>`;
{options.map(option => (
<MenuListBoxItem key={option.value} id={option.value}>
{option.label}
</MenuListBoxItem>
))}
</MenuAutocompleteListbox>
</MenuTrigger>
</Flex>`;
export const autocompleteListboxMultiple = `const [selected, setSelected] = useState<Selection>(
new Set(['blueberry', 'cherry']),
);
<Flex direction="column" gap="2" align="start">
<Text>Selected: {Array.from(selected).join(', ')}</Text>
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<MenuAutocompleteListbox
selectionMode="multiple"
selectedKeys={selected}
onSelectionChange={setSelected}
>
{options.map(option => (
<MenuListBoxItem key={option.value} id={option.value}>
{option.label}
</MenuListBoxItem>
))}
</MenuAutocompleteListbox>
</MenuTrigger>
</Flex>`;
+222 -20
View File
@@ -37,6 +37,7 @@ import {
RiSettingsLine,
RiUserLine,
RiDeleteBinLine,
RiShareBoxLine,
} from '@remixicon/react';
import { useState } from 'react';
import { Selection } from 'react-aria-components';
@@ -70,25 +71,226 @@ const options = [
{ label: 'Honeydew', value: 'honeydew' },
];
export const Default: Story = {
export const Preview: Story = {
args: {
children: null,
},
render: () => (
<MenuTrigger>
<Button>Menu</Button>
<Button aria-label="Menu">Menu</Button>
<Menu>
{options.map(option => (
<MenuItem key={option.value}>{option.label}</MenuItem>
))}
<MenuItem>Edit</MenuItem>
<MenuItem>Duplicate</MenuItem>
<MenuItem>Rename</MenuItem>
<MenuSeparator />
<MenuItem iconStart={<RiShareBoxLine />}>Share</MenuItem>
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
<MenuSeparator />
<SubmenuTrigger>
<MenuItem iconStart={<RiSettingsLine />}>Settings</MenuItem>
<Menu placement="right top">
<MenuItem>Edit</MenuItem>
<MenuItem>Duplicate</MenuItem>
<MenuItem>Rename</MenuItem>
</Menu>
</SubmenuTrigger>
</Menu>
</MenuTrigger>
),
};
export const PreviewSubmenu: Story = {
args: {
...Preview.args,
},
render: () => (
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuItem>Edit</MenuItem>
<MenuItem>Duplicate</MenuItem>
<SubmenuTrigger>
<MenuItem>Submenu</MenuItem>
<Menu placement="right top">
<MenuItem>Edit</MenuItem>
<MenuItem>Duplicate</MenuItem>
<MenuItem>Rename</MenuItem>
<MenuSeparator />
<MenuItem>Share</MenuItem>
<MenuItem>Move</MenuItem>
<MenuSeparator />
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
</Menu>
</SubmenuTrigger>
</Menu>
</MenuTrigger>
),
};
export const PreviewIcons: Story = {
args: {
...Preview.args,
},
render: () => (
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuItem iconStart={<RiFileCopyLine />}>Copy</MenuItem>
<MenuItem iconStart={<RiEdit2Line />}>Rename</MenuItem>
<MenuItem iconStart={<RiChat1Line />}>Send feedback</MenuItem>
</Menu>
</MenuTrigger>
),
};
export const PreviewSections: Story = {
args: {
...Preview.args,
},
render: () => (
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuSection title="My Account">
<MenuItem iconStart={<RiUserLine />}>Profile</MenuItem>
<MenuItem iconStart={<RiSettingsLine />}>Settings</MenuItem>
</MenuSection>
<MenuSection title="Support">
<MenuItem iconStart={<RiQuestionLine />}>Help Center</MenuItem>
<MenuItem iconStart={<RiCustomerService2Line />}>
Contact Support
</MenuItem>
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
</MenuSection>
</Menu>
</MenuTrigger>
),
};
export const PreviewSeparators: Story = {
args: {
...Preview.args,
},
render: () => (
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuItem>Edit</MenuItem>
<MenuItem>Duplicate</MenuItem>
<MenuItem>Rename</MenuItem>
<MenuSeparator />
<MenuItem>Share</MenuItem>
<MenuItem>Move</MenuItem>
<MenuSeparator />
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
</Menu>
</MenuTrigger>
),
};
export const PreviewLinks: Story = {
args: {
...Preview.args,
},
render: () => (
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<Menu>
<MenuItem href="/home">Internal link</MenuItem>
<MenuItem href="https://www.google.com" target="_blank">
External link
</MenuItem>
<MenuItem href="mailto:test@test.com">Email link</MenuItem>
</Menu>
</MenuTrigger>
),
};
export const PreviewAutocompleteMenu: Story = {
args: {
...Preview.args,
},
render: () => (
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<MenuAutocomplete placeholder="Filter">
<MenuItem>Create new file...</MenuItem>
<MenuItem>Create new folder...</MenuItem>
<MenuItem>Assign to...</MenuItem>
<MenuItem>Assign to me</MenuItem>
<MenuItem>Change status...</MenuItem>
<MenuItem>Change priority...</MenuItem>
<MenuItem>Add label...</MenuItem>
<MenuItem>Remove label...</MenuItem>
</MenuAutocomplete>
</MenuTrigger>
),
};
export const PreviewAutocompleteListbox: Story = {
args: {
...Preview.args,
},
render: () => {
const [selected, setSelected] = useState<Selection>(
new Set([options[2].value]),
);
return (
<Flex direction="column" gap="2" align="center">
<Text>Selected: {Array.from(selected).join(', ')}</Text>
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<MenuAutocompleteListbox
selectedKeys={selected}
onSelectionChange={setSelected}
>
{options.map(option => (
<MenuListBoxItem key={option.value} id={option.value}>
{option.label}
</MenuListBoxItem>
))}
</MenuAutocompleteListbox>
</MenuTrigger>
</Flex>
);
},
};
export const PreviewAutocompleteListboxMultiple: Story = {
args: {
...Preview.args,
},
render: () => {
const [selected, setSelected] = useState<Selection>(
new Set([options[2].value, options[3].value]),
);
return (
<Flex direction="column" gap="2" align="center">
<Text>Selected: {Array.from(selected).join(', ')}</Text>
<MenuTrigger>
<Button aria-label="Menu">Menu</Button>
<MenuAutocompleteListbox
selectionMode="multiple"
selectedKeys={selected}
onSelectionChange={setSelected}
>
{options.map(option => (
<MenuListBoxItem key={option.value} id={option.value}>
{option.label}
</MenuListBoxItem>
))}
</MenuAutocompleteListbox>
</MenuTrigger>
</Flex>
);
},
};
export const Opened: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -104,7 +306,7 @@ export const Opened: Story = {
export const WithIcons: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -120,7 +322,7 @@ export const WithIcons: Story = {
export const WithScrolling: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -138,7 +340,7 @@ export const WithScrolling: Story = {
export const WithSections: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -162,7 +364,7 @@ export const WithSections: Story = {
export const WithSeparators: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -183,7 +385,7 @@ export const WithSeparators: Story = {
export const WithColors: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -203,7 +405,7 @@ export const WithColors: Story = {
export const WithLinks: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -221,7 +423,7 @@ export const WithLinks: Story = {
export const WithLinksTest: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -237,7 +439,7 @@ export const WithLinksTest: Story = {
export const WithListBox: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -253,7 +455,7 @@ export const WithListBox: Story = {
export const WithListBoxControlled: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => {
const [selected, setSelected] = useState<Selection>(new Set(['paul']));
@@ -289,7 +491,7 @@ export const WithListBoxControlled: Story = {
export const WithAutocompleteMenu: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -310,7 +512,7 @@ export const WithAutocompleteMenu: Story = {
export const WithAutocompleteListbox: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => {
const [selected, setSelected] = useState<Selection>(
@@ -340,7 +542,7 @@ export const WithAutocompleteListbox: Story = {
export const Submenu: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -368,7 +570,7 @@ export const Submenu: Story = {
export const SubmenuAutocompleteMenu: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => (
<MenuTrigger isOpen>
@@ -392,7 +594,7 @@ export const SubmenuAutocompleteMenu: Story = {
export const SubmenuAutocompleteListbox: Story = {
args: {
...Default.args,
...Preview.args,
},
render: () => {
const [selected, setSelected] = useState<Selection>(