@splunk/react-ui 5.7.1 → 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 +29 -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 +188 -169
- 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,361 @@
|
|
|
1
|
+
# Button
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
> Image: Illustration of a button group, the first button is grey and the second is blue.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## When to use this component
|
|
11
|
+
Buttons are crucial interface elements that facilitate user interaction by triggering specific actions. Primary buttons are designated for primary actions and should only appear once, while secondary buttons are used for less prominent actions and can appear multiple times.
|
|
12
|
+
|
|
13
|
+
- To trigger an action or event
|
|
14
|
+
- To alert the user to a required action in a flow
|
|
15
|
+
|
|
16
|
+
### Additional considerations
|
|
17
|
+
- We advise against using disabled buttons, even if issues arise (notably in filters and modal windows with a single call to action). It's crucial for users to recognize that the product/service is operational. If a user clicks the button, provide an error message or guidance on the next steps.
|
|
18
|
+
- Limit the number of primary action buttons per focus area.
|
|
19
|
+
- Limit the use of icons to ones that convey the most meaning to users.
|
|
20
|
+
|
|
21
|
+
## When to use another component
|
|
22
|
+
- Buttons are used to complete an action; if you are providing navigation, implement a `Link`.
|
|
23
|
+
- When you have two or more actions related to the primary action that need to be consolidated and space is limited, use a `SplitButton`.
|
|
24
|
+
|
|
25
|
+
```mermaid
|
|
26
|
+
graph TD
|
|
27
|
+
accDescr: Decision tree that guides on when to use the Button component or something else
|
|
28
|
+
A(When clicked do you need to navigate somewhere new?) -- Yes --- B(Link)
|
|
29
|
+
A -- No --- C(Button)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Check out
|
|
33
|
+
- [Link] [1]
|
|
34
|
+
- [Split Button] [2]
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Clear call to action
|
|
39
|
+
Avoid using multiple primary actions. Instead, use alternate button appearances to support additional actions.
|
|
40
|
+
> Image: Examples of a button group with a clear call to action. The first example with heart eyes emoji has a secondary button next to a primary button. The second example with a grimacing emoji has two primary buttons.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Avoid disabled buttons
|
|
44
|
+
Always enable buttons, use validation in forms, and provide an error message or guidance on the next steps.
|
|
45
|
+
> Image: Example with two modals. The first modal example with heart eyes emoji has an error message and an enabled button. The second example with a grimacing emoji does not use an error message and disables the button.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
### Dimmed button
|
|
49
|
+
If you absolutely need to disabled a button, use a dimmed button. This ensure users can still navigate to the button when using assistive technologies.
|
|
50
|
+
> Image: Example of using a dimmed button. The first example with heart eyes emoji has a dimmed button that is focused. The second example with a grimacing emoji uses a disabled button that is not dimmed and cannot receive focus.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Button pairing
|
|
54
|
+
When using multiple buttons in a group, it’s best to use only two different appearances.
|
|
55
|
+
> Image: Examples of button appearance pairings. The first example with heart eyes emoji has three buttons in a group, the first two use the same secondary appearance and the third uses the primary appearance. The second example with a grimacing emoji has three buttons in a group. The first uses the flat appearance, the second uses default appearance, and the third uses primary appearance.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Destructive actions
|
|
59
|
+
Use the destructive button appearance for destructive actions.
|
|
60
|
+
> Image: Examples of destructive actions. The first example with heart eyes emoji has two buttons in a group. The first button in the group uses the secondary appearance and the second button uses the destructive red appearance. The second example with a grimacing emoji has two buttons in a group and the second button using the primary appearance for a destructive action.
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
### Toggle actions and state
|
|
64
|
+
Toggle buttons convey both the current state and the expected interaction outcome. Whenever possible, use tooltips and visual cues (e.g. filled icons, color changes, etc.) to communicate both pieces of information.
|
|
65
|
+
> Image: Examples of toggle buttons and their state. The first example with heart eyes emoji has two icon buttons in a group. The first button is a pin icon that uses and outline and is not filled, the second button is hovered and uses an eye icon that is filled and has a tooltip above it with
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Content guidelines
|
|
69
|
+
- Start button text with a verb when possible.
|
|
70
|
+
- Avoid vague or generic language such as “click here” or “read more”; this is not helpful for screen reader users.
|
|
71
|
+
- Avoid any acronyms or confusing jargon that may leave a user guessing or afraid to click on a button (SC 2.4.4).
|
|
72
|
+
|
|
73
|
+
### Label should not exceed three words
|
|
74
|
+
Write concise button labels: 1 or 2 words.
|
|
75
|
+
> Image: Examples of button label length. The first example with heart eyes emoji has a primary button with a label the reads
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
### Use sentence-style capitalization
|
|
79
|
+
> Image: Examples of sentence-style for button labels. The first example with heart eyes emoji has a primary button with a label using sentence-style capitalization that reads
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
### Use precise language
|
|
83
|
+
Describe the action. For example, use “Add” when using an existing object in a new context, and use “Create” when making a new object from scratch.
|
|
84
|
+
> Image: Examples of precise language for button labels. The first example with heart eyes emoji has a primary button with a label that reads
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
### Label overflow
|
|
88
|
+
While usage guidelines for content recommend one to two words for buttons, for internationalization there might be instances when text needs to overflow.
|
|
89
|
+
> Image: Examples of button label overflow. The first example with heart eyes has a primary button that is hovered with a truncated label that reads
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
[1]: ./Link
|
|
94
|
+
[2]: ./SplitButton
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
### Basic
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import React from 'react';
|
|
103
|
+
|
|
104
|
+
import Button from '@splunk/react-ui/Button';
|
|
105
|
+
import Layout from '@splunk/react-ui/Layout';
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
function Basic() {
|
|
109
|
+
return (
|
|
110
|
+
<Layout>
|
|
111
|
+
<Button label="Primary" appearance="primary" />
|
|
112
|
+
<Button label="Secondary" />
|
|
113
|
+
<Button label="Destructive" appearance="destructive" />
|
|
114
|
+
<Button label="Standalone" appearance="standalone" />
|
|
115
|
+
<Button label="Subtle" appearance="subtle" />
|
|
116
|
+
</Layout>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export default Basic;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
### With icons
|
|
126
|
+
|
|
127
|
+
By default, icons appear to the left of Button text. If you want an icon to appear the right of Button text, add it as a child and correct margins or other positioning as necessary.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import React from 'react';
|
|
131
|
+
|
|
132
|
+
import Printer from '@splunk/react-icons/Printer';
|
|
133
|
+
import TrashCanCross from '@splunk/react-icons/TrashCanCross';
|
|
134
|
+
import Button from '@splunk/react-ui/Button';
|
|
135
|
+
import Layout from '@splunk/react-ui/Layout';
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
function Icons() {
|
|
139
|
+
return (
|
|
140
|
+
<Layout>
|
|
141
|
+
<Button icon={<Printer />} />
|
|
142
|
+
<Button icon={<Printer />} label="Print" />
|
|
143
|
+
<Button icon={<Printer />} appearance="primary" label="Print" />
|
|
144
|
+
<Button icon={<Printer />} appearance="standalone" label="Print" />
|
|
145
|
+
<Button icon={<Printer />} appearance="subtle" label="Print" />
|
|
146
|
+
<Button icon={<TrashCanCross />} appearance="destructive" label="Delete" />
|
|
147
|
+
</Layout>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export default Icons;
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
### With menu indicator
|
|
157
|
+
|
|
158
|
+
Add the isMenu prop to display the menu indicator to the right of Button text. See [Dropdown](Dropdown) to learn how to create a dropdown menu.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import React from 'react';
|
|
162
|
+
|
|
163
|
+
import Button from '@splunk/react-ui/Button';
|
|
164
|
+
import Layout from '@splunk/react-ui/Layout';
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
function Menus() {
|
|
168
|
+
return (
|
|
169
|
+
<Layout>
|
|
170
|
+
<Button isMenu label="Default" />
|
|
171
|
+
<Button isMenu appearance="primary" label="Primary" />
|
|
172
|
+
<Button isMenu appearance="subtle" label="Subtle" />
|
|
173
|
+
</Layout>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default Menus;
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
### As a link
|
|
183
|
+
|
|
184
|
+
Add the to prop to indicate that the Button behaves as a link. The openInNewContext prop will open the link in a new window or tab and display the external link indicator
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import React from 'react';
|
|
188
|
+
|
|
189
|
+
import Button from '@splunk/react-ui/Button';
|
|
190
|
+
import Layout from '@splunk/react-ui/Layout';
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
function To() {
|
|
194
|
+
return (
|
|
195
|
+
<Layout style={{ flexDirection: 'column', alignItems: 'flex-start' }}>
|
|
196
|
+
<Layout>
|
|
197
|
+
<Button to="Select" label="Select component" />
|
|
198
|
+
<Button to="Select" appearance="primary" label="Select component" />
|
|
199
|
+
<Button to="Select" appearance="standalone" label="Select component" />
|
|
200
|
+
<Button to="Select" appearance="subtle" label="Select component" />
|
|
201
|
+
</Layout>
|
|
202
|
+
<Layout>
|
|
203
|
+
<Button to="https://www.splunk.com" openInNewContext label="Splunk" />
|
|
204
|
+
<Button
|
|
205
|
+
to="https://www.splunk.com"
|
|
206
|
+
openInNewContext
|
|
207
|
+
appearance="primary"
|
|
208
|
+
label="Splunk"
|
|
209
|
+
/>
|
|
210
|
+
<Button
|
|
211
|
+
to="https://www.splunk.com"
|
|
212
|
+
openInNewContext
|
|
213
|
+
appearance="standalone"
|
|
214
|
+
label="Splunk"
|
|
215
|
+
/>
|
|
216
|
+
<Button
|
|
217
|
+
to="https://www.splunk.com"
|
|
218
|
+
openInNewContext
|
|
219
|
+
appearance="subtle"
|
|
220
|
+
label="Splunk"
|
|
221
|
+
/>
|
|
222
|
+
</Layout>
|
|
223
|
+
</Layout>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export default To;
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
### Block sized
|
|
233
|
+
|
|
234
|
+
By default, Buttons exist in inline blocks. Set inline to false to make the Button the full width of the parent container.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import React from 'react';
|
|
238
|
+
|
|
239
|
+
import Button from '@splunk/react-ui/Button';
|
|
240
|
+
import Layout from '@splunk/react-ui/Layout';
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
function Block() {
|
|
244
|
+
return (
|
|
245
|
+
<Layout style={{ flexDirection: 'column', width: '300px' }}>
|
|
246
|
+
<Button inline={false} appearance="primary" label="Primary" />
|
|
247
|
+
<Button inline={false} appearance="secondary" label="Secondary" />
|
|
248
|
+
<Button inline={false} appearance="standalone" label="Standalone" />
|
|
249
|
+
</Layout>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export default Block;
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
### Disabled
|
|
259
|
+
|
|
260
|
+
Consider keeping Buttons enabled and inform the user why the action may have failed or can't be completed. Disabling buttons creates barriers for all users and can exclude people with disabilities. If a Button is passed disabled the default behavior is to render a 'dimmed' button. This ensures the button is still discoverable and can receive focus, but the button cannot not be activated by the user. If necessary, a Button can be completely disabled by setting disabled='disabled'. In these cases, consider contacting us to collaborate on alternatives for a more inclusive experience.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import React from 'react';
|
|
264
|
+
|
|
265
|
+
import DotsThreeVertical from '@splunk/react-icons/DotsThreeVertical';
|
|
266
|
+
import Button from '@splunk/react-ui/Button';
|
|
267
|
+
import Layout from '@splunk/react-ui/Layout';
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
function Disabled() {
|
|
271
|
+
return (
|
|
272
|
+
<Layout>
|
|
273
|
+
<Button disabled label="Default" />
|
|
274
|
+
<Button disabled icon={<DotsThreeVertical height="16px" width="16px" />} />
|
|
275
|
+
<Button disabled label="Primary" appearance="primary" />
|
|
276
|
+
<Button disabled label="Destructive" appearance="destructive" />
|
|
277
|
+
<Button disabled label="Standalone" appearance="standalone" />
|
|
278
|
+
<Button disabled label="Subtle" appearance="subtle" />
|
|
279
|
+
</Layout>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export default Disabled;
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
## API
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
### Button API
|
|
293
|
+
|
|
294
|
+
#### Props
|
|
295
|
+
|
|
296
|
+
| Name | Type | Required | Default | Description |
|
|
297
|
+
|------|------|------|------|------|
|
|
298
|
+
| action | string | no | | Returns a value on click. Use when composing or testing. |
|
|
299
|
+
| appearance | \| 'default' \| 'secondary' \| 'primary' \| 'destructive' \| 'destructiveSecondary' \| 'standalone' \| 'subtle' | no | 'default' | Changes the style of the button. |
|
|
300
|
+
| append | boolean | no | | Removes the right border and border-radius of the button so you can append other elements to it. |
|
|
301
|
+
| children | React.ReactNode | no | | |
|
|
302
|
+
| disabled | boolean \| 'dimmed' \| 'disabled' | no | | Prevents user from activating the button and adds disabled styling. If set to `dimmed`, the component is able to receive focus. If set to `disabled`, the component is unable to receive focus (as a result of setting the html `disabled` attribute). The default behavior when `disabled={true}` is `dimmed`. |
|
|
303
|
+
| elementRef | React.Ref<HTMLAnchorElement \| HTMLButtonElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
|
|
304
|
+
| icon | React.ReactNode | no | | Applies an icon to the button. See @splunk/react-icons documentation for more information. |
|
|
305
|
+
| inline | boolean | no | true | Restricts the horizontal size of the button. Set `inline` to `false` to remove the right margin and stretch the button to the full width of its container. |
|
|
306
|
+
| isMenu | boolean | no | | Uses interactive styling and adds the chevron-down icon to indicate menu behavior. **Accessibility:** This prop should be used with the `Dropdown` component, which manages the required `aria-controls` and `aria-expanded` attributes. If not using `Dropdown`, you must manually provide these ARIA attributes. |
|
|
307
|
+
| label | React.ReactNode | no | | Applies the text that displays on the button. |
|
|
308
|
+
| onClick | ( event: React.MouseEvent<HTMLAnchorElement \| HTMLButtonElement>, data: { action?: string; icon?: React.ReactNode; label?: React.ReactNode; value?: any; // eslint-disable-line @typescript-eslint/no-explicit-any } ) => void | no | | Called when the user activates the button. |
|
|
309
|
+
| openInNewContext | boolean \| string | no | | Open the "to" link in a new context, which is usually a new tab or window based on browser settings. An icon and a screen reader message is added to indicate this behavior to users. The default message is "(Opens new window)"; this can be customized by passing a string instead of boolean to `openInNewContext`. |
|
|
310
|
+
| prepend | boolean | no | | Removes the left border and border-radius of the button so you can prepend elements to it. |
|
|
311
|
+
| to | string | no | | Identifies the URL for a link. If set, Splunk UI applies an <a> tag instead of a <button> tag. |
|
|
312
|
+
| value | any | no | | Returns a value on click. Use when composing or testing. |
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
## Accessibility
|
|
318
|
+
|
|
319
|
+
Buttons are used to complete an action; if you are providing navigation, implement a link.
|
|
320
|
+
|
|
321
|
+
## Visual Design
|
|
322
|
+
- Color contrast ratio **MUST** be:
|
|
323
|
+
- >= 4.5:1 between text and background [SC 1.4.3][1]
|
|
324
|
+
- >= 3:1 between button outline color to background OR icon color to background [SC 1.4.11][2]
|
|
325
|
+
- Focus State: If the focus ring has a radius of [SC 1.4.11][2]
|
|
326
|
+
- < 3px: >= 4.5.1 between button <> focus <> background
|
|
327
|
+
- > 3px: >= 3.1 button button <> focus <> background
|
|
328
|
+
|
|
329
|
+
## Content
|
|
330
|
+
- **SHOULD** avoid vague or generic language such as “click here” or “read more”, or acronyms that are not widely known to users
|
|
331
|
+
|
|
332
|
+
## States
|
|
333
|
+
- Color contrast guidelines do not apply to disabled button
|
|
334
|
+
|
|
335
|
+
## Interaction Model
|
|
336
|
+
- **MUST** be perceivable and functional when when zoomed from 50-200% [SC 1.4.4][3] [SC 1.4.10][4]
|
|
337
|
+
- **SHOULD** use button semantics to be addressed by screen reader
|
|
338
|
+
- Icon buttons **MUST** have a tooltip that describes its function on hover [SC 1.4.13][5]
|
|
339
|
+
|
|
340
|
+
## Implementation
|
|
341
|
+
- **MUST** have a visible focus border [SC 2.4.7][6]
|
|
342
|
+
- **MUST** have keyboard navigation [SC 2.1][7]
|
|
343
|
+
- <kbd>ENTER</kbd>/<kbd>SPACE</kbd> to execute the action
|
|
344
|
+
- <kbd>TAB</kbd> to move focus to next interactive element
|
|
345
|
+
- <kbd>SHIFT</kbd>+<kbd>TAB</kbd> to move the focus to the previous interactive element
|
|
346
|
+
- **MUST** use a button for an action on the page. This should be reflected in wireframes, otherwise confirm with the designer before new implementation. [SC 4.1.2][8]
|
|
347
|
+
- Focus Management **SHOULD** be considered. Example behaviors are:
|
|
348
|
+
- 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)
|
|
349
|
+
- When pressing a button with a change in context, focus **MUST** be moved to first available component (i.e. primary button that requires page reload, so focus moves to first interactive element on new page)
|
|
350
|
+
- Buttons within other components **SHOULD** have consultation with a11y team to determine most accessible experience
|
|
351
|
+
|
|
352
|
+
[1]: https://www.w3.org/WAI/GL/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html
|
|
353
|
+
[2]: https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html
|
|
354
|
+
[3]: https://www.w3.org/WAI/GL/UNDERSTANDING-WCAG20/visual-audio-contrast-scale.html
|
|
355
|
+
[4]: https://www.w3.org/WAI/WCAG21/Understanding/reflow.html
|
|
356
|
+
[5]: https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus
|
|
357
|
+
[6]: https://www.w3.org/TR/WCAG21/#focus-visible
|
|
358
|
+
[7]: https://www.w3.org/TR/WCAG21/#keyboard-accessible
|
|
359
|
+
[8]: https://www.w3.org/TR/WCAG21/#name-role-value
|
|
360
|
+
|
|
361
|
+
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Card Layout
|
|
2
|
+
|
|
3
|
+
## Examples
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### StyledWrapper
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import React, { Component } from 'react';
|
|
10
|
+
|
|
11
|
+
import styled from 'styled-components';
|
|
12
|
+
|
|
13
|
+
import Card from '@splunk/react-ui/Card';
|
|
14
|
+
import CardLayout from '@splunk/react-ui/CardLayout';
|
|
15
|
+
import ControlGroup from '@splunk/react-ui/ControlGroup';
|
|
16
|
+
import Number, { NumberChangeHandler } from '@splunk/react-ui/Number';
|
|
17
|
+
import Select from '@splunk/react-ui/Select';
|
|
18
|
+
import Switch, { SwitchClickHandler } from '@splunk/react-ui/Switch';
|
|
19
|
+
import { variables } from '@splunk/themes';
|
|
20
|
+
|
|
21
|
+
interface InteractiveState {
|
|
22
|
+
value?: number;
|
|
23
|
+
minWidth?: number;
|
|
24
|
+
maxWidth?: number;
|
|
25
|
+
gutterSize?: number;
|
|
26
|
+
wrapCards: boolean;
|
|
27
|
+
alignCards: 'left' | 'center' | 'right';
|
|
28
|
+
setAsWidth: boolean;
|
|
29
|
+
hasMaxWidth: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const StyledWrapper = styled.div`
|
|
33
|
+
display: grid;
|
|
34
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
35
|
+
column-gap: ${variables.spacingXLarge};
|
|
36
|
+
|
|
37
|
+
/* 5th child is the first checkbox, which we want to start on a new row */
|
|
38
|
+
& > :nth-child(5) {
|
|
39
|
+
grid-column: 1;
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const StyledCardContent = styled.div`
|
|
44
|
+
height: 200px;
|
|
45
|
+
background: ${variables.neutral200};
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Interactive extends Component<object, InteractiveState> {
|
|
50
|
+
constructor(props: object) {
|
|
51
|
+
super(props);
|
|
52
|
+
|
|
53
|
+
this.state = {
|
|
54
|
+
value: 5,
|
|
55
|
+
minWidth: 200,
|
|
56
|
+
maxWidth: 600,
|
|
57
|
+
gutterSize: 10,
|
|
58
|
+
wrapCards: true,
|
|
59
|
+
alignCards: 'left',
|
|
60
|
+
setAsWidth: false,
|
|
61
|
+
hasMaxWidth: false,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
handleChangeAmount: NumberChangeHandler = (e, { value }) => {
|
|
66
|
+
this.setState({ value });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
handleChangeMinWidth: NumberChangeHandler = (e, { value }) => {
|
|
70
|
+
this.setState({ minWidth: value });
|
|
71
|
+
if (this.state.setAsWidth) {
|
|
72
|
+
this.setState({ maxWidth: value });
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
handleChangeMaxWidth: NumberChangeHandler = (e, { value }) => {
|
|
77
|
+
this.setState({ maxWidth: value });
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
handleChangeGutter: NumberChangeHandler = (e, { value }) => {
|
|
81
|
+
this.setState({ gutterSize: value });
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
handleChangeWrap: SwitchClickHandler = (e, { value }) => {
|
|
85
|
+
this.setState({ wrapCards: !value });
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// @ts-expect-error - @docs-hide-comment - fix once Select is typed
|
|
89
|
+
handleChangeAlign = (e, { value }) => {
|
|
90
|
+
this.setState({ alignCards: value });
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
handleSetAsWidth: SwitchClickHandler = (e, { value }) => {
|
|
94
|
+
this.setState({ setAsWidth: !value });
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
handleHasMaxWidth: SwitchClickHandler = (e, { value }) => {
|
|
98
|
+
this.setState({ hasMaxWidth: !value });
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
render() {
|
|
102
|
+
const cards = [];
|
|
103
|
+
for (let i = 0; i < (this.state.value ?? 0); i += 1) {
|
|
104
|
+
cards.push(
|
|
105
|
+
<Card key={i}>
|
|
106
|
+
<Card.Header title={`Card ${i + 1}`} />
|
|
107
|
+
<Card.Body>
|
|
108
|
+
<StyledCardContent />
|
|
109
|
+
</Card.Body>
|
|
110
|
+
</Card>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return (
|
|
114
|
+
<article>
|
|
115
|
+
<StyledWrapper>
|
|
116
|
+
<ControlGroup label="Number of cards">
|
|
117
|
+
<Number
|
|
118
|
+
value={this.state.value}
|
|
119
|
+
onChange={this.handleChangeAmount}
|
|
120
|
+
min={1}
|
|
121
|
+
max={100}
|
|
122
|
+
/>
|
|
123
|
+
</ControlGroup>
|
|
124
|
+
<ControlGroup label="Gutter size">
|
|
125
|
+
<Number
|
|
126
|
+
value={this.state.gutterSize}
|
|
127
|
+
onChange={this.handleChangeGutter}
|
|
128
|
+
min={0}
|
|
129
|
+
max={50}
|
|
130
|
+
step={5}
|
|
131
|
+
/>
|
|
132
|
+
</ControlGroup>
|
|
133
|
+
<ControlGroup label="Align cards">
|
|
134
|
+
<Select value={this.state.alignCards} onChange={this.handleChangeAlign}>
|
|
135
|
+
<Select.Option label="Left" value="left" />
|
|
136
|
+
<Select.Option label="Center" value="center" />
|
|
137
|
+
<Select.Option label="Right" value="right" />
|
|
138
|
+
</Select>
|
|
139
|
+
</ControlGroup>
|
|
140
|
+
<ControlGroup
|
|
141
|
+
label={
|
|
142
|
+
this.state.setAsWidth ||
|
|
143
|
+
(!this.state.setAsWidth && this.state.hasMaxWidth)
|
|
144
|
+
? 'Card width'
|
|
145
|
+
: 'Card min width'
|
|
146
|
+
}
|
|
147
|
+
style={{
|
|
148
|
+
marginBottom:
|
|
149
|
+
this.state.hasMaxWidth && !this.state.setAsWidth ? 2 : undefined,
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
<Number
|
|
153
|
+
value={this.state.minWidth}
|
|
154
|
+
onChange={this.handleChangeMinWidth}
|
|
155
|
+
min={100}
|
|
156
|
+
max={1000}
|
|
157
|
+
step={50}
|
|
158
|
+
/>
|
|
159
|
+
{this.state.hasMaxWidth && !this.state.setAsWidth && (
|
|
160
|
+
<Number
|
|
161
|
+
value={this.state.maxWidth}
|
|
162
|
+
onChange={this.handleChangeMaxWidth}
|
|
163
|
+
min={100}
|
|
164
|
+
max={2000}
|
|
165
|
+
step={50}
|
|
166
|
+
/>
|
|
167
|
+
)}
|
|
168
|
+
{!this.state.setAsWidth && this.state.hasMaxWidth && (
|
|
169
|
+
<ControlGroup label=" ">
|
|
170
|
+
<div style={{ width: '50%' }}>Min</div>
|
|
171
|
+
<div style={{ width: '50%', marginLeft: 10 }}>Max</div>
|
|
172
|
+
</ControlGroup>
|
|
173
|
+
)}
|
|
174
|
+
</ControlGroup>
|
|
175
|
+
<ControlGroup label="Wrap cards">
|
|
176
|
+
<Switch
|
|
177
|
+
value={this.state.wrapCards}
|
|
178
|
+
onClick={this.handleChangeWrap}
|
|
179
|
+
selected={this.state.wrapCards}
|
|
180
|
+
appearance="toggle"
|
|
181
|
+
/>
|
|
182
|
+
</ControlGroup>
|
|
183
|
+
<ControlGroup label="Width only">
|
|
184
|
+
<Switch
|
|
185
|
+
value={this.state.setAsWidth}
|
|
186
|
+
onClick={this.handleSetAsWidth}
|
|
187
|
+
selected={this.state.setAsWidth}
|
|
188
|
+
appearance="toggle"
|
|
189
|
+
/>
|
|
190
|
+
</ControlGroup>
|
|
191
|
+
<ControlGroup label="Show max width">
|
|
192
|
+
<Switch
|
|
193
|
+
value={this.state.hasMaxWidth}
|
|
194
|
+
onClick={this.handleHasMaxWidth}
|
|
195
|
+
selected={this.state.hasMaxWidth}
|
|
196
|
+
appearance="toggle"
|
|
197
|
+
disabled={this.state.setAsWidth}
|
|
198
|
+
/>
|
|
199
|
+
</ControlGroup>
|
|
200
|
+
</StyledWrapper>
|
|
201
|
+
<CardLayout
|
|
202
|
+
alignCards={this.state.alignCards}
|
|
203
|
+
gutterSize={this.state.gutterSize}
|
|
204
|
+
cardWidth={this.state.setAsWidth ? this.state.minWidth : undefined}
|
|
205
|
+
cardMinWidth={this.state.minWidth || undefined}
|
|
206
|
+
cardMaxWidth={this.state.hasMaxWidth ? this.state.maxWidth : undefined}
|
|
207
|
+
wrapCards={this.state.wrapCards}
|
|
208
|
+
>
|
|
209
|
+
{cards}
|
|
210
|
+
</CardLayout>
|
|
211
|
+
</article>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export default Interactive;
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
### StyledCardContent
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import React from 'react';
|
|
225
|
+
|
|
226
|
+
import styled from 'styled-components';
|
|
227
|
+
|
|
228
|
+
import Card from '@splunk/react-ui/Card';
|
|
229
|
+
import CardLayout from '@splunk/react-ui/CardLayout';
|
|
230
|
+
import { variables } from '@splunk/themes';
|
|
231
|
+
|
|
232
|
+
const StyledCardContent = styled.div`
|
|
233
|
+
height: 200px;
|
|
234
|
+
background: ${variables.neutral200};
|
|
235
|
+
`;
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
function Basic() {
|
|
239
|
+
const cardContent = <StyledCardContent />;
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<CardLayout style={{ maxWidth: 900 }}>
|
|
243
|
+
<Card>
|
|
244
|
+
<Card.Header title="Card 1" />
|
|
245
|
+
<Card.Body>{cardContent}</Card.Body>
|
|
246
|
+
</Card>
|
|
247
|
+
<Card>
|
|
248
|
+
<Card.Header title="Card 2" />
|
|
249
|
+
<Card.Body>{cardContent}</Card.Body>
|
|
250
|
+
</Card>
|
|
251
|
+
<Card>
|
|
252
|
+
<Card.Header title="Card 3" />
|
|
253
|
+
<Card.Body>{cardContent}</Card.Body>
|
|
254
|
+
</Card>
|
|
255
|
+
</CardLayout>
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export default Basic;
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
## API
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
### CardLayout API
|
|
269
|
+
|
|
270
|
+
#### Props
|
|
271
|
+
|
|
272
|
+
| Name | Type | Required | Default | Description |
|
|
273
|
+
|------|------|------|------|------|
|
|
274
|
+
| alignCards | 'left' \| 'center' \| 'right' | no | 'left' | Aligns `Card`s in layout. |
|
|
275
|
+
| cardMaxWidth | number \| string | no | | Sets maximum width of each `Card`. |
|
|
276
|
+
| cardMinWidth | number \| string | no | | Sets minimum width of each `Card`. |
|
|
277
|
+
| cardWidth | number \| string | no | | Sets width of each `Card`. |
|
|
278
|
+
| children | React.ReactNode | no | | Must be `Card`. |
|
|
279
|
+
| elementRef | React.Ref<HTMLDivElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
|
|
280
|
+
| gutterSize | number | no | 10 | Sets space in pixels between each `Card`. |
|
|
281
|
+
| wrapCards | boolean | no | true | Lets `Card`s wrap to a new line when the container gets narrow. |
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|