diff --git a/.changeset/deprecate-actionmenuv1-promote-actionmenuv2.md b/.changeset/deprecate-actionmenuv1-promote-actionmenuv2.md deleted file mode 100644 index 4c769755314..00000000000 --- a/.changeset/deprecate-actionmenuv1-promote-actionmenuv2.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -'@primer/react': major ---- - -### ActionMenu - -ActionMenu now ships a composable API that utilises ActionList - -See full list of props and examples: https://primer.style/react/ActionMenu - -Main changes: - -1. Instead of using `items` prop, use `ActionList` inside `ActionMenu` -2. Instead of using `anchorContent` on `ActionMenu`, use `ActionMenu.Button` with `children` -3. Instead of using `onAction` prop on `ActionMenu`, use `onSelect` prop on `ActionList.Item` -4. Instead of using `groupMetadata` on `ActionMenu`, use `ActionList.Group` -5. Instead of `overlayProps` on `ActionMenu`, use `ActionMenu.Overlay` - - - - - - - - - -
Before After
- -```jsx - -``` - - - -```jsx - - Menu - - - New file - Copy link - Edit file - - Delete file - - - -``` - -
- -To continue to use the deprecated API for now, change the import path to `@primer/react/deprecated`: - -```js -import {ActionMenu} from '@primer/react/deprecated' -``` diff --git a/.changeset/empty-pillows-hunt.md b/.changeset/empty-pillows-hunt.md index bda4d62c285..922ed22e895 100644 --- a/.changeset/empty-pillows-hunt.md +++ b/.changeset/empty-pillows-hunt.md @@ -2,151 +2,4 @@ '@primer/react': major --- -### ActionList - -ActionList now ships a composable API. - -See full list of props and examples: https://primer.style/react/ActionList - - - - - - - - - - - - - - - - - -
Before After
- -```jsx - -``` - - - -```jsx - - New file - Copy link - Edit file - - Delete file - -``` - -
- -```jsx - , - text: 'mona', - description: 'Monalisa Octocat', - descriptionVariant: 'block' - }, - { - key: '2', - leadingVisual: GearIcon, - text: 'View Settings', - trailingVisual: ArrowRightIcon - } - ]} -/> -``` - - - -```jsx - - - - - - github/primer - - - - - - mona - Monalisa Octocat - - - - - - View settings - - - - - -``` - -
- -```jsx - -``` - - - -```jsx - - - repo:github/github - - - - - Table - Board Description> - - - - View settings - -``` - -
- -To continue to use the deprecated API for now, change the import path to `@primer/react/deprecated`: - -```js -import {ActionList} from '@primer/react/deprecated' -``` +Prepare library for `v35` diff --git a/docs/content/ActionList.mdx b/docs/content/ActionList.mdx index f414bdd2f3d..7eb5066cca2 100644 --- a/docs/content/ActionList.mdx +++ b/docs/content/ActionList.mdx @@ -3,447 +3,98 @@ componentId: action_list title: ActionList status: Alpha source: https://github.com/primer/react/tree/main/src/ActionList -storybook: '/react/storybook?path=/story/composite-components-actionlist' -description: An ActionList is a list of items that can be activated or selected. ActionList is the base component for many menu-type components, including DropdownMenu and ActionMenu. --- -import {Avatar} from '@primer/react' -import {ActionList} from '@primer/react' -import {LinkIcon, AlertIcon, ArrowRightIcon} from '@primer/octicons-react' -import InlineCode from '@primer/gatsby-theme-doctocat/src/components/inline-code' +An `ActionList` is a list of items which can be activated or selected. `ActionList` is the base component for many of our menu-type components, including `DropdownMenu` and `ActionMenu`. - - - - - - - github.com/primer - - A React implementation of GitHub's Primer Design System - - - - - - - mona - Monalisa Octocat - - - - - - 4 vulnerabilities - - - - - - - -```js -import {ActionList} from '@primer/react' -``` - -## Examples - -### Minimal example - -```jsx live - - New file - Copy link - Edit file - - Delete file - -``` - -### With leading visual - -Leading visuals are optional and appear at the start of an item. They can be octicons, avatars, and other custom visuals that fit a small area. - - -```jsx live - - - - github.com/primer - - - - 4 vulnerabilities - - - - mona - - - -``` - -### With trailing visual - -Trailing visual and trailing text can display auxiliary information. They're placed at the right of the item, and can denote status, keyboard shortcuts, or be used to set expectations about what the action does. +## Minimal example ```jsx live - - - New file - ⌘ + N - - - Copy link - ⌘ + C - - - Edit file - ⌘ + E - - - Delete file - - - -``` - -### With description and dividers - -Item dividers allow users to parse heavier amounts of information. They're placed between items and are useful in complex lists, particularly when descriptions or multi-line text is present. - -```jsx live - - - - - - mona - Monalisa Octocat - - - - - - hubot - Hubot - - - - - - primer-css - GitHub Design Systems Bot - - + ``` -### With links - -When you want to add links to the List instead of actions, use `ActionList.LinkItem` +## Example with grouped items - ```jsx live - - - - - - github/primer - - - - - - MIT License - - - - - - 1.4k stars - - -``` - -### With groups - -```javascript live noinline -const SelectFields = () => { - const [options, setOptions] = React.useState([ - {text: 'Status', selected: true}, - {text: 'Stage', selected: true}, - {text: 'Assignee', selected: true}, - {text: 'Team', selected: true}, - {text: 'Estimate', selected: false}, - {text: 'Due Date', selected: false} - ]) - - const visibleOptions = options.filter(option => option.selected) - const hiddenOptions = options.filter(option => !option.selected) - - const toggle = text => { - setOptions( - options.map(option => { - if (option.text === text) option.selected = !option.selected - return option - }) - ) - } - - return ( - - - {visibleOptions.map(option => ( - toggle(option.text)}> - {option.text} - - ))} - - - {hiddenOptions.map((option, index) => ( - toggle(option.text)}> - {option.text} - - ))} - {hiddenOptions.length === 0 && No hidden fields} - - - ) -} - -render() + ``` -## Props - -### ActionList - - - - - inset children are offset (vertically and horizontally) from list edges.{' '} - full children are flush (vertically and horizontally) with list edges - - } - /> - - - AriaRole - } - description={ - <> - ARIA role describing the function of the list. listbox and{' '} - menu are a common values. - - } - /> - - - -### ActionList.Item - - - - - danger indicates that the item is destructive. - - } - /> - - - - AriaRole - } - description={ - <> - ARIA role describing the function of the item. option is a common value. - - } - /> - - - -### ActionList.LinkItem - - - - MDN +## Example with custom item renderer + +```jsx + + }, + { + text: 'React Router link', + renderItem: props => + }, + { + text: 'NextJS style', + renderItem: props => ( + + + + ) } - /> - - -### ActionList.LeadingVisual - - - - - - -### ActionList.TrailingVisual - - - - - - -### ActionList.Description - - - - - inline descriptions are positioned beside primary text. block{' '} - descriptions are positioned below primary text. - - } - /> - - - -### ActionList.Group - - - - - - - inline descriptions are positioned beside primary text. block{' '} - descriptions are positioned below primary text. - - } - /> - - Set selectionVariant at the group level. - - } - /> - AriaRole - } - description={ - <> - ARIA role describing the function of the list inside the group. listbox and{' '} - menu are a common values. - - } - /> - - - -## Status - - +``` -## Further reading - -- [Interface guidelines: Action List](https://primer.style/design/components/action-list) - -## Related components +## Props -- [ActionMenu](/ActionMenu) -- [DropdownMenu](/DropdownMenu) -- [SelectPanel](/SelectPanel) +| Name | Type | Default | Description | +| :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :---------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------ | +| items | `(ItemProps & Omit, keyof ItemProps>) \| ((Partial & {renderItem: RenderItemFn}) & {key?: Key})` | `undefined` | Required. A list of item objects conforming to the `ActionList.Item` props interface. | +| renderItem | `(props: ItemProps) => JSX.Element` | `ActionList.Item` | Optional. If defined, each item in `items` will be passed to this function, allowing for `ActionList`-wide custom item rendering. | +| groupMetadata | `GroupProps[]` | `undefined` | Optional. If defined, `ActionList` will group `items` into `ActionList.Group`s separated by `ActionList.Divider` according to their `groupId` property. | +| showItemDividers | `boolean` | `false` | Optional. If `true` dividers will be displayed above each `ActionList.Item` which does not follow an `ActionList.Header` or `ActionList.Divider` | diff --git a/docs/content/ActionMenu.mdx b/docs/content/ActionMenu.mdx index 8c97169f556..1c0e13ecd9b 100644 --- a/docs/content/ActionMenu.mdx +++ b/docs/content/ActionMenu.mdx @@ -2,338 +2,81 @@ componentId: action_menu title: ActionMenu status: Alpha -source: https://github.com/primer/react/tree/main/src/ActionMenu.tsx -storybook: '/react/storybook?path=/story/composite-components-actionmenu' -description: An ActionMenu is an ActionList-based component for creating a menu of actions that expands through a trigger button. --- -import {Box, Avatar, ActionList, ActionMenu} from '@primer/react' +An `ActionMenu` is an ActionList-based component for creating a menu of actions that expands through a trigger button. -
- - - - Menu - - - - Copy link - ⌘C - - - Quote reply - ⌘Q - - - Edit comment - ⌘E - - - - Delete file - ⌘D - - - - - - -
- -```js -import {ActionMenu} from '@primer/react/drafts' -``` - -
- -## Examples - -### Minimal example - -`ActionMenu` ships with `ActionMenu.Button` which is an accessible trigger for the overlay. It's recommended to compose `ActionList` with `ActionMenu.Overlay` - -  +## Default example ```jsx live - - Menu - - - - console.log('New file')}>New file - Copy link - Edit file - - Delete file - - - -``` - -### With a custom anchor - -You can choose to have a different _anchor_ for the Menu dependending on the application's context. - -  - -```jsx live - - - - - - - - - - - - - - Rename - - - - - - Archive all cards - - - - - - Delete - - - - + console.log(text)} + items={[ + {text: 'New file', key: 'new-file'}, + ActionMenu.Divider, + {text: 'Copy link', key: 'copy-link'}, + {text: 'Edit file', key: 'edit-file'}, + {text: 'Delete file', variant: 'danger', key: 'delete-file'} + ]} +/> ``` -### With Groups +## Example with grouped items ```jsx live - - Open column menu - - - - - - - - - repo:github/memex,github/github - - - - - - - - - Table - - Information-dense table optimized for operations across teams - - - - - - - Board - Kanban-style board focused on visual states - - - - - - - - - Save sort and filters to current view - - - - - - Save sort and filters to new view - - - - - - - - - View settings - - - - - -``` - -### With selection - -Use `selectionVariant` on `ActionList` to create a menu with single or multiple selection. - -```javascript live noinline -const fieldTypes = [ - {icon: TypographyIcon, name: 'Text'}, - {icon: NumberIcon, name: 'Number'}, - {icon: CalendarIcon, name: 'Date'}, - {icon: SingleSelectIcon, name: 'Single select'}, - {icon: IterationsIcon, name: 'Iteration'} -] - -const Example = () => { - const [selectedIndex, setSelectedIndex] = React.useState(1) - const selectedType = fieldTypes[selectedIndex] - - return ( - - - {selectedType.name} - - - - {fieldTypes.map((type, index) => ( - setSelectedIndex(index)}> - {type.name} - - ))} - - - - ) -} - -render() -``` - -### With External Anchor - -To create an anchor outside of the menu, you need to switch to controlled mode for the menu and pass it as `anchorRef` to `ActionMenu`. Make sure you add `aria-expanded` and `aria-haspopup` to the external anchor: - -```javascript live noinline -const Example = () => { - const [open, setOpen] = React.useState(false) - const anchorRef = React.createRef() - - return ( - <> - - - - - - Copy link - Quote reply - Edit comment - - Delete file - - - - - ) -} - -render() -``` - -### With Overlay Props - -To create an anchor outside of the menu, you need to switch to controlled mode for the menu and pass it as `anchorRef` to `ActionMenu`: - -```javascript live noinline -const handleEscape = () => alert('you hit escape!') - -render( - - Open Actions Menu - - - - Open current Codespace - - Your existing Codespace will be opened to its previous state, and you'll be asked to manually switch to - new-branch. - - ⌘O - - - Create new Codespace - - Create a brand new Codespace with a fresh image and checkout this branch. - - ⌘C - - - - -) -``` - -## Props / API reference - -### ActionMenu - -| Name | Type | Default | Description | -| :----------- | :----------------------------- | :-----: | :----------------------------------------------------------------------------------------------------------------------- | -| children\* | `React.ReactElement[]` | - | Required. Recommended: `ActionMenu.Button` or `ActionMenu.Anchor` with `ActionMenu.Overlay` | -| open | `boolean` | - | Optional. If defined, will control the open/closed state of the overlay. Must be used in conjuction with `onOpenChange`. | -| onOpenChange | `(open: boolean) => void` | - | Optional. If defined, will control the open/closed state of the overlay. Must be used in conjuction with `open`. | -| anchorRef | `React.RefObject` | - | Optional. Useful for defining an external anchor | - -### ActionMenu.Button - -| Type | Default | Description | -| :----------------------------------------------- | :-----: | :---------------------------------------------------------------------------------------------------------------- | -| [Button v2 props](/drafts/Button2#api-reference) | - | You can pass all of the props that you would pass to a [`Button`](/drafts/Button2) component like `variant`, `sx` | - -### ActionMenu.Anchor - -| Name | Type | Default | Description | -| :--------- | :------------------- | :-----: | :-------------------------------- | -| children\* | `React.ReactElement` | - | Required. Accepts a single child. | - -### ActionMenu.Overlay - -| Name | Type | Default | Description | -| :--------------------------------------- | :-------------------- | :-----------------: | :-------------------------------------------------------------------------------------------- | -| children\* | `React.ReactElement[] | React.ReactElement` | Required. Recommended: [`ActionList`](/ActionList) | -| [OverlayProps](/Overlay#component-props) | - | - | Optional. Props to be spread on the internal [`AnchoredOverlay`](/AnchoredOverlay) component. | - -## Status - - console.log(text)} + groupMetadata={[ + {groupId: '0'}, + {groupId: '1', header: {title: 'Live query', variant: 'subtle'}}, + {groupId: '2', header: {title: 'Layout', variant: 'subtle'}}, + {groupId: '3'}, + {groupId: '4'} + ]} + items={[ + {key: '1', leadingVisual: TypographyIcon, text: 'Rename', groupId: '0'}, + {key: '2', leadingVisual: VersionsIcon, text: 'Duplicate', groupId: '0'}, + {key: '3', leadingVisual: SearchIcon, text: 'repo:github/github', groupId: '1'}, + { + key: '4', + leadingVisual: NoteIcon, + text: 'Table', + description: 'Information-dense table optimized for operations across teams', + descriptionVariant: 'block', + groupId: '2' + }, + { + key: '5', + leadingVisual: ProjectIcon, + text: 'Board', + description: 'Kanban-style board focused on visual states', + descriptionVariant: 'block', + groupId: '2' + }, + { + key: '6', + leadingVisual: FilterIcon, + text: 'Save sort and filters to current view', + disabled: true, + groupId: '3' + }, + {key: '7', leadingVisual: FilterIcon, text: 'Save sort and filters to new view', groupId: '3'}, + {key: '8', leadingVisual: GearIcon, text: 'View settings', groupId: '4'} + ]} /> +``` -## Further reading - -[Interface guidelines: Action List + Menu](https://primer.style/design/components/action-list) - -## Related components - -- [ActionList](/ActionList) -- [SelectPanel](/SelectPanel) -- [Button](/drafts/Button2) +## Component props + +| Name | Type | Default | Description | +| :------------ | :------------------------------------ | :---------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| items | `ItemProps[]` | `undefined` | Required. A list of item objects conforming to the `ActionList.Item` props interface. | +| renderItem | `(props: ItemProps) => JSX.Element` | `ActionList.Item` | Optional. If defined, each item in `items` will be passed to this function, allowing for `ActionList`-wide custom item rendering. | +| groupMetadata | `GroupProps[]` | `undefined` | Optional. If defined, `ActionList` will group `items` into `ActionList.Group`s separated by `ActionList.Divider` according to their `groupId` property. | +| renderAnchor | `(props: ButtonProps) => JSX.Element` | `Button` | Optional. If defined, provided component will be used to render the menu anchor. Will receive the selected `Item` text as `children` prop when an item is activated. | +| anchorContent | React.ReactNode | `undefined` | Optional. If defined, it will be passed to the trigger as the elements child. | +| onAction | (props: ItemProps) => void | `undefined` | Optional. If defined, this function will be called when a menu item is activated either by a click or a keyboard press. | +| open | boolean | `undefined` | Optional. If defined, ActionMenu will use this to control the open/closed state of the Overlay instead of controlling the state internally. Should be used in conjunction with the `setOpen` prop. | +| setOpen | (state: boolean) => void | `undefined` | Optional. If defined, ActionMenu will use this to control the open/closed state of the Overlay instead of controlling the state internally. Should be used in conjunction with the `open` prop. | diff --git a/docs/content/deprecated/ActionList.mdx b/docs/content/deprecated/ActionList.mdx deleted file mode 100644 index 1b933448b03..00000000000 --- a/docs/content/deprecated/ActionList.mdx +++ /dev/null @@ -1,136 +0,0 @@ ---- -componentId: action_list -title: ActionList -status: Deprecated -source: https://github.com/primer/react/tree/main/src/deprecated/ActionList ---- - -An `ActionList` is a list of items which can be activated or selected. `ActionList` is the base component for many of our menu-type components, including `DropdownMenu` and `ActionMenu`. - -## Deprecation - -Use [composable API instead](/ActionList) instead. - -**Before** - -```jsx - -``` - -**After** - -```jsx - - New file - Copy link - Edit file - - Delete file - -``` - -Or continue using deprecated API: - -```js -import {ActionList} from '@primer/react/deprecated' -``` - -## Minimal example - -```jsx live deprecated - -``` - -## Example with grouped items - -```jsx live deprecated - -``` - -## Example with custom item renderer - -```jsx deprecated - - }, - { - text: 'React Router link', - renderItem: props => - }, - { - text: 'NextJS style', - renderItem: props => ( - - - - ) - } - ]} -/> -``` - -## Props - -| Name | Type | Default | Description | -| :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :---------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------ | -| items | `(ItemProps & Omit, keyof ItemProps>) \| ((Partial & {renderItem: RenderItemFn}) & {key?: Key})` | `undefined` | Required. A list of item objects conforming to the `ActionList.Item` props interface. | -| renderItem | `(props: ItemProps) => JSX.Element` | `ActionList.Item` | Optional. If defined, each item in `items` will be passed to this function, allowing for `ActionList`-wide custom item rendering. | -| groupMetadata | `GroupProps[]` | `undefined` | Optional. If defined, `ActionList` will group `items` into `ActionList.Group`s separated by `ActionList.Divider` according to their `groupId` property. | -| showItemDividers | `boolean` | `false` | Optional. If `true` dividers will be displayed above each `ActionList.Item` which does not follow an `ActionList.Header` or `ActionList.Divider` | diff --git a/docs/content/deprecated/ActionMenu.mdx b/docs/content/deprecated/ActionMenu.mdx deleted file mode 100644 index 46c542ba2e3..00000000000 --- a/docs/content/deprecated/ActionMenu.mdx +++ /dev/null @@ -1,127 +0,0 @@ ---- -componentId: action_menu -title: ActionMenu -status: Deprecated -source: https://github.com/primer/react/tree/main/src/deprecated/ActionMenu.tsx ---- - -An `ActionMenu` is an ActionList-based component for creating a menu of actions that expands through a trigger button. - -## Deprecation - -Use [composable API](/ActionMenu) with ActionList instead. - -**Before** - -```jsx - -``` - -**After** - -```jsx - - Menu - - - New file - Copy link - Edit file - - Delete file - - - -``` - -Or continue using deprecated API: - -```js -import {ActionMenu} from '@primer/react/deprecated' -``` - -## Default example - -```jsx live deprecated - console.log(text)} - items={[ - {text: 'New file', key: 'new-file'}, - ActionMenu.Divider, - {text: 'Copy link', key: 'copy-link'}, - {text: 'Edit file', key: 'edit-file'}, - {text: 'Delete file', variant: 'danger', key: 'delete-file'} - ]} -/> -``` - -## Example with grouped items - -```jsx live deprecated - console.log(text)} - groupMetadata={[ - {groupId: '0'}, - {groupId: '1', header: {title: 'Live query', variant: 'subtle'}}, - {groupId: '2', header: {title: 'Layout', variant: 'subtle'}}, - {groupId: '3'}, - {groupId: '4'} - ]} - items={[ - {key: '1', leadingVisual: TypographyIcon, text: 'Rename', groupId: '0'}, - {key: '2', leadingVisual: VersionsIcon, text: 'Duplicate', groupId: '0'}, - {key: '3', leadingVisual: SearchIcon, text: 'repo:github/github', groupId: '1'}, - { - key: '4', - leadingVisual: NoteIcon, - text: 'Table', - description: 'Information-dense table optimized for operations across teams', - descriptionVariant: 'block', - groupId: '2' - }, - { - key: '5', - leadingVisual: ProjectIcon, - text: 'Board', - description: 'Kanban-style board focused on visual states', - descriptionVariant: 'block', - groupId: '2' - }, - { - key: '6', - leadingVisual: FilterIcon, - text: 'Save sort and filters to current view', - disabled: true, - groupId: '3' - }, - {key: '7', leadingVisual: FilterIcon, text: 'Save sort and filters to new view', groupId: '3'}, - {key: '8', leadingVisual: GearIcon, text: 'View settings', groupId: '4'} - ]} -/> -``` - -## Component props - -| Name | Type | Default | Description | -| :------------ | :------------------------------------ | :---------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| items | `ItemProps[]` | `undefined` | Required. A list of item objects conforming to the `ActionList.Item` props interface. | -| renderItem | `(props: ItemProps) => JSX.Element` | `ActionList.Item` | Optional. If defined, each item in `items` will be passed to this function, allowing for `ActionList`-wide custom item rendering. | -| groupMetadata | `GroupProps[]` | `undefined` | Optional. If defined, `ActionList` will group `items` into `ActionList.Group`s separated by `ActionList.Divider` according to their `groupId` property. | -| renderAnchor | `(props: ButtonProps) => JSX.Element` | `Button` | Optional. If defined, provided component will be used to render the menu anchor. Will receive the selected `Item` text as `children` prop when an item is activated. | -| anchorContent | React.ReactNode | `undefined` | Optional. If defined, it will be passed to the trigger as the elements child. | -| onAction | (props: ItemProps) => void | `undefined` | Optional. If defined, this function will be called when a menu item is activated either by a click or a keyboard press. | -| open | boolean | `undefined` | Optional. If defined, ActionMenu will use this to control the open/closed state of the Overlay instead of controlling the state internally. Should be used in conjunction with the `setOpen` prop. | -| setOpen | (state: boolean) => void | `undefined` | Optional. If defined, ActionMenu will use this to control the open/closed state of the Overlay instead of controlling the state internally. Should be used in conjunction with the `open` prop. | diff --git a/docs/content/drafts/ActionList2.mdx b/docs/content/drafts/ActionList2.mdx new file mode 100644 index 00000000000..bf842be1fe6 --- /dev/null +++ b/docs/content/drafts/ActionList2.mdx @@ -0,0 +1,449 @@ +--- +componentId: action_list2 +title: ActionList v2 +status: Alpha +source: https://github.com/primer/react/tree/main/src/ActionList2 +storybook: '/react/storybook?path=/story/composite-components-actionlist2' +description: An ActionList is a list of items that can be activated or selected. ActionList is the base component for many menu-type components, including DropdownMenu and ActionMenu. +--- + +import {Avatar} from '@primer/react' +import {ActionList} from '@primer/react/drafts' +import {LinkIcon, AlertIcon, ArrowRightIcon} from '@primer/octicons-react' +import InlineCode from '@primer/gatsby-theme-doctocat/src/components/inline-code' + + + + + + + + github.com/primer + + A React implementation of GitHub's Primer Design System + + + + + + + mona + Monalisa Octocat + + + + + + 4 vulnerabilities + + + + + + + +```js +import {ActionList} from '@primer/react/drafts' +``` + +## Examples + +### Minimal example + +```jsx live drafts + + New file + Copy link + Edit file + + Delete file + +``` + +### With leading visual + +Leading visuals are optional and appear at the start of an item. They can be octicons, avatars, and other custom visuals that fit a small area. + + +```jsx live drafts + + + + github.com/primer + + + + 4 vulnerabilities + + + + mona + + + +``` + +### With trailing visual + +Trailing visual and trailing text can display auxiliary information. They're placed at the right of the item, and can denote status, keyboard shortcuts, or be used to set expectations about what the action does. + +```jsx live drafts + + + New file + ⌘ + N + + + Copy link + ⌘ + C + + + Edit file + ⌘ + E + + + Delete file + + + +``` + +### With description and dividers + +Item dividers allow users to parse heavier amounts of information. They're placed between items and are useful in complex lists, particularly when descriptions or multi-line text is present. + +```jsx live drafts + + + + + + mona + Monalisa Octocat + + + + + + hubot + Hubot + + + + + + primer-css + GitHub Design Systems Bot + + +``` + +### With links + +When you want to add links to the List instead of actions, use `ActionList.LinkItem` + + +```jsx live drafts + + + + + + github/primer + + + + + + MIT License + + + + + + 1.4k stars + + +``` + +### With groups + +```javascript live noinline drafts +const SelectFields = () => { + const [options, setOptions] = React.useState([ + {text: 'Status', selected: true}, + {text: 'Stage', selected: true}, + {text: 'Assignee', selected: true}, + {text: 'Team', selected: true}, + {text: 'Estimate', selected: false}, + {text: 'Due Date', selected: false} + ]) + + const visibleOptions = options.filter(option => option.selected) + const hiddenOptions = options.filter(option => !option.selected) + + const toggle = text => { + setOptions( + options.map(option => { + if (option.text === text) option.selected = !option.selected + return option + }) + ) + } + + return ( + + + {visibleOptions.map(option => ( + toggle(option.text)}> + {option.text} + + ))} + + + {hiddenOptions.map((option, index) => ( + toggle(option.text)}> + {option.text} + + ))} + {hiddenOptions.length === 0 && No hidden fields} + + + ) +} + +render() +``` + +## Props + +### ActionList + + + + + inset children are offset (vertically and horizontally) from list edges.{' '} + full children are flush (vertically and horizontally) with list edges + + } + /> + + + AriaRole + } + description={ + <> + ARIA role describing the function of the list. listbox and{' '} + menu are a common values. + + } + /> + + + +### ActionList.Item + + + + + danger indicates that the item is destructive. + + } + /> + + + + AriaRole + } + description={ + <> + ARIA role describing the function of the item. option is a common value. + + } + /> + + + +### ActionList.LinkItem + + + + MDN + } + /> + + +### ActionList.LeadingVisual + + + + + + +### ActionList.TrailingVisual + + + + + + +### ActionList.Description + + + + + inline descriptions are positioned beside primary text. block{' '} + descriptions are positioned below primary text. + + } + /> + + + +### ActionList.Group + + + + + + + inline descriptions are positioned beside primary text. block{' '} + descriptions are positioned below primary text. + + } + /> + + Set selectionVariant at the group level. + + } + /> + AriaRole + } + description={ + <> + ARIA role describing the function of the list inside the group. listbox and{' '} + menu are a common values. + + } + /> + + + +## Status + + + +## Further reading + +- [Interface guidelines: Action List](https://primer.style/design/components/action-list) + +## Related components + +- [ActionMenu](/drafts/ActionMenu2) +- [DropdownMenu](/DropdownMenu) +- [SelectPanel](/SelectPanel) diff --git a/docs/content/drafts/ActionMenu2.mdx b/docs/content/drafts/ActionMenu2.mdx new file mode 100644 index 00000000000..4ccc9b3c5a3 --- /dev/null +++ b/docs/content/drafts/ActionMenu2.mdx @@ -0,0 +1,341 @@ +--- +componentId: action_menu2 +title: ActionMenu v2 +status: Alpha +source: https://github.com/primer/react/tree/main/src/ActionMenu +storybook: '/react/storybook?path=/story/composite-components-actionmenu2' +description: An ActionMenu is an ActionList-based component for creating a menu of actions that expands through a trigger button. +--- + +import {Box, Avatar} from '@primer/react' +import {ActionMenu, ActionList} from '@primer/react/drafts' +import {Props} from '../../src/props' + +
+ + + + Menu + + + + Copy link + ⌘C + + + Quote reply + ⌘Q + + + Edit comment + ⌘E + + + + Delete file + ⌘D + + + + + + +
+ +```js +import {ActionMenu} from '@primer/react/drafts' +``` + +
+ +## Examples + +### Minimal example + +`ActionMenu` ships with `ActionMenu.Button` which is an accessible trigger for the overlay. It's recommended to compose `ActionList` with `ActionMenu.Overlay` + +  + +```jsx live drafts + + Menu + + + + console.log('New file')}>New file + Copy link + Edit file + + Delete file + + + +``` + +### With a custom anchor + +You can choose to have a different _anchor_ for the Menu dependending on the application's context. + +  + +```jsx live drafts + + + + + + + + + + + + + + Rename + + + + + + Archive all cards + + + + + + Delete + + + + +``` + +### With Groups + +```jsx live drafts + + Open column menu + + + + + + + + + repo:github/memex,github/github + + + + + + + + + Table + + Information-dense table optimized for operations across teams + + + + + + + Board + Kanban-style board focused on visual states + + + + + + + + + Save sort and filters to current view + + + + + + Save sort and filters to new view + + + + + + + + + View settings + + + + + +``` + +### With selection + +Use `selectionVariant` on `ActionList` to create a menu with single or multiple selection. + +```javascript live noinline drafts +const fieldTypes = [ + {icon: TypographyIcon, name: 'Text'}, + {icon: NumberIcon, name: 'Number'}, + {icon: CalendarIcon, name: 'Date'}, + {icon: SingleSelectIcon, name: 'Single select'}, + {icon: IterationsIcon, name: 'Iteration'} +] + +const Example = () => { + const [selectedIndex, setSelectedIndex] = React.useState(1) + const selectedType = fieldTypes[selectedIndex] + + return ( + + + {selectedType.name} + + + + {fieldTypes.map((type, index) => ( + setSelectedIndex(index)}> + {type.name} + + ))} + + + + ) +} + +render() +``` + +### With External Anchor + +To create an anchor outside of the menu, you need to switch to controlled mode for the menu and pass it as `anchorRef` to `ActionMenu`. Make sure you add `aria-expanded` and `aria-haspopup` to the external anchor: + +```javascript live noinline drafts +const Example = () => { + const [open, setOpen] = React.useState(false) + const anchorRef = React.createRef() + + return ( + <> + + + + + + Copy link + Quote reply + Edit comment + + Delete file + + + + + ) +} + +render() +``` + +### With Overlay Props + +To create an anchor outside of the menu, you need to switch to controlled mode for the menu and pass it as `anchorRef` to `ActionMenu`: + +```javascript live noinline drafts +const handleEscape = () => alert('you hit escape!') + +render( + + Open Actions Menu + + + + Open current Codespace + + Your existing Codespace will be opened to its previous state, and you'll be asked to manually switch to + new-branch. + + ⌘O + + + Create new Codespace + + Create a brand new Codespace with a fresh image and checkout this branch. + + ⌘C + + + + +) +``` + +## Props / API reference + +### ActionMenu + +| Name | Type | Default | Description | +| :----------- | :----------------------------- | :-----: | :----------------------------------------------------------------------------------------------------------------------- | +| children\* | `React.ReactElement[]` | - | Required. Recommended: `ActionMenu.Button` or `ActionMenu.Anchor` with `ActionMenu.Overlay` | +| open | `boolean` | - | Optional. If defined, will control the open/closed state of the overlay. Must be used in conjuction with `onOpenChange`. | +| onOpenChange | `(open: boolean) => void` | - | Optional. If defined, will control the open/closed state of the overlay. Must be used in conjuction with `open`. | +| anchorRef | `React.RefObject` | - | Optional. Useful for defining an external anchor | + +### ActionMenu.Button + +| Type | Default | Description | +| :----------------------------------------------- | :-----: | :---------------------------------------------------------------------------------------------------------------- | +| [Button v2 props](/drafts/Button2#api-reference) | - | You can pass all of the props that you would pass to a [`Button`](/drafts/Button2) component like `variant`, `sx` | + +### ActionMenu.Anchor + +| Name | Type | Default | Description | +| :--------- | :------------------- | :-----: | :-------------------------------- | +| children\* | `React.ReactElement` | - | Required. Accepts a single child. | + +### ActionMenu.Overlay + +| Name | Type | Default | Description | +| :--------------------------------------- | :-------------------- | :-----------------: | :-------------------------------------------------------------------------------------------- | +| children\* | `React.ReactElement[] | React.ReactElement` | Required. Recommended: [`ActionList`](/drafts/ActionList2) | +| [OverlayProps](/Overlay#component-props) | - | - | Optional. Props to be spread on the internal [`AnchoredOverlay`](/AnchoredOverlay) component. | + +## Status + + + +## Further reading + +[Interface guidelines: Action List + Menu](https://primer.style/design/components/action-list) + +## Related components + +- [ActionList](/drafts/ActionList2) +- [SelectPanel](/SelectPanel) +- [Button](/drafts/Button2) diff --git a/docs/content/drafts/DropdownMenu2.mdx b/docs/content/drafts/DropdownMenu2.mdx index 47eba34b758..68cd522aa1e 100644 --- a/docs/content/drafts/DropdownMenu2.mdx +++ b/docs/content/drafts/DropdownMenu2.mdx @@ -7,8 +7,8 @@ storybook: '/react/storybook?path=/story/composite-components-dropdownmenu2' description: Use DropdownMenu to select a single option from a list of menu options. --- -import {Box, Avatar, ActionList} from '@primer/react' -import {DropdownMenu} from '@primer/react/drafts' +import {Box, Avatar} from '@primer/react' +import {DropdownMenu, ActionList} from '@primer/react/drafts' import {Props} from '../../src/props' import State from '../../components/State' import {CalendarIcon, IterationsIcon, NumberIcon, SingleSelectIcon, TypographyIcon} from '@primer/octicons-react' @@ -312,7 +312,7 @@ Use `DropdownMenu` to select an option from a small list. If you’re looking fo description={ <> Recommended:{' '} - + ActionList @@ -359,6 +359,6 @@ Use `DropdownMenu` to select an option from a small list. If you’re looking fo ## Related components -- [ActionList](/ActionList) -- [ActionMenu](/ActionMenu) +- [ActionList](/drafts/ActionList2) +- [ActionMenu](/ActionMenu2) - [SelectPanel](/SelectPanel) diff --git a/docs/src/@primer/gatsby-theme-doctocat/nav.yml b/docs/src/@primer/gatsby-theme-doctocat/nav.yml index 9adbb423ab3..8873d9c6db2 100644 --- a/docs/src/@primer/gatsby-theme-doctocat/nav.yml +++ b/docs/src/@primer/gatsby-theme-doctocat/nav.yml @@ -35,10 +35,6 @@ # url: /useOverlay - title: Components children: - - title: ActionList - url: /ActionList - - title: ActionMenu - url: /ActionMenu - title: Autocomplete url: /Autocomplete - title: Avatar @@ -147,6 +143,10 @@ url: /drafts/LinkButton - title: IconButton url: /drafts/IconButton + - title: ActionList v2 + url: /drafts/ActionList2 + - title: ActionMenu v2 + url: /drafts/ActionMenu2 - title: Deprecated children: - title: BorderBox @@ -165,7 +165,3 @@ url: /FormGroup - title: SelectMenu url: /deprecated/SelectMenu - - title: ActionList - url: /deprecated/ActionList - - title: ActionMenu - url: /deprecated/ActionMenu diff --git a/src/ActionList/Divider.tsx b/src/ActionList/Divider.tsx index e286448eb42..0513b60f1ce 100644 --- a/src/ActionList/Divider.tsx +++ b/src/ActionList/Divider.tsx @@ -1,29 +1,25 @@ import React from 'react' -import Box from '../Box' +import styled from 'styled-components' import {get} from '../constants' -import {Theme} from '../ThemeProvider' -import {SxProp, merge} from '../sx' + +export const StyledDivider = styled.div` + height: 1px; + background: ${get('colors.border.muted')}; + margin-top: calc(${get('space.2')} - 1px); + margin-bottom: ${get('space.2')}; +` /** * Visually separates `Item`s or `Group`s in an `ActionList`. */ - -export const Divider: React.FC = ({sx = {}}) => { - return ( -