@workday/canvas-kit-docs 7.0.0-alpha.89-next.16 → 7.0.0-alpha.90-next.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/lib/specs.js +131 -0
- package/dist/es6/lib/specs.js +131 -0
- package/dist/mdx/7.0-MIGRATION-GUIDE.mdx +311 -1
- package/dist/mdx/changelog.stories.mdx +1 -0
- package/dist/mdx/preview-react/form-field/examples/Custom.tsx +3 -4
- package/dist/mdx/preview-react/menu/Menu.mdx +1 -1
- package/dist/mdx/preview-react/menu/examples/Icons.tsx +0 -1
- package/dist/mdx/react/collection/Collection.mdx +136 -0
- package/dist/mdx/react/collection/examples/Basic.tsx +12 -0
- package/dist/mdx/react/collection/examples/BasicVirtual.tsx +24 -0
- package/dist/mdx/react/collection/examples/DynamicItems.tsx +20 -0
- package/dist/mdx/react/collection/examples/IdentifiedItems.tsx +12 -0
- package/dist/mdx/react/collection/examples/MultiSelection.tsx +56 -0
- package/dist/mdx/react/collection/examples/RovingFocus.tsx +39 -0
- package/dist/mdx/react/collection/examples/Selection.tsx +58 -0
- package/dist/mdx/react/menu/Menu.mdx +123 -0
- package/dist/mdx/react/menu/examples/Basic.tsx +26 -0
- package/dist/mdx/react/menu/examples/ContextMenu.tsx +25 -0
- package/dist/mdx/react/menu/examples/Icons.tsx +41 -0
- package/dist/mdx/react/modal/examples/ReturnFocus.tsx +1 -1
- package/dist/mdx/react/popup/examples/InitialFocus.tsx +4 -2
- package/dist/mdx/react/popup/examples/NestedPopups.tsx +17 -17
- package/dist/mdx/react/popup/examples/RTL.tsx +6 -3
- package/dist/mdx/react/tabs/Tabs.mdx +4 -8
- package/dist/mdx/react/tabs/examples/DynamicTabs.tsx +4 -7
- package/dist/mdx/react/tabs/examples/HoistedModel.tsx +8 -8
- package/dist/mdx/react/tabs/examples/Icons.tsx +4 -4
- package/dist/mdx/react/tabs/examples/NamedTabs.tsx +10 -10
- package/dist/mdx/react/tabs/examples/OverflowTabs.tsx +2 -2
- package/dist/mdx/react/text-input/examples/Basic.tsx +3 -0
- package/package.json +7 -16
- package/ts3.5/dist/commonjs/index.d.ts +0 -4
- package/ts3.5/dist/commonjs/lib/Specifications.d.ts +0 -6
- package/ts3.5/dist/commonjs/lib/docs.d.ts +0 -5
- package/ts3.5/dist/commonjs/lib/specs.d.ts +0 -16
- package/ts3.5/dist/es6/index.d.ts +0 -4
- package/ts3.5/dist/es6/lib/Specifications.d.ts +0 -6
- package/ts3.5/dist/es6/lib/docs.d.ts +0 -5
- package/ts3.5/dist/es6/lib/specs.d.ts +0 -16
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {ListBox} from '@workday/canvas-kit-react/collection';
|
|
2
|
+
|
|
3
|
+
import Basic from './examples/Basic';
|
|
4
|
+
import DynamicItems from './examples/DynamicItems';
|
|
5
|
+
import BasicVirtual from './examples/BasicVirtual';
|
|
6
|
+
import IdentifiedItems from './examples/IdentifiedItems';
|
|
7
|
+
import RovingFocus from './examples/RovingFocus';
|
|
8
|
+
import Selection from './examples/Selection';
|
|
9
|
+
import MultiSelection from './examples/MultiSelection';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Canvas Kit Collection API
|
|
13
|
+
|
|
14
|
+
The Collection API is a system of models and behaviors for creating lists and grids. For example,
|
|
15
|
+
`Tabs` uses collection behaviors and so does `Menu`. The UI of each looks very different, but much
|
|
16
|
+
of the behavior is shared. The Collection API should be used if a component doesn't already exist to
|
|
17
|
+
satisfy your needs.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
yarn add @workday/canvas-kit-react
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### ListBox
|
|
28
|
+
|
|
29
|
+
The `ListBox` is a basic component that offers vertical rendering of a collection in the form of a
|
|
30
|
+
2-dimension list. It understands virtualization, rendering only visible items in the DOM while also
|
|
31
|
+
providing aria attributes to allow screen readers to still navigate virtual lists. The `ListBox`
|
|
32
|
+
contains a basic `ListBox.Item` that renders list items that render correctly with virtualization
|
|
33
|
+
and adds `aria-setsize` and `aria-posinset` for screen readers.
|
|
34
|
+
|
|
35
|
+
The `ListBox` is very basic and only adds enough functionality to render correctly. No additional
|
|
36
|
+
behaviors are added to navigate or select. React Hooks are provided to add this functionality and
|
|
37
|
+
are used by higher level components like `Menu` and `Menu.Item` which utilize `ListBox`.
|
|
38
|
+
|
|
39
|
+
<ArgsTable of={ListBox} />
|
|
40
|
+
|
|
41
|
+
#### Basic Example
|
|
42
|
+
|
|
43
|
+
The `ListBox` on its own isn't very useful. It registers each item with the model. The
|
|
44
|
+
`ListBox.Item` only uses the `useListItemRegister` hook which handles registration of static items
|
|
45
|
+
to the model. The `ListBox` uses `useListRenderItems` which handles rendering static items as well
|
|
46
|
+
as [Dynamic List](#dynamic-list) example).
|
|
47
|
+
|
|
48
|
+
<ExampleCodeBlock code={Basic} />
|
|
49
|
+
|
|
50
|
+
#### Identifying Items
|
|
51
|
+
|
|
52
|
+
A list item takes an optional `data-id` property that will be used to identify an item. Without a
|
|
53
|
+
`data-id`, the identifier will be the item's index when first registered. The basic example has a
|
|
54
|
+
`data-id` attribute that is a string representation of the index. Providing a `data-id` will
|
|
55
|
+
override to a value of your choosing. This identifier will be used by other hooks to identify the
|
|
56
|
+
item for selection, maintaining a cursor, or anything else.
|
|
57
|
+
|
|
58
|
+
<ExampleCodeBlock code={IdentifiedItems} />
|
|
59
|
+
|
|
60
|
+
#### Dynamic Items
|
|
61
|
+
|
|
62
|
+
The `ListBox` also handles a dynamic collection of items. Instead of providing each `ListBox.Item`
|
|
63
|
+
statically, provide a render function instead. The function is called with an `items` value that is
|
|
64
|
+
the same was what's provided to the model. By default, providing items will enable virtualization.
|
|
65
|
+
This example adds a `maxHeight` to ensure overflow. Virtualization uses absolute positioning of each
|
|
66
|
+
item, which could cause problems for popup menus. If your item count is low, pass
|
|
67
|
+
`shouldVirtualize={false}` to disable virtualization.
|
|
68
|
+
|
|
69
|
+
<ExampleCodeBlock code={DynamicItems} />
|
|
70
|
+
|
|
71
|
+
### List Model
|
|
72
|
+
|
|
73
|
+
The List model contains the the state and events necessary to track items, selection, and a cursor.
|
|
74
|
+
Various hooks can be used for a List model to create common behaviors associated with lists, such as
|
|
75
|
+
navigating a list with a keyboard, selection (single and multiple), and virtualization.
|
|
76
|
+
|
|
77
|
+
A list also has a "cursor". A cursor is often represented by focus, but it is not always a 1:1
|
|
78
|
+
mapping. Think of the cursor as the focus item within the list. If the list has browser focus, the
|
|
79
|
+
cursor will map to browser focus. Behaviors such as `useListRovingFocus` will map the cursor to the
|
|
80
|
+
active tab stop of the list. For more information, see
|
|
81
|
+
[Roving Tabindex](https://w3c.github.io/aria-practices/#kbd_roving_tabindex). `useListRovingFocus`
|
|
82
|
+
adds keyboard events that map to navigation events. A [Navigation Manager](#navigation-manager) is
|
|
83
|
+
used to map new cursor ids to these events. The `ListModel` takes an optional `navigation`
|
|
84
|
+
configuration to change the default navigation behavior. The default navigation manager is a
|
|
85
|
+
[wrappingNavigationManager](#wrappingnavigationmanager) meaning the cursor will wrap around the
|
|
86
|
+
beginning and the ends. The cursor also provides a [navigationManager](#navigationmanager) that does
|
|
87
|
+
not wrap. This is the default navigation for grids.
|
|
88
|
+
|
|
89
|
+
The cursor also adds the concept of `orientation` which defaults to `'vertical'`. A Tab list is an
|
|
90
|
+
example of a `'horizontal'` list.
|
|
91
|
+
|
|
92
|
+
### Grid
|
|
93
|
+
|
|
94
|
+
A Cursor List can become a grid if a `columnCount` is provided. The array of items is still flat,
|
|
95
|
+
but navigational controls are now in two dimensions instead of one. A one dimension list is
|
|
96
|
+
bidirectionally aware, but a grid will always
|
|
97
|
+
|
|
98
|
+
#### Navigation Manager
|
|
99
|
+
|
|
100
|
+
#### Roving Tabindex
|
|
101
|
+
|
|
102
|
+
The list system also includes a cursor that extends the list. A cursor is mostly used for focusing
|
|
103
|
+
items. The [roving tabindex](https://w3c.github.io/aria-practices/#kbd_roving_tabindex) is a
|
|
104
|
+
well-supported way to accomplish accessibility requirements for focusing items within a list. This
|
|
105
|
+
example shows how to use `useListRovingFocus`. This example uses the `ListBox` component, but the
|
|
106
|
+
default `ListBox.Item` is very basic. We have two options, we can either pass additional
|
|
107
|
+
functionality via `elemPropsHook` or by creating a new item using our elemProps hook primitives.
|
|
108
|
+
Both will be demonstrated. Creating a custom item is recommended if you create a custom component
|
|
109
|
+
and export it. Using `elemPropsHook` with `ListBox.Item` is recommended only for one-off instances.
|
|
110
|
+
|
|
111
|
+
You can either use the tab key for focus on an item or click on an item and then use the up/down
|
|
112
|
+
keys to navigation the list. By default, the list is set to wrap navigation using the
|
|
113
|
+
`wrappingNavigationManager`. Only a single item in the list is a focus stop that "roves" as the
|
|
114
|
+
up/down arrows are pressed.
|
|
115
|
+
|
|
116
|
+
**Note:** This example doesn't meet accessibility requirements. The list will have to have some type
|
|
117
|
+
of context. Like "navigation list" or "menu list".
|
|
118
|
+
|
|
119
|
+
<ExampleCodeBlock code={RovingFocus} />
|
|
120
|
+
|
|
121
|
+
#### Selection
|
|
122
|
+
|
|
123
|
+
Lists support selection. `useSelectionItem` is applied to an item which adds an `onClick` that adds
|
|
124
|
+
the item to the `state.selectedIds`. The default selection manager is a single select. This example
|
|
125
|
+
uses `ListBox` and creates a custom `SelectableItem` elemProps hook and component.
|
|
126
|
+
|
|
127
|
+
<ExampleCodeBlock code={Selection} />
|
|
128
|
+
|
|
129
|
+
#### Multiple Selection
|
|
130
|
+
|
|
131
|
+
Lists support selection. `useSelectionItem` is applied to an item which adds an `onClick` that adds
|
|
132
|
+
the item to the `state.selectedIds`. The default selection manager is a single select.
|
|
133
|
+
|
|
134
|
+
<ExampleCodeBlock code={MultiSelection} />
|
|
135
|
+
|
|
136
|
+
### Hooks
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {ListBox, useListItemRovingFocus} from '@workday/canvas-kit-react/collection';
|
|
4
|
+
|
|
5
|
+
interface Item {
|
|
6
|
+
id: string;
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const items: Item[] = Array(1000)
|
|
11
|
+
.fill(true)
|
|
12
|
+
.map((_, index) => ({id: String(index + 1), text: `Item - ${index + 1}`}));
|
|
13
|
+
|
|
14
|
+
export default () => {
|
|
15
|
+
return (
|
|
16
|
+
<ListBox items={items} maxHeight={300}>
|
|
17
|
+
{(item: Item) => (
|
|
18
|
+
<ListBox.Item data-id={item.id} elemPropsHook={useListItemRovingFocus}>
|
|
19
|
+
{item.text}
|
|
20
|
+
</ListBox.Item>
|
|
21
|
+
)}
|
|
22
|
+
</ListBox>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {ListBox} from '@workday/canvas-kit-react/collection';
|
|
4
|
+
|
|
5
|
+
interface Item {
|
|
6
|
+
id: string;
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const items: Item[] = Array(1000)
|
|
11
|
+
.fill(true)
|
|
12
|
+
.map((_, index) => ({id: String(index + 1), text: `Item - ${index + 1}`}));
|
|
13
|
+
|
|
14
|
+
export default () => {
|
|
15
|
+
return (
|
|
16
|
+
<ListBox items={items} maxHeight={300}>
|
|
17
|
+
{(item: Item) => <ListBox.Item data-id={item.id}>{item.text}</ListBox.Item>}
|
|
18
|
+
</ListBox>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {ListBox} from '@workday/canvas-kit-react/collection';
|
|
4
|
+
|
|
5
|
+
export default () => {
|
|
6
|
+
return (
|
|
7
|
+
<ListBox>
|
|
8
|
+
<ListBox.Item data-id="first">First</ListBox.Item>
|
|
9
|
+
<ListBox.Item data-id="second">Second</ListBox.Item>
|
|
10
|
+
</ListBox>
|
|
11
|
+
);
|
|
12
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
useListItemRegister,
|
|
5
|
+
useListItemRovingFocus,
|
|
6
|
+
useListItemSelect,
|
|
7
|
+
useListModel,
|
|
8
|
+
ListItemProps,
|
|
9
|
+
ListBox,
|
|
10
|
+
} from '@workday/canvas-kit-react/collection';
|
|
11
|
+
import {composeHooks, createSubcomponent} from '@workday/canvas-kit-react/common';
|
|
12
|
+
import {multiSelectionManager} from '../../lib/useSelectionListModel';
|
|
13
|
+
|
|
14
|
+
const useMultiSelectItem = composeHooks(
|
|
15
|
+
useListItemSelect,
|
|
16
|
+
useListItemRovingFocus,
|
|
17
|
+
useListItemRegister
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const Item = createSubcomponent('button')({
|
|
21
|
+
displayName: 'MultiSelectableItem',
|
|
22
|
+
modelHook: useListModel,
|
|
23
|
+
elemPropsHook: useMultiSelectItem,
|
|
24
|
+
})<ListItemProps>((elemProps, Element, model) => {
|
|
25
|
+
return (
|
|
26
|
+
<Element
|
|
27
|
+
role="listitem"
|
|
28
|
+
{...elemProps}
|
|
29
|
+
style={{
|
|
30
|
+
background: model.state.selectedIds.includes(elemProps['data-id']) ? 'gray' : 'white',
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export default () => {
|
|
37
|
+
const model = useListModel({
|
|
38
|
+
initialSelectedIds: ['first', 'second'],
|
|
39
|
+
selection: multiSelectionManager,
|
|
40
|
+
orientation: 'horizontal',
|
|
41
|
+
});
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<ListBox model={model}>
|
|
45
|
+
<Item data-id="first">First</Item>
|
|
46
|
+
<Item data-id="second">Second</Item>
|
|
47
|
+
<Item data-id="third">Third</Item>
|
|
48
|
+
</ListBox>
|
|
49
|
+
|
|
50
|
+
<p>Cursor ID: {model.state.cursorId}</p>
|
|
51
|
+
<p>
|
|
52
|
+
Selected IDs: {(model.state.selectedIds !== 'all' ? model.state.selectedIds : []).join(',')}
|
|
53
|
+
</p>
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
useListItemRegister,
|
|
5
|
+
useListItemRovingFocus,
|
|
6
|
+
useListModel,
|
|
7
|
+
ListBox,
|
|
8
|
+
ListItemProps,
|
|
9
|
+
} from '@workday/canvas-kit-react/collection';
|
|
10
|
+
import {composeHooks, createSubcomponent} from '@workday/canvas-kit-react/common';
|
|
11
|
+
|
|
12
|
+
// create our own hook using `useListItemRegister` and `useListItemRovingFocus`. Note the
|
|
13
|
+
// `useListItemRegister` must be the last hook when using `composeHooks`
|
|
14
|
+
const useRovingFocusItem = composeHooks(useListItemRovingFocus, useListItemRegister);
|
|
15
|
+
|
|
16
|
+
// create our own item. We use `modelHook` to define which model should be used and `elemPropsHook`
|
|
17
|
+
// to determine which elemProps hook should be used. `elemProps` will be populated with props to
|
|
18
|
+
// pass to the element
|
|
19
|
+
const RovingFocusItem = createSubcomponent('li')({
|
|
20
|
+
displayName: 'RovingFocusItem',
|
|
21
|
+
modelHook: useListModel,
|
|
22
|
+
elemPropsHook: useRovingFocusItem,
|
|
23
|
+
})<ListItemProps>((elemProps, Element) => {
|
|
24
|
+
return <Element {...elemProps} />;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default () => {
|
|
28
|
+
return (
|
|
29
|
+
<ListBox>
|
|
30
|
+
{/* We can use `ListBox.Item` and add `useListItemRovingFocus`. Useful for one-off */}
|
|
31
|
+
<ListBox.Item data-id="first" elemPropsHook={useListItemRovingFocus}>
|
|
32
|
+
First
|
|
33
|
+
</ListBox.Item>
|
|
34
|
+
{/* Use a custom item. Useful for reusing components */}
|
|
35
|
+
<RovingFocusItem data-id="second">Second</RovingFocusItem>
|
|
36
|
+
<RovingFocusItem data-id="third">Third</RovingFocusItem>
|
|
37
|
+
</ListBox>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
useListItemRegister,
|
|
5
|
+
useListItemRovingFocus,
|
|
6
|
+
useListItemSelect,
|
|
7
|
+
useListModel,
|
|
8
|
+
ListItemProps,
|
|
9
|
+
ListBox,
|
|
10
|
+
} from '@workday/canvas-kit-react/collection';
|
|
11
|
+
import {
|
|
12
|
+
composeHooks,
|
|
13
|
+
createElemPropsHook,
|
|
14
|
+
createSubcomponent,
|
|
15
|
+
} from '@workday/canvas-kit-react/common';
|
|
16
|
+
|
|
17
|
+
// Create a custom hook for our item
|
|
18
|
+
const useItem = composeHooks(
|
|
19
|
+
createElemPropsHook(useListModel)((model, ref, elemProps: ListItemProps) => {
|
|
20
|
+
return {
|
|
21
|
+
role: 'listitem',
|
|
22
|
+
style: {
|
|
23
|
+
background: model.state.selectedIds.includes(elemProps['data-id']) ? 'gray' : 'white',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}),
|
|
27
|
+
useListItemSelect,
|
|
28
|
+
useListItemRovingFocus,
|
|
29
|
+
useListItemRegister
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Create a custom item
|
|
33
|
+
const SelectableItem = createSubcomponent('button')({
|
|
34
|
+
displayName: 'SelectableItem',
|
|
35
|
+
modelHook: useListModel,
|
|
36
|
+
elemPropsHook: useItem,
|
|
37
|
+
})<ListItemProps>((elemProps, Element) => {
|
|
38
|
+
return <Element {...elemProps} />;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export default () => {
|
|
42
|
+
const model = useListModel({
|
|
43
|
+
initialSelectedIds: ['first'],
|
|
44
|
+
orientation: 'horizontal',
|
|
45
|
+
});
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<ListBox model={model}>
|
|
49
|
+
<SelectableItem data-id="first">First</SelectableItem>
|
|
50
|
+
<SelectableItem data-id="second">Second</SelectableItem>
|
|
51
|
+
<SelectableItem data-id="third">Third</SelectableItem>
|
|
52
|
+
</ListBox>
|
|
53
|
+
|
|
54
|
+
<p>Cursor ID: {model.state.cursorId}</p>
|
|
55
|
+
<p>Selected ID: {model.state.selectedIds[0]}</p>
|
|
56
|
+
</>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import {Specifications} from '@workday/canvas-kit-docs';
|
|
2
|
+
import {Menu} from '@workday/canvas-kit-react/menu';
|
|
3
|
+
|
|
4
|
+
import Basic from './examples/Basic';
|
|
5
|
+
import ContextMenu from './examples/ContextMenu';
|
|
6
|
+
import Icons from './examples/Icons';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Canvas Kit Menu
|
|
10
|
+
|
|
11
|
+
`Menu` displays a list of options when triggered by an action or UI element like an icon or button.
|
|
12
|
+
|
|
13
|
+
[> Workday Design Reference](https://design.workday.com/components/popups/menus)
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
yarn add @workday/canvas-kit-react
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Basic Example
|
|
24
|
+
|
|
25
|
+
`Menu` is typically triggered by an action such as pressing a button. The `Menu` comes with a
|
|
26
|
+
`Target` subcomponent and a Popup.
|
|
27
|
+
|
|
28
|
+
<ExampleCodeBlock code={Basic} />
|
|
29
|
+
|
|
30
|
+
`Menu` will automatically focus on the cursor item (first item by default). The `Menu` uses a menu
|
|
31
|
+
model which composes a list model and a popup model and sets up accessibility features for you.
|
|
32
|
+
|
|
33
|
+
`Menu` follows the
|
|
34
|
+
[Actions Menu pattern](https://www.w3.org/TR/wai-aria-practices/examples/menu-button/menu-button-actions.html)
|
|
35
|
+
using roving tabindex. Below is table of supported keyboard shortcuts and associated actions.
|
|
36
|
+
|
|
37
|
+
| Key | Action |
|
|
38
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------ |
|
|
39
|
+
| `Enter` or `Space` | Activates the menu item and then closes the menu |
|
|
40
|
+
| `Escape` | Closes the menu |
|
|
41
|
+
| `Up Arrow` | Moves focus to the previous menu item – if focus is on first menu item, it moves focus to the last menu item |
|
|
42
|
+
| `Down Arrow` | Moves focus to the next menu item – if focus is on last menu item, it moves focus to the first menu item |
|
|
43
|
+
| `Home` | Moves focus to the first menu item |
|
|
44
|
+
| `End` | Moves focus to the last menu item |
|
|
45
|
+
|
|
46
|
+
### Context Menu
|
|
47
|
+
|
|
48
|
+
<ExampleCodeBlock code={ContextMenu} />
|
|
49
|
+
|
|
50
|
+
### Icons
|
|
51
|
+
|
|
52
|
+
<ExampleCodeBlock code={Icons} />
|
|
53
|
+
|
|
54
|
+
## Components
|
|
55
|
+
|
|
56
|
+
### Menu
|
|
57
|
+
|
|
58
|
+
### Usage
|
|
59
|
+
|
|
60
|
+
`Menu` is a combination of as popup and a list. It usually has some type of target element that
|
|
61
|
+
expands/collapses the menu and a `menu` role and and several `menuitem` roles. Focus is managed uses
|
|
62
|
+
[roving tabindex](https://w3c.github.io/aria-practices/#kbd_roving_tabindex) for maximum
|
|
63
|
+
compatibility. A `Menu` can have two modes: `single` and `multiple`. This mode determines both how
|
|
64
|
+
many items can be selected as well as the default behavior when a menuitem is clicked. For the
|
|
65
|
+
`single` mode, selecting a `menuitem` will select and close the menu. For the `multiple` mode,
|
|
66
|
+
clicking a `menuitem` will toggle selection and will not close the menu.
|
|
67
|
+
|
|
68
|
+
#### Props
|
|
69
|
+
|
|
70
|
+
<ArgsTable of={Menu} />
|
|
71
|
+
|
|
72
|
+
#### Usage
|
|
73
|
+
|
|
74
|
+
Like all compound components, the `Menu` component is a container component. It is responsible for
|
|
75
|
+
putting a `MenuModel` in the React context for all the menu subcomponents. You can either pass this
|
|
76
|
+
component model config or a "hoisted" model.
|
|
77
|
+
|
|
78
|
+
With model config:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
<Menu mode="multiple">{/* Child components */}</Menu>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
With a hoisted model:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
const model = useMenuModel({
|
|
88
|
+
mode: 'multiple',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// we now have access to `model.state` and `model.events`
|
|
92
|
+
|
|
93
|
+
<Menu model={model}>{/* Child components */}</Menu>;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Menu.Target
|
|
97
|
+
|
|
98
|
+
#### Usage
|
|
99
|
+
|
|
100
|
+
`Menu.Target` is similar to all `Popup.Target` types. The component only provides behavior and no
|
|
101
|
+
styling. The `as` prop is used to determine which component is rendered. This component should
|
|
102
|
+
forward the `ref` and apply any additional props directly to an element. The default `as` is a
|
|
103
|
+
`SecondaryButton`. Any Canvas Kit component should work with an `as`.
|
|
104
|
+
|
|
105
|
+
An example changing to a `PrimaryButton`
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<Menu.Target as={PrimaryButton}>Primary Button Text</Menu.Target>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This element will apply `aria-haspopup` and `aria-expanded` to inform screen readers there's a popup
|
|
112
|
+
associated with the element.
|
|
113
|
+
|
|
114
|
+
#### Props
|
|
115
|
+
|
|
116
|
+
Undocumented props are spread to the element provided by the `as` which is a `SecondaryButton` by
|
|
117
|
+
default.
|
|
118
|
+
|
|
119
|
+
<ArgsTable of={Menu.Target} />
|
|
120
|
+
|
|
121
|
+
## Specifications
|
|
122
|
+
|
|
123
|
+
<Specifications file="Menu.spec.ts" name="Menu" />
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {Menu} from '@workday/canvas-kit-react/menu';
|
|
4
|
+
|
|
5
|
+
export default () => {
|
|
6
|
+
const [selected, setSelected] = React.useState('');
|
|
7
|
+
return (
|
|
8
|
+
<Menu onSelect={data => setSelected(data.id)}>
|
|
9
|
+
<Menu.Target>Open Menu</Menu.Target>
|
|
10
|
+
<Menu.Popper>
|
|
11
|
+
<Menu.Card>
|
|
12
|
+
<Menu.List>
|
|
13
|
+
<Menu.Item>First Item</Menu.Item>
|
|
14
|
+
<Menu.Item>Second Item</Menu.Item>
|
|
15
|
+
<Menu.Divider />
|
|
16
|
+
<Menu.Item>Third Item (with a really, really, really long label)</Menu.Item>
|
|
17
|
+
<Menu.Item>Fourth Item</Menu.Item>
|
|
18
|
+
</Menu.List>
|
|
19
|
+
</Menu.Card>
|
|
20
|
+
</Menu.Popper>
|
|
21
|
+
<p>
|
|
22
|
+
Selected: <span data-testid="output">{selected}</span>
|
|
23
|
+
</p>
|
|
24
|
+
</Menu>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {Menu} from '@workday/canvas-kit-react/menu';
|
|
4
|
+
|
|
5
|
+
export default () => {
|
|
6
|
+
const [selected, setSelected] = React.useState('');
|
|
7
|
+
return (
|
|
8
|
+
<Menu onSelect={data => setSelected(data.id)}>
|
|
9
|
+
<Menu.TargetContext>Open Menu</Menu.TargetContext>
|
|
10
|
+
<Menu.Popper>
|
|
11
|
+
<Menu.Card>
|
|
12
|
+
<Menu.List>
|
|
13
|
+
<Menu.Item>First Item</Menu.Item>
|
|
14
|
+
<Menu.Item>Second Item</Menu.Item>
|
|
15
|
+
<Menu.Item>Third Item (with a really, really, really long label)</Menu.Item>
|
|
16
|
+
<Menu.Item>Fourth Item</Menu.Item>
|
|
17
|
+
</Menu.List>
|
|
18
|
+
</Menu.Card>
|
|
19
|
+
</Menu.Popper>
|
|
20
|
+
<p>
|
|
21
|
+
Selected: <span data-testid="output">{selected}</span>
|
|
22
|
+
</p>
|
|
23
|
+
</Menu>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
setupIcon,
|
|
4
|
+
uploadCloudIcon,
|
|
5
|
+
userIcon,
|
|
6
|
+
taskContactIcon,
|
|
7
|
+
} from '@workday/canvas-system-icons-web';
|
|
8
|
+
import {Menu} from '@workday/canvas-kit-react/menu';
|
|
9
|
+
|
|
10
|
+
export default () => {
|
|
11
|
+
return (
|
|
12
|
+
<Menu>
|
|
13
|
+
<Menu.Card>
|
|
14
|
+
<Menu.List>
|
|
15
|
+
<Menu.Item>
|
|
16
|
+
<Menu.Item.Icon icon={uploadCloudIcon} />
|
|
17
|
+
<Menu.Item.Text>First Item</Menu.Item.Text>
|
|
18
|
+
</Menu.Item>
|
|
19
|
+
<Menu.Item>
|
|
20
|
+
<Menu.Item.Icon icon={setupIcon} />
|
|
21
|
+
<Menu.Item.Text>Second Item (with a really really really long label)</Menu.Item.Text>
|
|
22
|
+
</Menu.Item>
|
|
23
|
+
<Menu.Item aria-disabled>
|
|
24
|
+
<Menu.Item.Icon icon={uploadCloudIcon} />
|
|
25
|
+
<Menu.Item.Text>Third Item</Menu.Item.Text>
|
|
26
|
+
<Menu.Item.Icon icon={taskContactIcon} />
|
|
27
|
+
</Menu.Item>
|
|
28
|
+
<Menu.Item>
|
|
29
|
+
<Menu.Item.Icon icon={userIcon} />
|
|
30
|
+
<Menu.Item.Text></Menu.Item.Text>
|
|
31
|
+
</Menu.Item>
|
|
32
|
+
<Menu.Divider />
|
|
33
|
+
<Menu.Item>
|
|
34
|
+
<Menu.Item.Icon icon={taskContactIcon} />
|
|
35
|
+
<Menu.Item.Text>Fifth Item (with divider)</Menu.Item.Text>
|
|
36
|
+
</Menu.Item>
|
|
37
|
+
</Menu.List>
|
|
38
|
+
</Menu.Card>
|
|
39
|
+
</Menu>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import {Modal, useModalModel} from '@workday/canvas-kit-react/modal';
|
|
3
|
-
import {DeleteButton
|
|
3
|
+
import {DeleteButton} from '@workday/canvas-kit-react/button';
|
|
4
4
|
import {FormField} from '@workday/canvas-kit-react/form-field';
|
|
5
5
|
import {HStack, Box} from '@workday/canvas-kit-react/layout';
|
|
6
6
|
import {Select} from '@workday/canvas-kit-preview-react/select';
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
useInitialFocus,
|
|
9
9
|
useReturnFocus,
|
|
10
10
|
} from '@workday/canvas-kit-react/popup';
|
|
11
|
+
import {HStack} from '@workday/canvas-kit-react/layout';
|
|
11
12
|
|
|
12
13
|
export default () => {
|
|
13
14
|
const initialFocusRef = React.useRef(null);
|
|
@@ -29,11 +30,12 @@ export default () => {
|
|
|
29
30
|
<Popup.Heading>Confirmation</Popup.Heading>
|
|
30
31
|
<Popup.Body>
|
|
31
32
|
<p id="popup-message">Your message has been sent!</p>
|
|
32
|
-
|
|
33
|
+
</Popup.Body>
|
|
34
|
+
<HStack spacing="s">
|
|
33
35
|
<Popup.CloseButton ref={initialFocusRef} aria-describedby="popup-message">
|
|
34
36
|
OK
|
|
35
37
|
</Popup.CloseButton>
|
|
36
|
-
</
|
|
38
|
+
</HStack>
|
|
37
39
|
</Popup.Card>
|
|
38
40
|
</Popup.Popper>
|
|
39
41
|
</Popup>
|
|
@@ -37,24 +37,24 @@ export default () => {
|
|
|
37
37
|
<Popup.CloseIcon aria-label="Close" size="small" />
|
|
38
38
|
<Popup.Body>
|
|
39
39
|
<p>Contents of Popup 1</p>
|
|
40
|
-
<Popup model={popup2}>
|
|
41
|
-
<Popup.Target>Open Popup 2</Popup.Target>
|
|
42
|
-
<Popup.Popper>
|
|
43
|
-
<Popup.Card aria-label="Popup 2">
|
|
44
|
-
<Popup.CloseIcon aria-label="Close" size="small" />
|
|
45
|
-
<Popup.Body>
|
|
46
|
-
<p>Contents of Popup 2</p>
|
|
47
|
-
<HStack spacing="s">
|
|
48
|
-
<Popup.CloseButton as={Popup.CloseButton} model={popup1}>
|
|
49
|
-
Close Both (as)
|
|
50
|
-
</Popup.CloseButton>
|
|
51
|
-
<SecondaryButton {...closeBothProps}>Close Both (props)</SecondaryButton>
|
|
52
|
-
</HStack>
|
|
53
|
-
</Popup.Body>
|
|
54
|
-
</Popup.Card>
|
|
55
|
-
</Popup.Popper>
|
|
56
|
-
</Popup>
|
|
57
40
|
</Popup.Body>
|
|
41
|
+
<Popup model={popup2}>
|
|
42
|
+
<Popup.Target>Open Popup 2</Popup.Target>
|
|
43
|
+
<Popup.Popper>
|
|
44
|
+
<Popup.Card aria-label="Popup 2">
|
|
45
|
+
<Popup.CloseIcon aria-label="Close" size="small" />
|
|
46
|
+
<Popup.Body>
|
|
47
|
+
<p>Contents of Popup 2</p>
|
|
48
|
+
</Popup.Body>
|
|
49
|
+
<HStack spacing="s">
|
|
50
|
+
<Popup.CloseButton as={Popup.CloseButton} model={popup1}>
|
|
51
|
+
Close Both (as)
|
|
52
|
+
</Popup.CloseButton>
|
|
53
|
+
<SecondaryButton {...closeBothProps}>Close Both (props)</SecondaryButton>
|
|
54
|
+
</HStack>
|
|
55
|
+
</Popup.Card>
|
|
56
|
+
</Popup.Popper>
|
|
57
|
+
</Popup>
|
|
58
58
|
</Popup.Card>
|
|
59
59
|
</Popup.Popper>
|
|
60
60
|
</Popup>
|