Improve documentation
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -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" />
|
||||
|
||||
@@ -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>`;
|
||||
|
||||
@@ -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>(
|
||||
|
||||
Reference in New Issue
Block a user