@splunk/react-ui 5.7.0 → 5.8.0
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/Accordion.js +6 -6
- package/Box.js +83 -34
- package/CHANGELOG.md +34 -0
- package/CollapsiblePanel.js +11 -11
- package/ComboBox.js +31 -27
- package/ControlGroup.js +92 -91
- package/DefinitionList.js +9 -9
- package/Drawer.d.ts +2 -0
- package/Drawer.js +679 -0
- package/DualListbox.js +1 -1
- package/JSONTree.js +73 -72
- package/Link.js +2 -2
- package/MIGRATION.md +10 -0
- package/Menu.js +338 -240
- package/Modal.js +127 -109
- package/Multiselect.js +437 -351
- package/Paginator.js +14 -12
- package/Popover.js +4 -1
- package/README.md +11 -0
- package/RadioBar.js +1 -1
- package/Search.js +103 -88
- package/Select.js +42 -40
- package/SelectBase.js +374 -328
- package/SidePanel.js +346 -167
- package/SlidingPanels.js +11 -11
- package/StepBar.js +7 -7
- package/Switch.js +5 -5
- package/Text.js +24 -24
- package/TextArea.js +7 -7
- package/TransitionOpen.js +204 -185
- package/docs-llm/Accordion.md +267 -0
- package/docs-llm/Anchor Menu.md +115 -0
- package/docs-llm/Anchor.md +54 -0
- package/docs-llm/AnimationToggle.md +254 -0
- package/docs-llm/Avatar.md +298 -0
- package/docs-llm/Badge.md +212 -0
- package/docs-llm/Breadcrumbs.md +306 -0
- package/docs-llm/Button Group.md +53 -0
- package/docs-llm/Button.md +361 -0
- package/docs-llm/Card Layout.md +286 -0
- package/docs-llm/Card.md +619 -0
- package/docs-llm/Checkbox.md +218 -0
- package/docs-llm/Chip.md +291 -0
- package/docs-llm/Clickable.md +160 -0
- package/docs-llm/Code.md +292 -0
- package/docs-llm/Collapsible Panel.md +744 -0
- package/docs-llm/Color.md +253 -0
- package/docs-llm/Column Layout.md +391 -0
- package/docs-llm/Combo Box.md +540 -0
- package/docs-llm/Control Group.md +594 -0
- package/docs-llm/Date.md +270 -0
- package/docs-llm/Definition List.md +278 -0
- package/docs-llm/Divider.md +216 -0
- package/docs-llm/Drawer.md +414 -0
- package/docs-llm/Dropdown.md +472 -0
- package/docs-llm/Dual Listbox.md +325 -0
- package/docs-llm/File.md +653 -0
- package/docs-llm/Form Rows.md +374 -0
- package/docs-llm/Heading.md +179 -0
- package/docs-llm/Image.md +109 -0
- package/docs-llm/JSON Tree.md +260 -0
- package/docs-llm/Layer.md +74 -0
- package/docs-llm/Layout.md +50 -0
- package/docs-llm/Link.md +318 -0
- package/docs-llm/List.md +189 -0
- package/docs-llm/Markdown.md +179 -0
- package/docs-llm/Menu.md +735 -0
- package/docs-llm/Message Bar.md +236 -0
- package/docs-llm/Message.md +248 -0
- package/docs-llm/Modal.md +443 -0
- package/docs-llm/Monogram.md +159 -0
- package/docs-llm/Multiselect.md +937 -0
- package/docs-llm/Number.md +298 -0
- package/docs-llm/Paginator.md +395 -0
- package/docs-llm/Paragraph.md +148 -0
- package/docs-llm/Phone Number.md +254 -0
- package/docs-llm/Popover.md +166 -0
- package/docs-llm/Progress.md +141 -0
- package/docs-llm/Radio Bar.md +303 -0
- package/docs-llm/Radio List.md +350 -0
- package/docs-llm/Resize.md +362 -0
- package/docs-llm/Screen Reader Content.md +73 -0
- package/docs-llm/Scroll Container Context.md +155 -0
- package/docs-llm/Scroll.md +152 -0
- package/docs-llm/Search.md +381 -0
- package/docs-llm/Select.md +985 -0
- package/docs-llm/Side Panel.md +777 -0
- package/docs-llm/Slider.md +339 -0
- package/docs-llm/Sliding Panels.md +340 -0
- package/docs-llm/Split Button.md +295 -0
- package/docs-llm/Static Content.md +90 -0
- package/docs-llm/Step Bar.md +292 -0
- package/docs-llm/Switch.md +268 -0
- package/docs-llm/Tab Bar.md +439 -0
- package/docs-llm/Tab Layout.md +398 -0
- package/docs-llm/Table.md +2642 -0
- package/docs-llm/Text Area.md +253 -0
- package/docs-llm/Text.md +339 -0
- package/docs-llm/Tooltip.md +325 -0
- package/docs-llm/Transition Open.md +406 -0
- package/docs-llm/Tree.md +586 -0
- package/docs-llm/Typography.md +125 -0
- package/docs-llm/Wait Spinner.md +121 -0
- package/docs-llm/llms.txt +97 -0
- package/package.json +6 -5
- package/types/src/Box/Box.d.ts +2 -10
- package/types/src/Drawer/Body.d.ts +17 -0
- package/types/src/Drawer/Drawer.d.ts +114 -0
- package/types/src/Drawer/DrawerContext.d.ts +11 -0
- package/types/src/Drawer/Footer.d.ts +25 -0
- package/types/src/Drawer/Header.d.ts +41 -0
- package/types/src/Drawer/docs/examples/Basic.d.ts +6 -0
- package/types/src/Drawer/docs/examples/ContainerPosition.d.ts +7 -0
- package/types/src/Drawer/docs/examples/InitialFocus.d.ts +9 -0
- package/types/src/Drawer/docs/examples/InlinePosition.d.ts +7 -0
- package/types/src/Drawer/docs/examples/PagePosition.d.ts +7 -0
- package/types/src/Drawer/index.d.ts +2 -0
- package/types/src/JSONTree/JSONTree.d.ts +12 -5
- package/types/src/JSONTree/renderTreeItems.d.ts +2 -1
- package/types/src/Menu/Item.d.ts +2 -1
- package/types/src/Menu/docs/examples/SelectableCheckbox.d.ts +7 -0
- package/types/src/Modal/Modal.d.ts +1 -2
- package/types/src/Select/Option.d.ts +6 -3
- package/types/src/Select/Select.d.ts +8 -5
- package/types/src/Select/docs/examples/Dimmed.d.ts +7 -0
- package/types/src/SelectBase/OptionBase.d.ts +6 -3
- package/types/src/SelectBase/SelectBase.d.ts +8 -3
- package/types/src/SidePanel/SidePanel.d.ts +43 -2
- package/types/src/SidePanel/docs/examples/DockLayout.d.ts +17 -0
- package/types/src/SidePanel/docs/examples/InitialFocus.d.ts +9 -0
- package/types/src/TransitionOpen/TransitionOpen.d.ts +29 -4
- package/types/src/useKeyPress/index.d.ts +9 -2
- package/types/src/useOnClickOutside/index.d.ts +2 -0
- package/types/src/useOnClickOutside/useOnClickOutside.d.ts +4 -0
- package/useKeyPress.js +23 -18
- package/useOnClickOutside.d.ts +2 -0
- package/useOnClickOutside.js +79 -0
- package/types/src/RadioList/docs/examples/Row.d.ts +0 -6
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# Dual Listbox
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
> Image: Illustration of a dual listbox component
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## When to use this component
|
|
13
|
+
- When you need to manage or transfer content between two lists.
|
|
14
|
+
- A clear visual distinction between available and selected items is required.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## When to use another component
|
|
19
|
+
- Consider a `Radio List` when only one item from a list needs to be selected
|
|
20
|
+
- When space is extremely limited and a compact selection is needed, use `Multiselect`
|
|
21
|
+
- Consider `Table` or `List` when representing hierarchical data or key-value pairs
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
```mermaid
|
|
25
|
+
graph TD
|
|
26
|
+
accDescr: Decision tree that guides on when to use the DualListbox component or something else
|
|
27
|
+
A(Do you need to represent hierarchical data?) -- Yes --- B(Table or List)
|
|
28
|
+
A -- No --- C(Do you need to select many items?)
|
|
29
|
+
C -- Yes --- D(Do you need to transfer items between lists?)
|
|
30
|
+
D -- Yes --- E(Dual listbox)
|
|
31
|
+
D -- No --- F(Multiselect)
|
|
32
|
+
C -- No --- G(Radio list)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Check out
|
|
36
|
+
- [Radio List][1]
|
|
37
|
+
- [Multiselect][2]
|
|
38
|
+
- [Table] [3]
|
|
39
|
+
- [List][4]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
### Use descriptive names for each list box when applicable.
|
|
44
|
+
The default names for each list box are “Source” and “Target”, but they can be changed to accurately reflect the data within each list.
|
|
45
|
+
|
|
46
|
+
> Image: The image shows a do-and-don’t comparison between two Dual Listboxes. The image with the heart eyes emoji shows a Dual Listbox with a source label of
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Use a controlled dual list box for bulk transfers.
|
|
50
|
+
If it is anticipated that the user will be moving numerous items, a controlled dual listbox allows users to revert changes, providing greater control and preventing accidental bulk modifications.
|
|
51
|
+
|
|
52
|
+
> Image: The image shows a Dual Listbox where the left list is labeled
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
### Confirming high-impact changes
|
|
56
|
+
If the dual list box will create major changes in the page or workflow, include a confirmation button, such as “Apply” or “Save” so users understand that their changes will be finalized.
|
|
57
|
+
|
|
58
|
+
> Image: The image shows a Dual Listbox where the left list is labeled ‘Available countries’ and the right is labeled ‘Enabled countries. A button labeled ‘Apply’ is underneath the Dual Listbox component to allow users to confirm the choices they made.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
### Modal placement for expanded space
|
|
63
|
+
When limited space prevents the effective display of a dual listbox, consider placing it within a modal. Ensure the modal includes a clear confirmation button.
|
|
64
|
+
|
|
65
|
+
> Image: The image shows a Dual Listbox inside of a Modal component titled ‘Publish components’. The left list is labeled ‘Available components’ and the right list is labeled ‘Released components’. The Modal footer has two buttons; the left one says ‘Cancel’ and the right one says ‘Apply’.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
[1]: ./RadioList
|
|
69
|
+
[2]: ./Multiselect
|
|
70
|
+
[3]: ./Table
|
|
71
|
+
[4]: ./List
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
### Uncontrolled
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import React from 'react';
|
|
80
|
+
|
|
81
|
+
import DualListbox, { Option } from '@splunk/react-ui/DualListbox';
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
function Uncontrolled() {
|
|
85
|
+
return (
|
|
86
|
+
<DualListbox
|
|
87
|
+
lists={[
|
|
88
|
+
{ name: 'list-1', label: 'Source' },
|
|
89
|
+
{ name: 'list-2', label: 'Target' },
|
|
90
|
+
]}
|
|
91
|
+
>
|
|
92
|
+
<Option id="option-0" label="Alpha" value="alpha" />
|
|
93
|
+
<Option id="option-1" label="Beta" value="beta" />
|
|
94
|
+
<Option id="option-2" label="Gamma" value="gamma" />
|
|
95
|
+
<Option id="option-3" label="Delta" value="delta" />
|
|
96
|
+
<Option id="option-4" label="Epsilon" value="epsilon" />
|
|
97
|
+
</DualListbox>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default Uncontrolled;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
### optionValues
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import React, { useState } from 'react';
|
|
110
|
+
|
|
111
|
+
import Button from '@splunk/react-ui/Button';
|
|
112
|
+
import DualListbox, {
|
|
113
|
+
Option,
|
|
114
|
+
DualListboxChangeHandler,
|
|
115
|
+
DualListboxSelectHandler,
|
|
116
|
+
} from '@splunk/react-ui/DualListbox';
|
|
117
|
+
import ScreenReaderContent from '@splunk/react-ui/ScreenReaderContent';
|
|
118
|
+
|
|
119
|
+
const optionValues = ['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon'];
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
const Controlled = () => {
|
|
123
|
+
const [twoValues, setTwoValues] = useState<string[]>([]);
|
|
124
|
+
const [oneSelectedValues, setOneSelectedValues] = useState<string[]>([]);
|
|
125
|
+
const [twoSelectedValues, setTwoSelectedValues] = useState<string[]>([]);
|
|
126
|
+
const [screenReaderMessage, setScreenReaderMessage] = useState<string>('');
|
|
127
|
+
|
|
128
|
+
const handleChange: DualListboxChangeHandler = (
|
|
129
|
+
event,
|
|
130
|
+
{ targetListName, targetValues, sourceValues, screenReaderMessage: message }
|
|
131
|
+
) => {
|
|
132
|
+
if (targetListName === 'list-1') {
|
|
133
|
+
setTwoSelectedValues([]);
|
|
134
|
+
setTwoValues(sourceValues);
|
|
135
|
+
} else {
|
|
136
|
+
setOneSelectedValues([]);
|
|
137
|
+
setTwoValues(targetValues);
|
|
138
|
+
}
|
|
139
|
+
setScreenReaderMessage(message);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const handleSelect: DualListboxSelectHandler = (event, { values, listName }) => {
|
|
143
|
+
if (listName === 'list-1') {
|
|
144
|
+
setOneSelectedValues(values);
|
|
145
|
+
} else {
|
|
146
|
+
setTwoSelectedValues(values);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const handleResetClick = () => {
|
|
151
|
+
setOneSelectedValues([]);
|
|
152
|
+
setTwoSelectedValues([]);
|
|
153
|
+
setTwoValues([]);
|
|
154
|
+
// If the state of the DualListbox is being altered externally, it needs to be messaged.
|
|
155
|
+
setScreenReaderMessage('Source and Target listbox values reset');
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const twoSet = new Set(twoValues);
|
|
159
|
+
const twoSelectedSet = new Set(twoSelectedValues);
|
|
160
|
+
const oneSelectedSet = new Set(oneSelectedValues);
|
|
161
|
+
|
|
162
|
+
const options = optionValues.map((value) => {
|
|
163
|
+
const isList2Item = twoSet.has(value);
|
|
164
|
+
let isSelected = false;
|
|
165
|
+
if (isList2Item) {
|
|
166
|
+
isSelected = twoSelectedSet.has(value);
|
|
167
|
+
} else {
|
|
168
|
+
isSelected = oneSelectedSet.has(value);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<Option
|
|
173
|
+
id={`controlled-id-${value}`}
|
|
174
|
+
key={value}
|
|
175
|
+
label={value}
|
|
176
|
+
value={value}
|
|
177
|
+
selected={isSelected}
|
|
178
|
+
listName={isList2Item ? 'list-2' : 'list-1'}
|
|
179
|
+
/>
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<>
|
|
185
|
+
<Button label="Reset lists" onClick={handleResetClick} />
|
|
186
|
+
<DualListbox
|
|
187
|
+
controlled
|
|
188
|
+
onChange={handleChange}
|
|
189
|
+
onSelect={handleSelect}
|
|
190
|
+
lists={[
|
|
191
|
+
{ name: 'list-1', label: 'Source' },
|
|
192
|
+
{ name: 'list-2', label: 'Target' },
|
|
193
|
+
]}
|
|
194
|
+
>
|
|
195
|
+
{options}
|
|
196
|
+
</DualListbox>
|
|
197
|
+
{}
|
|
198
|
+
<ScreenReaderContent aria-live="assertive">{screenReaderMessage}</ScreenReaderContent>
|
|
199
|
+
</>
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export default Controlled;
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
### Fill
|
|
209
|
+
|
|
210
|
+
Dynamic size example.
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import React from 'react';
|
|
214
|
+
|
|
215
|
+
import DualListbox, { Option } from '@splunk/react-ui/DualListbox';
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
function Fill() {
|
|
219
|
+
return (
|
|
220
|
+
<div
|
|
221
|
+
style={{
|
|
222
|
+
height: '500px',
|
|
223
|
+
position: 'relative',
|
|
224
|
+
width: '800px',
|
|
225
|
+
}}
|
|
226
|
+
>
|
|
227
|
+
<DualListbox
|
|
228
|
+
fill
|
|
229
|
+
lists={[
|
|
230
|
+
{ name: 'list-1', label: 'Source' },
|
|
231
|
+
{ name: 'list-2', label: 'Target' },
|
|
232
|
+
]}
|
|
233
|
+
>
|
|
234
|
+
<Option id="option-0" label="Alpha" value="alpha" />
|
|
235
|
+
<Option id="option-1" label="Beta" value="beta" />
|
|
236
|
+
<Option id="option-2" label="Gamma" value="gamma" />
|
|
237
|
+
<Option id="option-3" label="Delta" value="delta" />
|
|
238
|
+
<Option id="option-4" label="Epsilon" value="epsilon" />
|
|
239
|
+
</DualListbox>
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export default Fill;
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
## API
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
### DualListbox API
|
|
254
|
+
|
|
255
|
+
#### Props
|
|
256
|
+
|
|
257
|
+
| Name | Type | Required | Default | Description |
|
|
258
|
+
|------|------|------|------|------|
|
|
259
|
+
| children | React.ReactNode | no | | All children must be instances of `DualListbox.Option`. |
|
|
260
|
+
| controlled | boolean | no | false | When true, `Options`'s `listName` and `selected` state props are fully controlled. |
|
|
261
|
+
| elementRef | React.Ref<HTMLDivElement> | no | | A React ref which is set to the DOM element when the component mounts, and null when it unmounts. |
|
|
262
|
+
| fill | boolean | no | false | When true, fill height and width of the relative parent container. |
|
|
263
|
+
| inline | boolean | no | false | When false, display as inline-block with the default width. Ignored if `fill=true` set. |
|
|
264
|
+
| lists | ListboxConfig[] | yes | | List identifiers. `name` should map to child `Option`s `listName` prop, and will be returned with event calls. `label` will be used for visual and assistive text. |
|
|
265
|
+
| onChange | DualListboxChangeHandler | no | | Callback for selected options moving from one list to another. |
|
|
266
|
+
| onSelect | DualListboxSelectHandler | no | | Callback for single selected/de-select actions. |
|
|
267
|
+
|
|
268
|
+
#### Types
|
|
269
|
+
|
|
270
|
+
| Name | Type | Description |
|
|
271
|
+
|------|------|------|
|
|
272
|
+
| DualListboxChangeHandler | ( event: \| React.MouseEvent<HTMLButtonElement> \| React.MouseEvent<HTMLLIElement> \| React.KeyboardEvent<HTMLUListElement>, data: { /** * The values being changed. */ affectedValues: string[]; /** * The a11y text describing the move action. * If `controlled="true"` the presentation of the text MUST be implemented by the consumer. */ screenReaderMessage: string; /** * The list `name` associated to the source of the move action. */ sourceListName: string; /** * The values now contained within the source list. */ sourceValues: string[]; /** * The list `name` associated to the target of the move action. */ targetListName: string; /** * The values now contained within the target list. */ targetValues: string[]; } ) => void | |
|
|
273
|
+
| DualListboxSelectHandler | ( event: \| React.MouseEvent<HTMLLIElement> \| React.KeyboardEvent<HTMLUListElement> \| React.ChangeEvent<HTMLInputElement>, data: { /** * The list `name` associated to the batch action. */ listName: string; /** * The values marked as selected with the given list. */ values: string[]; } ) => void | |
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
## Accessibility
|
|
280
|
+
|
|
281
|
+
## Visual Design
|
|
282
|
+
- Color contrast ratio **MUST** be:
|
|
283
|
+
- >= 4.5:1 for normal text: 14 pt (typically 18.66px) and bold or larger [SC 1.4.3][1]
|
|
284
|
+
- Focus State: If the focus ring has a radius of [SC 1.4.11][2]
|
|
285
|
+
- < 3px: >= 4.5.1 between button <> focus <> background
|
|
286
|
+
- > 3px: >= 3.1 button button <> focus <> background
|
|
287
|
+
|
|
288
|
+
## States
|
|
289
|
+
- Color contrast does not apply to a disabled child elements in DualListbox
|
|
290
|
+
|
|
291
|
+
## Interaction Model
|
|
292
|
+
- **MUST** be perceivable and functional when when zoomed from 50-200% [SC 1.4.4][3] [SC 1.4.10][4]
|
|
293
|
+
- **SHOULD** use button semantics to be addressed by screen reader
|
|
294
|
+
- Icon buttons **MUST** have a tooltip that describes its function on hover [SC 1.4.13][5]
|
|
295
|
+
- **MUST** have keyboard navigation [SC 2.1][6]
|
|
296
|
+
- <kbd>Tab</kbd> and <kbd>Shift</kbd>+<kbd>Tab</kbd> to move through interactive elements within the DualListbox.
|
|
297
|
+
- <kbd>Space</kbd> changes the selection state of the focused option.
|
|
298
|
+
- <kbd>Down Arrow</kbd> moves focus to the next option.
|
|
299
|
+
- <kbd>Up Arrow</kbd> moves focus to the previous option.
|
|
300
|
+
- <kbd>Home</kbd> moves focus to the first option.
|
|
301
|
+
- <kbd>End</kbd> moves focus to the last option.
|
|
302
|
+
- <kbd>Shift</kbd>+<kbd>Down Arrow</kbd> moves focus to and selects the next option.
|
|
303
|
+
- <kbd>Shift</kbd>+<kbd>Up Arrow</kbd> moves focus to and selects the previous option.
|
|
304
|
+
- <kbd>Control</kbd>+<kbd>Shift</kbd>+<kbd>Home</kbd> selects from the focused option to the beginning of the list.
|
|
305
|
+
- <kbd>Control</kbd>+<kbd>Shift</kbd>+<kbd>End</kbd> selects from the focused option to the end of the list.
|
|
306
|
+
- <kbd>Control</kbd>+<kbd>A</kbd> selects all options in the list. If all options are selected, unselects all options.
|
|
307
|
+
- <kbd>Command</kbd>+<kbd>A</kbd> (macOS) selects all options in the list. If all options are selected, unselects all options.
|
|
308
|
+
- <kbd>Enter</kbd> performs an add only when focus is in the available options list.
|
|
309
|
+
- <kbd>Delete</kbd> performs a remove only when focus is in the chosen options list.
|
|
310
|
+
|
|
311
|
+
## Implementation
|
|
312
|
+
- When pressing a button without a change in context, focus **MUST NOT** be lost (i.e. a refresh button refreshes a table or data visualization)
|
|
313
|
+
- **MUST** have a visible focus border [SC 2.4.7][7]
|
|
314
|
+
- **MUST** use HTML semantics: `<ul>` or `<ol>` with `<li>` child elements, or `<dl>` with `<dt>` and `<dd>` child elements [SC 1.3.1][8]
|
|
315
|
+
|
|
316
|
+
[1]: https://www.w3.org/TR/WCAG21/#contrast-minimum
|
|
317
|
+
[2]: https://www.w3.org/TR/WCAG21/#non-text-contrast
|
|
318
|
+
[3]: https://www.w3.org/WAI/GL/UNDERSTANDING-WCAG20/visual-audio-contrast-scale.html
|
|
319
|
+
[4]: https://www.w3.org/WAI/WCAG21/Understanding/reflow.html
|
|
320
|
+
[5]: https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus
|
|
321
|
+
[6]: https://www.w3.org/TR/WCAG21/#keyboard-accessible
|
|
322
|
+
[7]: https://www.w3.org/TR/WCAG21/#focus-visible
|
|
323
|
+
[8]: https://www.w3.org/TR/WCAG21/#info-and-relationships
|
|
324
|
+
|
|
325
|
+
|