Skip to content

Commit

Permalink
ActionMenu.Overlay for ActionMenu v2 (#1675)
Browse files Browse the repository at this point in the history
* steal props from overlay :D

* Alternate implementation with context

* add changelog
  • Loading branch information
siddharthkp committed Dec 6, 2021
1 parent 9a9da55 commit 2380b66
Show file tree
Hide file tree
Showing 10 changed files with 516 additions and 377 deletions.
5 changes: 5 additions & 0 deletions .changeset/actionmenu-overlay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/components': patch
---

ActionMenu v2: Added `ActionMenu.Overlay` which accepts props to customize the Menu overlay.
289 changes: 170 additions & 119 deletions docs/content/drafts/ActionMenu2.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: ActionMenu
title: ActionMenu v2
status: Alpha
source: https://github.com/primer/react/tree/main/src/ActionMenu
storybook: '/react/storybook?path=/story/composite-components-actionmenu2'
Expand All @@ -13,29 +13,30 @@ import {Props} from '../../src/props'
<br />

<Box sx={{border: '1px solid', borderColor: 'border.default', borderRadius: 2, padding: 6}}>
<ActionMenu>
<ActionMenu.Button>Menu</ActionMenu.Button>
<ActionList>
<ActionList.Item onSelect={() => onSelect('Copy link')}>
Copy link
<ActionList.TrailingVisual>⌘C</ActionList.TrailingVisual>
</ActionList.Item>
<ActionList.Item onSelect={() => onSelect('Quote reply')}>
Quote reply
<ActionList.TrailingVisual>⌘Q</ActionList.TrailingVisual>
</ActionList.Item>
<ActionList.Item onSelect={() => onSelect('Edit comment')}>
Edit comment
<ActionList.TrailingVisual>⌘E</ActionList.TrailingVisual>
</ActionList.Item>
<ActionList.Divider />
<ActionList.Item variant="danger" onSelect={() => onSelect('Delete file')}>
Delete file
<ActionList.TrailingVisual>⌘D</ActionList.TrailingVisual>
</ActionList.Item>
</ActionList>

</ActionMenu>
<ActionMenu>
<ActionMenu.Button>Menu</ActionMenu.Button>
<ActionMenu.Overlay>
<ActionList>
<ActionList.Item>
Copy link
<ActionList.TrailingVisual>⌘C</ActionList.TrailingVisual>
</ActionList.Item>
<ActionList.Item>
Quote reply
<ActionList.TrailingVisual>⌘Q</ActionList.TrailingVisual>
</ActionList.Item>
<ActionList.Item>
Edit comment
<ActionList.TrailingVisual>⌘E</ActionList.TrailingVisual>
</ActionList.Item>
<ActionList.Divider />
<ActionList.Item variant="danger">
Delete file
<ActionList.TrailingVisual>⌘D</ActionList.TrailingVisual>
</ActionList.Item>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
</Box>

<br />
Expand All @@ -50,7 +51,7 @@ import {ActionMenu} from '@primer/components/drafts'

### Minimal example

`ActionMenu` ships with `ActionMenu.Button` which is an accessible trigger for the overlay. It's recommended to compose `ActionList` with this component.
`ActionMenu` ships with `ActionMenu.Button` which is an accessible trigger for the overlay. It's recommended to compose `ActionList` with `ActionMenu.Overlay`

&nbsp;

Expand All @@ -62,13 +63,15 @@ render(
<ActionMenu>
<ActionMenu.Button>Menu</ActionMenu.Button>

<ActionList>
<ActionList.Item onSelect={event => console.log('New file')}>New file</ActionList.Item>
<ActionList.Item>Copy link</ActionList.Item>
<ActionList.Item>Edit file</ActionList.Item>
<ActionList.Divider />
<ActionList.Item variant="danger">Delete file</ActionList.Item>
</ActionList>
<ActionMenu.Overlay>
<ActionList>
<ActionList.Item onSelect={event => console.log('New file')}>New file</ActionList.Item>
<ActionList.Item>Copy link</ActionList.Item>
<ActionList.Item>Edit file</ActionList.Item>
<ActionList.Divider />
<ActionList.Item variant="danger">Delete file</ActionList.Item>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
)
```
Expand All @@ -91,26 +94,28 @@ render(
</ButtonInvisible>
</ActionMenu.Anchor>

<ActionList>
<ActionList.Item>
<ActionList.LeadingVisual>
<PencilIcon />
</ActionList.LeadingVisual>
Rename
</ActionList.Item>
<ActionList.Item>
<ActionList.LeadingVisual>
<ArchiveIcon />
</ActionList.LeadingVisual>
Archive all cards
</ActionList.Item>
<ActionList.Item variant="danger">
<ActionList.LeadingVisual>
<TrashIcon />
</ActionList.LeadingVisual>
Delete
</ActionList.Item>
</ActionList>
<ActionMenu.Overlay>
<ActionList>
<ActionList.Item>
<ActionList.LeadingVisual>
<PencilIcon />
</ActionList.LeadingVisual>
Rename
</ActionList.Item>
<ActionList.Item>
<ActionList.LeadingVisual>
<ArchiveIcon />
</ActionList.LeadingVisual>
Archive all cards
</ActionList.Item>
<ActionList.Item variant="danger">
<ActionList.LeadingVisual>
<TrashIcon />
</ActionList.LeadingVisual>
Delete
</ActionList.Item>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
)
```
Expand All @@ -125,59 +130,61 @@ render(
<ActionMenu>
<ActionMenu.Button>Open column menu</ActionMenu.Button>

<ActionList showDividers>
<ActionList.Group title="Live query">
<ActionList.Item>
<ActionList.LeadingVisual>
<SearchIcon />
</ActionList.LeadingVisual>
repo:github/memex,github/github
</ActionList.Item>
</ActionList.Group>
<ActionList.Divider />
<ActionList.Group title="Layout" variant="subtle">
<ActionList.Item>
<ActionList.LeadingVisual>
<NoteIcon />
</ActionList.LeadingVisual>
Table
<ActionList.Description variant="block">
Information-dense table optimized for operations across teams
</ActionList.Description>
</ActionList.Item>
<ActionList.Item role="listitem">
<ActionList.LeadingVisual>
<ProjectIcon />
</ActionList.LeadingVisual>
Board
<ActionList.Description variant="block">Kanban-style board focused on visual states</ActionList.Description>
</ActionList.Item>
</ActionList.Group>
<ActionList.Divider />
<ActionList.Group>
<ActionList.Item>
<ActionList.LeadingVisual>
<FilterIcon />
</ActionList.LeadingVisual>
Save sort and filters to current view
</ActionList.Item>
<ActionList.Item>
<ActionList.LeadingVisual>
<FilterIcon />
</ActionList.LeadingVisual>
Save sort and filters to new view
</ActionList.Item>
</ActionList.Group>
<ActionList.Divider />
<ActionList.Group>
<ActionList.Item>
<ActionList.LeadingVisual>
<GearIcon />
</ActionList.LeadingVisual>
View settings
</ActionList.Item>
</ActionList.Group>
</ActionList>
<ActionMenu.Overlay>
<ActionList showDividers>
<ActionList.Group title="Live query">
<ActionList.Item>
<ActionList.LeadingVisual>
<SearchIcon />
</ActionList.LeadingVisual>
repo:github/memex,github/github
</ActionList.Item>
</ActionList.Group>
<ActionList.Divider />
<ActionList.Group title="Layout" variant="subtle">
<ActionList.Item>
<ActionList.LeadingVisual>
<NoteIcon />
</ActionList.LeadingVisual>
Table
<ActionList.Description variant="block">
Information-dense table optimized for operations across teams
</ActionList.Description>
</ActionList.Item>
<ActionList.Item role="listitem">
<ActionList.LeadingVisual>
<ProjectIcon />
</ActionList.LeadingVisual>
Board
<ActionList.Description variant="block">Kanban-style board focused on visual states</ActionList.Description>
</ActionList.Item>
</ActionList.Group>
<ActionList.Divider />
<ActionList.Group>
<ActionList.Item>
<ActionList.LeadingVisual>
<FilterIcon />
</ActionList.LeadingVisual>
Save sort and filters to current view
</ActionList.Item>
<ActionList.Item>
<ActionList.LeadingVisual>
<FilterIcon />
</ActionList.LeadingVisual>
Save sort and filters to new view
</ActionList.Item>
</ActionList.Group>
<ActionList.Divider />
<ActionList.Group>
<ActionList.Item>
<ActionList.LeadingVisual>
<GearIcon />
</ActionList.LeadingVisual>
View settings
</ActionList.Item>
</ActionList.Group>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
)
```
Expand All @@ -201,13 +208,15 @@ const Example = () => {
</Button>

<ActionMenu open={open} onOpenChange={setOpen} anchorRef={anchorRef}>
<ActionList>
<ActionList.Item>Copy link</ActionList.Item>
<ActionList.Item>Quote reply</ActionList.Item>
<ActionList.Item>Edit comment</ActionList.Item>
<ActionList.Divider />
<ActionList.Item variant="danger">Delete file</ActionList.Item>
</ActionList>
<ActionMenu.Overlay>
<ActionList>
<ActionList.Item>Copy link</ActionList.Item>
<ActionList.Item>Quote reply</ActionList.Item>
<ActionList.Item>Edit comment</ActionList.Item>
<ActionList.Divider />
<ActionList.Item variant="danger">Delete file</ActionList.Item>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
</>
)
Expand All @@ -216,17 +225,52 @@ const Example = () => {
render(<Example />)
```

### 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
// import {ActionMenu, ActionList} from '@primer/components/drafts'
const {ActionMenu, ActionList} = drafts // ignore docs silliness; import like that ↑

const handleEscape = () => alert('you hit escape!')

render(
<ActionMenu>
<ActionMenu.Button>Open Actions Menu</ActionMenu.Button>
<ActionMenu.Overlay width="medium" onEscape={handleEscape}>
<ActionList>
<ActionList.Item>
Open current Codespace
<ActionList.Description variant="block">
Your existing Codespace will be opened to its previous state, and you&apos;ll be asked to manually switch to
new-branch.
</ActionList.Description>
<ActionList.TrailingVisual>⌘O</ActionList.TrailingVisual>
</ActionList.Item>
<ActionList.Item>
Create new Codespace
<ActionList.Description variant="block">
Create a brand new Codespace with a fresh image and checkout this branch.
</ActionList.Description>
<ActionList.TrailingVisual>⌘C</ActionList.TrailingVisual>
</ActionList.Item>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
)
```

## Props / API reference

### ActionMenu

| Name | Type | Default | Description |
| :----------- | :-------------------------------------------------- | :-----: | :----------------------------------------------------------------------------------------------------------------------- |
| children\* | `React.ReactElement[]` | - | Required. Recommended: `ActionMenu.Button` or `ActionMenu.Anchor` with [`ActionList`](/drafts/ActionList2) |
| 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<HTMLElement>` | - | Optional. Useful for defining an external anchor |
| overlayProps | [`Partial<OverlayProps>`](/Overlay#component-props) | - | Optional. Props to be spread on the internal [`AnchoredOverlay`](/AnchoredOverlay) component. |
| 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<HTMLElement>` | - | Optional. Useful for defining an external anchor |

### ActionMenu.Button

Expand All @@ -240,6 +284,13 @@ render(<Example />)
| :--------- | :------------------- | :-----: | :-------------------------------- |
| 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. |
## Further reading
[Interface guidelines: Action List + Menu](https://primer.style/design/components/action-list)
Expand Down
14 changes: 14 additions & 0 deletions src/ActionList2/ActionListContainerContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** This context can be used by components that compose ActionList inside a Menu */

import React from 'react'

type ContextProps = {
container?: string
listRole?: string
itemRole?: string
// This can be any function, we don't know anything about the arguments
// to be more specific here, this is as good as (...args: any[]) => unknown
// eslint-disable-next-line @typescript-eslint/ban-types
afterSelect?: Function
}
export const ActionListContainerContext = React.createContext<ContextProps>({})
Loading

0 comments on commit 2380b66

Please sign in to comment.