Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
91786c0
ActionMenu docs
ktabors Aug 17, 2021
24f7b98
copy edits
ktabors Aug 17, 2021
21950fe
docs usage import cleanup
ktabors Aug 17, 2021
a2d802a
adding shouldflip to the direction align story
ktabors Aug 17, 2021
190314c
copy edit
ktabors Aug 17, 2021
4229c35
fixing an example
ktabors Aug 18, 2021
7f56bd5
Update Events section copy
ktabors Aug 26, 2021
ffde14d
example improvements
ktabors Aug 26, 2021
343ab02
Merge branch 'main' into actionmenu_docs
ktabors Oct 15, 2021
44d94a5
Update packages/@react-spectrum/menu/stories/ActionMenu.stories.tsx
ktabors Dec 10, 2021
f3bdc61
Merge branch 'main' into actionmenu_docs
ktabors Dec 14, 2021
deb8904
fixing a merge conflix mistake
ktabors Dec 14, 2021
3f20a70
adding an isQuiet example to docs
ktabors Dec 14, 2021
b976159
simplifying quiet example and changing order
ktabors Dec 15, 2021
ed06179
removing the deploy after version
ktabors Dec 16, 2021
bd77fce
Merge branch 'main' into actionmenu_docs
snowystinger Dec 22, 2021
2a76bc6
Merge branch 'main' into actionmenu_docs
snowystinger Dec 23, 2021
f6748c8
Merge branch 'main' into actionmenu_docs
snowystinger Dec 23, 2021
fe3f100
upgrading mdx comment style
ktabors Dec 23, 2021
c8c48b7
updating keys to match names
ktabors Jan 5, 2022
043bd5a
Merge branch 'main' into actionmenu_docs
snowystinger Jan 5, 2022
049ff65
adding open and disabled exampels to the docs
ktabors Jan 6, 2022
81489c3
Merge branch 'actionmenu_docs' of github.com:adobe/react-spectrum int…
ktabors Jan 6, 2022
9e83c40
Merge branch 'main' into actionmenu_docs
ktabors Jan 7, 2022
f85a269
Merge branch 'main' into actionmenu_docs
ktabors Jan 12, 2022
d3f5164
Merge branch 'main' into actionmenu_docs
snowystinger Jan 31, 2022
7176660
Merge branch 'main' into actionmenu_docs
ktabors Feb 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
313 changes: 313 additions & 0 deletions packages/@react-spectrum/menu/docs/ActionMenu.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
{/* Copyright 2022 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-spectrum/menu';
import {HeaderInfo, PropTable} from '@react-spectrum/docs';
import packageData from '@react-spectrum/menu/package.json';

```jsx import
import {ActionMenu, Item, Section} from '@react-spectrum/menu';
import Copy from '@spectrum-icons/workflow/Copy';
import Cut from '@spectrum-icons/workflow/Cut';
import {Flex} from '@react-spectrum/layout';
import Paste from '@spectrum-icons/workflow/Paste';
```

---
category: Collections
keywords: [action menu, dropdown]
---

# ActionMenu

<p>{docs.exports.ActionMenu.description}</p>

<HeaderInfo
packageData={packageData}
componentNames={['ActionMenu']}
sourceData={[
{type: 'Spectrum', url: 'https://spectrum.adobe.com/page/popover/'}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Menu points to popover, I assumed that is applicable.

]} />

## Example

```tsx example
<ActionMenu>
<Item>Cut</Item>
<Item>Copy</Item>
<Item>Paste</Item>
</ActionMenu>
```

## Content

ActionMenu follows the [Collection Components](../react-stately/collections.html) API,
accepting both static and dynamic collections. See [Menu](Menu.html#content) for further explanation.

```tsx example
function Example() {
let actionMenuItems = [
{name: 'Cut'},
{name: 'Copy'},
{name: 'Paste'},
{name: 'Select All'}
];

return (
<ActionMenu items={actionMenuItems}>
{item => <Item key={item.name}>{item.name}</Item>}
</ActionMenu>
);
}
```

### Trays
On mobile, ActionMenus automatically display in a tray instead of a popover to improve usability.

### Internationalization
In order to internationalize an ActionMenu, the content provided to all child items should be localized.
For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the ActionMenu is flipped.

## Events
ActionMenu supports the `onAction` prop, which is called when the user presses a menu item. It receives the key of the item as an argument, which can be used to perform the relevant action.

```tsx example
function Example() {
let [action, setAction] = React.useState('');

return (
<>
<ActionMenu onAction={setAction}>
<Item key="cut">Cut</Item>
<Item key="copy">Copy</Item>
<Item key="paste">Paste</Item>
</ActionMenu>
<p>Action: {action}</p>
</>
);
}
```

## Sections
ActionMenu supports sections in order to group options. Sections can be used by wrapping groups of Items in a `Section` component. Each `Section` takes a `title` and `key` prop.

### Static Items

```tsx example
<ActionMenu>
<Section title="File">
<Item key="new">New</Item>
<Item key="open">Open...</Item>
</Section>
<Section title="Save">
<Item key="save">Save</Item>
<Item key="saveAs">Save As...</Item>
<Item key="saveAll">Save All</Item>
</Section>
</ActionMenu>
```

### Dynamic Items

Sections used with dynamic items are populated from a hierarchical data structure. Similarly to the props on ActionMenu, `<Section>` takes an array of data using the `items` prop.

```tsx example
function Example() {
let openWindows = [
{
name: 'Reversion',
id: 'reversion',
children: [
{id: 'undo', name: 'Undo'},
{id: 'redo', name: 'Redo'}
]
},
{
name: 'Clipboard',
id: 'clipboard',
children: [
{id: 'cut', name: 'Cut'},
{id: 'copy', name: 'Copy'},
{id: 'paste', name: 'Paste'}
]
}
];

return (
<ActionMenu
items={openWindows}>
{item => (
<Section items={item.children} title={item.name}>
{item => <Item>{item.name}</Item>}
</Section>
)}
</ActionMenu>
);
}
```

### Accessibility

Sections without a `title` must provide an `aria-label` for accessibility.

## Complex Menu Items

Items within ActionMenu also allow for additional content used to better communicate options. Icons and descriptions can be added to the `children` of `Item` as shown in the example below. If a description is added, the prop `slot="description"` must be used to distinguish the different `<Text>` elements.

```tsx example
import {Keyboard, Text} from '@react-spectrum/text';
<ActionMenu>
<Item key="cut" textValue="cut">
<Cut size="S"/>
<Text>Cut</Text>
<Keyboard>⌘X</Keyboard>
</Item>
<Item key="copy" textValue="copy">
<Copy size="S"/>
<Text>Copy</Text>
<Keyboard>⌘C</Keyboard>
</Item>
<Item key="paste" textValue="paste">
<Paste size="S"/>
<Text>Paste</Text>
<Keyboard>⌘V</Keyboard>
</Item>
</ActionMenu>
```

## Props

<PropTable component={docs.exports.ActionMenu} links={docs.links} />

## Visual options

### Quiet

[View guidelines](https://spectrum.adobe.com/page/action-button/#Quiet)

```tsx example
<ActionMenu
isQuiet
items={[
{name: 'Cut', id: 'cut'},
{name: 'Copy', id: 'copy'},
{name: 'Paste', id: 'paste'}
]}>
{item => <Item>{item.name}</Item>}
</ActionMenu>
```

### Autofocus

Applying `autoFocus` to the ActionMenu sets focus to the ActionMenu trigger.

### Disabled

```tsx example
<ActionMenu
items={[
{name: 'Undo', id: 'undo'},
{name: 'Redo', id: 'redo'},
{name: 'Cut', id: 'cut'},
{name: 'Copy', id: 'copy'},
{name: 'Paste', id: 'paste'}
]}
isDisabled>
{item => <Item>{item.name}</Item>}
</ActionMenu>
```

### Disabled items

```tsx example
<ActionMenu
items={[
{name: 'Undo', id: 'undo'},
{name: 'Redo', id: 'redo'},
{name: 'Cut', id: 'cut'},
{name: 'Copy', id: 'copy'},
{name: 'Paste', id: 'paste'}
]}
disabledKeys={['redo', 'paste']}>
{item => <Item>{item.name}</Item>}
</ActionMenu>
```

### Align and direction

[View guidelines](https://spectrum.adobe.com/page/popover/#Placement)

The `align` prop aligns the ActionMenu menu relative to the trigger and the `direction` prop controls the direction the ActionMenu will render.

```tsx example
<Flex gap="size-100">
<ActionMenu align="start">
<Item key="cut">Cut</Item>
<Item key="copy">Copy</Item>
<Item key="paste">Paste</Item>
</ActionMenu>
<ActionMenu align="end" direction="top" shouldFlip={false}>
<Item key="cut">Cut</Item>
<Item key="copy">Copy</Item>
<Item key="paste">Paste</Item>
</ActionMenu>
<ActionMenu direction="start" align="start">
<Item key="cut">Cut</Item>
<Item key="copy">Copy</Item>
<Item key="paste">Paste</Item>
</ActionMenu>
<ActionMenu direction="end" align="end">
<Item key="cut">Cut</Item>
<Item key="copy">Copy</Item>
<Item key="paste">Paste</Item>
</ActionMenu>
</Flex>
```

### Flipping
By default, the ActionMenu menu flips direction automatically upon opening when space is limited. To change this, set the `shouldFlip` prop to `false`. Try scrolling the viewport close to the edge of the trigger in the example to see this in action.

```tsx example
<Flex gap="size-100">
<ActionMenu shouldFlip>
<Item key="cut">Cut</Item>
<Item key="copy">Copy</Item>
<Item key="paste">Paste</Item>
</ActionMenu>
<ActionMenu shouldFlip={false}>
<Item key="cut">Cut</Item>
<Item key="copy">Copy</Item>
<Item key="paste">Paste</Item>
</ActionMenu>
</Flex>
```

### Open

The `isOpen` and `defaultOpen` props on the ActionMenu control whether the menu is open by default.
They apply controlled and uncontrolled behavior on the Menu respectively.

```tsx example
function Example() {
let [open, setOpen] = React.useState(false);

return (
<ActionMenu
isOpen={open}
onOpenChange={setOpen}>
<Item key="cut">Cut</Item>
<Item key="copy">Copy</Item>
<Item key="paste">Paste</Item>
</ActionMenu>
);
}
```
2 changes: 1 addition & 1 deletion packages/@react-spectrum/menu/docs/Menu.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ function Example() {
On mobile, Menus automatically display in a tray instead of a popover to improve usability.

### Internationalization
In order to internationalize an Menu, the content provided to all child items should be localized.
In order to internationalize a Menu, the content provided to all child items should be localized.
For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the Menu is flipped.

## Events
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/menu/docs/MenuTrigger.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,4 @@ function Example() {
</MenuTrigger>
);
}
```
2 changes: 1 addition & 1 deletion packages/@react-spectrum/menu/src/ActionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function ActionMenu<T extends object>(props: SpectrumActionMenuProps<T>, ref: Fo
}

/**
* Convenience component to display an ActionButton with a Menu.
* ActionMenu combines an ActionButton with a Menu for simple "more actions" use cases.
*/
let _ActionMenu = React.forwardRef(ActionMenu);
export {_ActionMenu as ActionMenu};
11 changes: 9 additions & 2 deletions packages/@react-spectrum/menu/stories/ActionMenu.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import {action} from '@storybook/addon-actions';
import {ActionMenu} from '..';
import {Alignment} from '@react-types/shared';
import {Checkbox} from '@react-spectrum/checkbox';
import {Flex} from '../../layout';
import {Item} from '../';
import {Meta, Story} from '@storybook/react';
Expand Down Expand Up @@ -84,6 +85,7 @@ function isOfAlignment(key: string): key is Alignment {
function DirectionAlignment() {
const [align, setAlignment] = useState<Alignment>('start');
const [direction, setDirection] = useState<Direction>('bottom');
const [shouldFlip, setShouldFlip] = useState(true);

const handleAlignChange = (key) => {
if (isOfAlignment(key)) {
Expand All @@ -104,10 +106,12 @@ function DirectionAlignment() {
<Picker label="Direction" items={directionItems} selectedKey={direction} onSelectionChange={handleDirectionChange}>
{(item) => <Item key={item.key}>{item.label}</Item>}
</Picker>
<Checkbox isSelected={shouldFlip} onChange={setShouldFlip}>Should Flip</Checkbox>
<ActionMenu
onAction={action('action')}
align={align}
direction={direction}>
direction={direction}
shouldFlip={shouldFlip}>
<Item key="one">One</Item>
<Item key="two">Two</Item>
<Item key="three">Three</Item>
Expand All @@ -130,6 +134,9 @@ Quiet.args = {isQuiet: true};
export const Disabled = Template().bind({});
Disabled.args = {isDisabled: true};

export const DisabledKeys = Template().bind({});
DisabledKeys.args = {disabledKeys: ['two']};

export const AutoFocus = Template().bind({});
AutoFocus.args = {autoFocus: true};

Expand All @@ -151,7 +158,7 @@ export const ControlledOpen = () => {
);
};

export const DirectionAlign = () => <DirectionAlignment />;
export const DirectionAlignFlip = () => <DirectionAlignment />;

export const WithTooltip = () => (
<TooltipTrigger delay={0}>
Expand Down