@splunk/react-ui 5.7.1 → 5.9.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/Anchor.js +2 -1
- package/Box.js +83 -34
- package/CHANGELOG.md +51 -0
- package/Calendar.js +134 -134
- package/Clickable.js +131 -94
- package/CollapsiblePanel.js +175 -137
- package/ComboBox.js +32 -27
- package/ControlGroup.js +92 -91
- package/DefinitionList.js +9 -9
- package/Drawer.d.ts +2 -0
- package/Drawer.js +679 -0
- package/Dropdown.js +27 -18
- package/DualListbox.js +1 -1
- package/File.js +35 -35
- package/JSONTree.js +73 -72
- package/Link.js +2 -2
- package/MIGRATION.md +10 -0
- package/Menu.js +403 -261
- package/Modal.js +263 -252
- package/Monogram.js +2 -2
- package/Multiselect.js +551 -385
- package/Number.js +2 -1
- package/Paginator.js +14 -12
- package/Popover.js +4 -1
- package/README.md +11 -0
- package/RadioBar.js +1 -1
- package/Search.js +111 -95
- package/Select.js +42 -40
- package/SelectBase.js +819 -715
- package/SidePanel.js +346 -167
- package/SlidingPanels.js +11 -11
- package/StepBar.js +7 -7
- package/Switch.js +5 -5
- package/Table.js +116 -119
- package/Text.js +48 -48
- 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 +292 -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 +939 -0
- package/docs-llm/Notifications.md +46 -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 +591 -0
- package/docs-llm/Typography.md +125 -0
- package/docs-llm/Wait Spinner.md +121 -0
- package/docs-llm/llms.txt +101 -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/Multiselect/Compact.d.ts +8 -3
- package/types/src/Multiselect/Multiselect.d.ts +8 -3
- package/types/src/Multiselect/Normal.d.ts +8 -3
- package/types/src/Multiselect/Option.d.ts +6 -3
- package/types/src/Multiselect/docs/examples/Disabled.d.ts +1 -0
- 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,2642 @@
|
|
|
1
|
+
# Table
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
> Image: Illustration of a table component.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## When to use this component
|
|
10
|
+
- There’s a need to arrange data in a precise way that’s easy to understand and interact with.
|
|
11
|
+
- In contexts where data points are compared side-by-side.
|
|
12
|
+
- For schedules, pricing plans, product features, or other structured data.
|
|
13
|
+
|
|
14
|
+
## When to use another component
|
|
15
|
+
- For simpler or single data points, use a List component instead.
|
|
16
|
+
- For data collection, create a form with Control Group.
|
|
17
|
+
- Consider a graph or chart if there are trends, patterns or exceptions to highlight in the data.
|
|
18
|
+
|
|
19
|
+
```mermaid
|
|
20
|
+
graph TD
|
|
21
|
+
accDescr: Decision tree that guides on when to use the Table component or something else
|
|
22
|
+
A(Do you need to collect data?) -- Yes --- B(Control Group)
|
|
23
|
+
A -- No --- C(Would your data benefit from graphical representation?)
|
|
24
|
+
C -- Yes --- D(Charts or graphs)
|
|
25
|
+
C -- No --- E(Do you need to display structured and comparable data)
|
|
26
|
+
E -- Yes --- F(Table)
|
|
27
|
+
E -- No --- G(List)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Check out
|
|
31
|
+
- [List] [1]
|
|
32
|
+
- [Control Group] [2]
|
|
33
|
+
- [Data Visualization] [3]
|
|
34
|
+
|
|
35
|
+
## Behaviors
|
|
36
|
+
|
|
37
|
+
### Row selection
|
|
38
|
+
> Image: A table displays several rows, each with a checkbox in the first column. Three rows are selected, indicated by check marks in their respective checkboxes. The table contains columns for
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Column resizing
|
|
42
|
+
> Image: A table shows several rows of data with columns for
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### Reordering
|
|
46
|
+
|
|
47
|
+
#### Columns
|
|
48
|
+
> Image: A table shows multiple rows with columns for
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
#### Rows
|
|
52
|
+
> Image: table displays several rows with columns for
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
### Striped rows
|
|
56
|
+
Striped rows provide an additional affordance to distinguish between alternating rows, especially in data-heavy tables.
|
|
57
|
+
> Image: A table with four columns: Name, Status, Path, and Max Size. The background color alternates between rows to help distinguish between them. The table lists database entries with names such as Simple, Audit, Test, and Splunk. The Status column displays either
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
### Minimize the number of columns
|
|
64
|
+
It's easier to scan many rows than it is to scan many columns.
|
|
65
|
+
> Image: Two examples of Table illustrating the impact of column quantity on scanability. The first example, with a heart-eyes emoji, has fewer columns, indicating it is easier to scan. The second example, with a grimacing face emoji, has many more columns, making it harder to read and scan due to the increased number of columns.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
### Limit column data
|
|
69
|
+
Each column should only contain a single piece of data to maintain readability, accessibility, and usability. If a column contains multiple pieces of data, it can become confusing and difficult for users to parse and understand the information.
|
|
70
|
+
|
|
71
|
+
> Image: Two examples of Table data, tables have several rows and columns for
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### Filtering and sorting
|
|
75
|
+
Start with filtering and sorting on the individual table columns before using other components. For more complex interactions, [dropdowns in column headers](#dropdown) and the [Table toolbar area](#table-toolbar-area) in Table layout.
|
|
76
|
+
|
|
77
|
+
#### Filter
|
|
78
|
+
A column can be filtered as indicated by a funnel icon next to the column header title. You can identify how many filters are applied to a column by the count '2/4' displayed after the column header title.
|
|
79
|
+
|
|
80
|
+
> Image: Table displays rows of data with columns labeled
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
#### Sort
|
|
84
|
+
A column can be sorted as indicated by the arrows after the column title. The direction of the arrow indicates whether the column is sorted in ascending or descending order. A column with the ArrowUpDown icon indicates that it can be sorted but is currently unsorted.
|
|
85
|
+
|
|
86
|
+
> Image: Table displays rows of data with columns labeled
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
#### Dropdown
|
|
90
|
+
Use a dropdown in the column header when you need to provide filter and sort on the same column or you have multiple actions.
|
|
91
|
+
> Image: Table displays rows of data with columns labeled
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
### Pagination
|
|
95
|
+
- Use pagination to avoid overloading the Table with too much data and improve performance.
|
|
96
|
+
- Consider including the option for users to increase or decrease the number of items in a table.
|
|
97
|
+
- Recommended to place a Paginator at the top and bottom of longer tables table for improved keyboard navigation.
|
|
98
|
+
|
|
99
|
+
> Image: A table displays multiple rows of data with columns labeled
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
### Editing
|
|
103
|
+
Editing table data should be initiated via an action in the actions column, which either opens a modal or toggles a side panel.
|
|
104
|
+
|
|
105
|
+
#### Modal
|
|
106
|
+
Use a modal to edit or update a table data if you have only a few inputs.
|
|
107
|
+
|
|
108
|
+
> Image: A modal window is displayed over a dimmed table in the background, indicating that the user is in the process of editing or updating table data. The modal contains two input fields and action buttons at the bottom, with a
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
#### Side panel
|
|
112
|
+
Use a side panel when you have several inputs. The side panel allows users to edit information without fully obstructing the view of the underlying table, providing context while making changes. The side panel can either overlay, push content, or occupy reserved space and remain static.
|
|
113
|
+
|
|
114
|
+
> Image: Table is partially visible on the left side of the screen, with a side panel open on the right side for editing or updating table data. The side panel contains input fields and action buttons similar to a modal, with a
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
## Table layout
|
|
118
|
+
|
|
119
|
+
Tables fill their container's width, but if they become too wide, readability can suffer as the content spreads too far apart. To improve legibility, ensure that the layout and alignment are properly adjusted. Additionally, organize the table content based on the importance of the information according to your users' needs.
|
|
120
|
+
|
|
121
|
+
### Title and description
|
|
122
|
+
All tables should include a title and, if possible, a description to provide context and clarity.
|
|
123
|
+
|
|
124
|
+
> Image: A table is displayed with a clear title,
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
### Table toolbar area
|
|
128
|
+
The toolbar area is reserved for elements like filter, find, tabs, pagination, buttons, and other interactive elements that manipulate the table data.
|
|
129
|
+
|
|
130
|
+
Organize elements into:
|
|
131
|
+
1. Find (Search) and Filter (Select, Multiselect)
|
|
132
|
+
2. Additional actions or inputs
|
|
133
|
+
3. Select (items per page) and Paginator (page control)
|
|
134
|
+
|
|
135
|
+
> Image: A table displays a highlighted area above the table which is reserved for elements like filter, find, tabs, pagination, buttons, and other interactive elements that manipulate the table data. The toolbar starts with a search bar and radio bar options labeled
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
### Actions
|
|
139
|
+
A maximum of three buttons should be used in the actions column. The first two actions should be the most relevant, with the third button reserved for overflow.
|
|
140
|
+
|
|
141
|
+
If you have button labels with 2 words, want to reduce the space of the actions column, or using an icon alone is too vague, it's recommended to use a menu button for all actions.
|
|
142
|
+
|
|
143
|
+
> Image: A table displays rows of data with columns labeled
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
### Show and hide columns
|
|
147
|
+
Use the actions table header to control the visibility of table columns.
|
|
148
|
+
|
|
149
|
+
> Image: A table displays rows of data with columns labeled
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
### Table data
|
|
153
|
+
|
|
154
|
+
#### Loading Table data
|
|
155
|
+
When loading data, it's best to render the table title, description and column headers on page load, and then load all rows at once.
|
|
156
|
+
|
|
157
|
+
> Image: A table is displayed with a title at the top,
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
#### Empty or no data
|
|
161
|
+
Empty state displays both when no rows exist and when no rows exist due to filtering.
|
|
162
|
+
|
|
163
|
+
> Image: table is displayed with a title at the top,
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
## Content
|
|
167
|
+
|
|
168
|
+
### Column header truncation and text wrapping
|
|
169
|
+
Stick with one or two words, use sentence-case capitalization, and avoid truncating or text wrapping if possible.
|
|
170
|
+
|
|
171
|
+
#### Truncation
|
|
172
|
+
> Image: Two examples illustrating the approach to handling truncation in column headers. The first example with heart eyes emoji, features clear and concise column headers like
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
#### Text wrapping
|
|
176
|
+
> Image: Two examples illustrating the approach to handling text wrapping in column headers. The first example with heart eyes emoji shows clear, unwrapped column headers
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
### Row truncation and text wrapping
|
|
180
|
+
Ensure enough of the data is displayed for legibility. Text wrapping should avoided if possible. Otherwise, limit text wrapping to a maximum of two lines to maintain row height consistency.
|
|
181
|
+
|
|
182
|
+
#### Truncation
|
|
183
|
+
|
|
184
|
+
> Image: Two examples demonstrating the approach handling long text within table rows. The first example with heart eyes emoji, shows the full text in the
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
#### Text wrapping
|
|
188
|
+
Text wrapping will make table rows taller and potentially harder to scan quickly, so it should be avoided to preserve readability and maintain the layout.
|
|
189
|
+
|
|
190
|
+
> Image: Two examples demonstrating the approach handling long text within table rows. The first example with heart eyes emoji, maintains a single line of text per row, ensuring that the full path is visible without wrapping, which keeps the table clean and easy to read. The second example with a grimacing face emoji, wraps the text in the
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
### Content alignment
|
|
194
|
+
Align numerical data to the right and text data to the left for easier comparison.
|
|
195
|
+
|
|
196
|
+
> Image: Two examples that demonstrate aligning text and numerical data. The first example with heart eyes emoji, correctly aligns text data, such as
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
### Missing values
|
|
200
|
+
To indicate null or not applicable (N/A) values in the data, use an em dash (—) where there are gaps.
|
|
201
|
+
|
|
202
|
+
Leaving gaps blank can create confusion and make it harder for users to discern whether data is missing or simply omitted. It’s important to use a clear indicator for missing values to improve readability and understanding.
|
|
203
|
+
|
|
204
|
+
> Image: Two examples demonstrating the approach to handling null or not applicable (N/A) values. The first example with heart eyes emoji, uses an em dash (—) to clearly indicate gaps or missing data in the
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
### Color
|
|
208
|
+
Do not rely solely on color to convey information. Ensure there are text or icons for better accessibility.
|
|
209
|
+
|
|
210
|
+
> Image: Two examples demonstrating the approach to color usage in tables. The first example with heart eyes emoji, uses both color and text/icons in the
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
[1]: ./List
|
|
214
|
+
[2]: ./ControlGroup
|
|
215
|
+
[3]: https://splunkui.splunk.com/Packages/visualizations/?path=/Overview
|
|
216
|
+
|
|
217
|
+
## Examples
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
### data
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import React from 'react';
|
|
224
|
+
|
|
225
|
+
import Table from '@splunk/react-ui/Table';
|
|
226
|
+
|
|
227
|
+
const data = [
|
|
228
|
+
{ name: 'Rylan', age: 42, email: 'Angelita_Weimann42@gmail.com' },
|
|
229
|
+
{ name: 'Amelia', age: 24, email: 'Dexter.Trantow57@hotmail.com' },
|
|
230
|
+
{ name: 'Estevan', age: 56, email: 'Aimee7@hotmail.com' },
|
|
231
|
+
{ name: 'Florence', age: 71, email: 'Jarrod.Bernier13@yahoo.com' },
|
|
232
|
+
{ name: 'Tressa', age: 38, email: 'Yadira1@hotmail.com' },
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
function Basic() {
|
|
237
|
+
return (
|
|
238
|
+
<Table>
|
|
239
|
+
<Table.Head>
|
|
240
|
+
<Table.HeadCell>Name</Table.HeadCell>
|
|
241
|
+
<Table.HeadCell align="right">Age</Table.HeadCell>
|
|
242
|
+
<Table.HeadCell>Email</Table.HeadCell>
|
|
243
|
+
</Table.Head>
|
|
244
|
+
<Table.Body>
|
|
245
|
+
{data.map((row) => (
|
|
246
|
+
<Table.Row key={row.email}>
|
|
247
|
+
<Table.Cell>{row.name}</Table.Cell>
|
|
248
|
+
<Table.Cell align="right">{row.age}</Table.Cell>
|
|
249
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
250
|
+
</Table.Row>
|
|
251
|
+
))}
|
|
252
|
+
</Table.Body>
|
|
253
|
+
</Table>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export default Basic;
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
### Sortable Columns
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import React, { useState, useCallback } from 'react';
|
|
266
|
+
|
|
267
|
+
import Table, { HeadCellSortHandler } from '@splunk/react-ui/Table';
|
|
268
|
+
|
|
269
|
+
interface Row {
|
|
270
|
+
email: string;
|
|
271
|
+
name: string;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const data: Row[] = [
|
|
275
|
+
{ name: 'Rylan', email: 'Angelita_Weimann42@gmail.com' },
|
|
276
|
+
{ name: 'Amelia', email: 'Dexter.Trantow57@hotmail.com' },
|
|
277
|
+
{ name: 'Estevan', email: 'Aimee7@hotmail.com' },
|
|
278
|
+
{ name: 'Florence', email: 'Jarrod.Bernier13@yahoo.com' },
|
|
279
|
+
{ name: 'Tressa', email: 'Yadira1@hotmail.com' },
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
interface Column {
|
|
283
|
+
label: string;
|
|
284
|
+
sortKey: 'email' | 'name';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const columns: Column[] = [
|
|
288
|
+
{ sortKey: 'name', label: 'Name' },
|
|
289
|
+
{ sortKey: 'email', label: 'Email' },
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
function SortableColumns() {
|
|
294
|
+
const [sortKey, setSortKey] = useState<'email' | 'name'>('name');
|
|
295
|
+
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');
|
|
296
|
+
|
|
297
|
+
const handleSort: HeadCellSortHandler = useCallback(
|
|
298
|
+
(e, { sortKey: newSortKey }) => {
|
|
299
|
+
setSortKey((prevSortKey) => {
|
|
300
|
+
const prevSortDir = prevSortKey === newSortKey ? sortDir : 'none';
|
|
301
|
+
const nextSortDir = prevSortDir === 'asc' ? 'desc' : 'asc';
|
|
302
|
+
setSortDir(nextSortDir);
|
|
303
|
+
|
|
304
|
+
return newSortKey as 'email' | 'name';
|
|
305
|
+
});
|
|
306
|
+
},
|
|
307
|
+
[sortDir]
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<Table>
|
|
312
|
+
<Table.Head>
|
|
313
|
+
{columns.map((headData) => (
|
|
314
|
+
<Table.HeadCell
|
|
315
|
+
key={headData.sortKey}
|
|
316
|
+
onSort={handleSort}
|
|
317
|
+
sortKey={headData.sortKey}
|
|
318
|
+
sortDir={headData.sortKey === sortKey ? sortDir : 'none'}
|
|
319
|
+
>
|
|
320
|
+
{headData.label}
|
|
321
|
+
</Table.HeadCell>
|
|
322
|
+
))}
|
|
323
|
+
</Table.Head>
|
|
324
|
+
<Table.Body>
|
|
325
|
+
{data
|
|
326
|
+
.sort((rowA, rowB) => {
|
|
327
|
+
if (sortDir === 'asc') {
|
|
328
|
+
return rowA[sortKey] > rowB[sortKey] ? 1 : -1;
|
|
329
|
+
}
|
|
330
|
+
return rowB[sortKey] > rowA[sortKey] ? 1 : -1;
|
|
331
|
+
})
|
|
332
|
+
.map((row) => (
|
|
333
|
+
<Table.Row key={row.email}>
|
|
334
|
+
<Table.Cell>{row.name}</Table.Cell>
|
|
335
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
336
|
+
</Table.Row>
|
|
337
|
+
))}
|
|
338
|
+
</Table.Body>
|
|
339
|
+
</Table>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export default SortableColumns;
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
### Clickable Cells
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
import React, { useState } from 'react';
|
|
352
|
+
|
|
353
|
+
import DL, { Term as DT, Description as DD } from '@splunk/react-ui/DefinitionList';
|
|
354
|
+
import Table, { CellClickHandler } from '@splunk/react-ui/Table';
|
|
355
|
+
import Typography from '@splunk/react-ui/Typography';
|
|
356
|
+
|
|
357
|
+
interface Row {
|
|
358
|
+
email: string;
|
|
359
|
+
name: string;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const initialData: Row[] = [
|
|
363
|
+
{ name: 'Rylan', email: 'Angelita_Weimann42@gmail.com' },
|
|
364
|
+
{ name: 'Amelia', email: 'Dexter.Trantow57@hotmail.com' },
|
|
365
|
+
{ name: 'Estevan', email: 'Aimee7@hotmail.com' },
|
|
366
|
+
{ name: 'Florence', email: 'Jarrod.Bernier13@yahoo.com' },
|
|
367
|
+
{ name: 'Tressa', email: 'Yadira1@hotmail.com' },
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
function Click() {
|
|
372
|
+
const [data] = useState<Row[]>(initialData);
|
|
373
|
+
const [clickedRow, setClickedRow] = useState<string | undefined>(undefined);
|
|
374
|
+
|
|
375
|
+
const handleClick: CellClickHandler = (e, rowData) => {
|
|
376
|
+
setClickedRow(JSON.stringify(rowData));
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
return (
|
|
380
|
+
<div>
|
|
381
|
+
<Table>
|
|
382
|
+
<Table.Head>
|
|
383
|
+
<Table.HeadCell>Name</Table.HeadCell>
|
|
384
|
+
<Table.HeadCell>Email</Table.HeadCell>
|
|
385
|
+
</Table.Head>
|
|
386
|
+
<Table.Body>
|
|
387
|
+
{data.map((row) => (
|
|
388
|
+
<Table.Row key={row.email}>
|
|
389
|
+
<Table.Cell onClick={handleClick} data={row}>
|
|
390
|
+
{row.name}
|
|
391
|
+
</Table.Cell>
|
|
392
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
393
|
+
</Table.Row>
|
|
394
|
+
))}
|
|
395
|
+
</Table.Body>
|
|
396
|
+
</Table>
|
|
397
|
+
<aside style={{ marginTop: 20 }} aria-live="polite" aria-relevant="text">
|
|
398
|
+
<Typography as="p">Click a name to see the returned data</Typography>
|
|
399
|
+
{clickedRow && (
|
|
400
|
+
<div style={{ overflow: 'scroll', marginBottom: 10 }}>
|
|
401
|
+
<DL>
|
|
402
|
+
<DT>Data:</DT>
|
|
403
|
+
<DD>
|
|
404
|
+
<code>{clickedRow}</code>
|
|
405
|
+
</DD>
|
|
406
|
+
</DL>
|
|
407
|
+
</div>
|
|
408
|
+
)}
|
|
409
|
+
</aside>
|
|
410
|
+
</div>
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export default Click;
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
### Clickable Rows
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
import React, { useState } from 'react';
|
|
423
|
+
|
|
424
|
+
import DL, { Term as DT, Description as DD } from '@splunk/react-ui/DefinitionList';
|
|
425
|
+
import Table, { RowClickHandler } from '@splunk/react-ui/Table';
|
|
426
|
+
import Typography from '@splunk/react-ui/Typography';
|
|
427
|
+
|
|
428
|
+
interface Row {
|
|
429
|
+
email: string;
|
|
430
|
+
name: string;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const initialData: Row[] = [
|
|
434
|
+
{ name: 'Rylan', email: 'Angelita_Weimann42@gmail.com' },
|
|
435
|
+
{ name: 'Amelia', email: 'Dexter.Trantow57@hotmail.com' },
|
|
436
|
+
{ name: 'Estevan', email: 'Aimee7@hotmail.com' },
|
|
437
|
+
{ name: 'Florence', email: 'Jarrod.Bernier13@yahoo.com' },
|
|
438
|
+
{ name: 'Tressa', email: 'Yadira1@hotmail.com' },
|
|
439
|
+
];
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
function ClickRows() {
|
|
443
|
+
const [data] = useState<Row[]>(initialData);
|
|
444
|
+
const [clickedRow, setClickedRow] = useState<string | undefined>(undefined);
|
|
445
|
+
|
|
446
|
+
const handleClick: RowClickHandler = (e, rowData) => {
|
|
447
|
+
setClickedRow(JSON.stringify(rowData));
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
return (
|
|
451
|
+
<div>
|
|
452
|
+
<Table>
|
|
453
|
+
<Table.Head>
|
|
454
|
+
<Table.HeadCell>Name</Table.HeadCell>
|
|
455
|
+
<Table.HeadCell>Email</Table.HeadCell>
|
|
456
|
+
</Table.Head>
|
|
457
|
+
<Table.Body>
|
|
458
|
+
{data.map((row) => (
|
|
459
|
+
<Table.Row key={row.email} onClick={handleClick} data={row}>
|
|
460
|
+
<Table.Cell>{row.name}</Table.Cell>
|
|
461
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
462
|
+
</Table.Row>
|
|
463
|
+
))}
|
|
464
|
+
</Table.Body>
|
|
465
|
+
</Table>
|
|
466
|
+
<aside style={{ marginTop: 20 }} aria-live="polite" aria-relevant="text">
|
|
467
|
+
<Typography as="p">Click a name to see the returned data</Typography>
|
|
468
|
+
{clickedRow && (
|
|
469
|
+
<div style={{ overflow: 'scroll' }}>
|
|
470
|
+
<DL>
|
|
471
|
+
<DT>Data:</DT>
|
|
472
|
+
<DD>
|
|
473
|
+
<code>{clickedRow}</code>
|
|
474
|
+
</DD>
|
|
475
|
+
</DL>
|
|
476
|
+
</div>
|
|
477
|
+
)}
|
|
478
|
+
</aside>
|
|
479
|
+
</div>
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export default ClickRows;
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
### Selectable Rows
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
import React, { useState, useCallback } from 'react';
|
|
492
|
+
|
|
493
|
+
import { cloneDeep, find } from 'lodash';
|
|
494
|
+
|
|
495
|
+
import Table, { RowRequestToggleHandler } from '@splunk/react-ui/Table';
|
|
496
|
+
|
|
497
|
+
interface Row {
|
|
498
|
+
email: string;
|
|
499
|
+
name: string;
|
|
500
|
+
selected: boolean;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const initialData: Row[] = [
|
|
504
|
+
{ name: 'Rylan', email: 'Angelita_Weimann42@gmail.com', selected: false },
|
|
505
|
+
{ name: 'Amelia', email: 'Dexter.Trantow57@hotmail.com', selected: false },
|
|
506
|
+
{ name: 'Estevan', email: 'Aimee7@hotmail.com', selected: false },
|
|
507
|
+
{ name: 'Florence', email: 'Jarrod.Bernier13@yahoo.com', selected: false },
|
|
508
|
+
{ name: 'Tressa', email: 'Yadira1@hotmail.com', selected: false },
|
|
509
|
+
];
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
function Selectable() {
|
|
513
|
+
const [data, setData] = useState<Row[]>(initialData);
|
|
514
|
+
|
|
515
|
+
const handleToggle: RowRequestToggleHandler = useCallback((event, { email }) => {
|
|
516
|
+
setData((prevData) => {
|
|
517
|
+
const updatedData = cloneDeep(prevData);
|
|
518
|
+
|
|
519
|
+
const selectedRow = find(updatedData, { email });
|
|
520
|
+
if (selectedRow) {
|
|
521
|
+
selectedRow.selected = !selectedRow.selected;
|
|
522
|
+
|
|
523
|
+
return updatedData;
|
|
524
|
+
}
|
|
525
|
+
return [];
|
|
526
|
+
});
|
|
527
|
+
}, []);
|
|
528
|
+
|
|
529
|
+
const getRowSelectionState = useCallback((rows: Row[]): 'none' | 'all' | 'some' => {
|
|
530
|
+
const selectedCount = rows.filter((row) => row.selected).length;
|
|
531
|
+
if (selectedCount === 0) return 'none';
|
|
532
|
+
if (selectedCount === rows.length) return 'all';
|
|
533
|
+
return 'some';
|
|
534
|
+
}, []);
|
|
535
|
+
|
|
536
|
+
const handleToggleAll = useCallback(() => {
|
|
537
|
+
setData((prevData) => {
|
|
538
|
+
const selected = getRowSelectionState(prevData) !== 'all';
|
|
539
|
+
return prevData.map((row) => ({ ...row, selected }));
|
|
540
|
+
});
|
|
541
|
+
}, [getRowSelectionState]);
|
|
542
|
+
|
|
543
|
+
return (
|
|
544
|
+
<div>
|
|
545
|
+
<Table
|
|
546
|
+
onRequestToggleAllRows={handleToggleAll}
|
|
547
|
+
rowSelection={getRowSelectionState(data)}
|
|
548
|
+
>
|
|
549
|
+
<Table.Head>
|
|
550
|
+
<Table.HeadCell>Name</Table.HeadCell>
|
|
551
|
+
<Table.HeadCell>Email</Table.HeadCell>
|
|
552
|
+
</Table.Head>
|
|
553
|
+
<Table.Body>
|
|
554
|
+
{data.map((row) => (
|
|
555
|
+
<Table.Row
|
|
556
|
+
key={row.email}
|
|
557
|
+
onRequestToggle={handleToggle}
|
|
558
|
+
data={row}
|
|
559
|
+
selected={row.selected}
|
|
560
|
+
>
|
|
561
|
+
<Table.Cell>{row.name}</Table.Cell>
|
|
562
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
563
|
+
</Table.Row>
|
|
564
|
+
))}
|
|
565
|
+
</Table.Body>
|
|
566
|
+
</Table>
|
|
567
|
+
</div>
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export default Selectable;
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
### Dropdowns in Header
|
|
577
|
+
|
|
578
|
+
```typescript
|
|
579
|
+
import React, { useState, useCallback } from 'react';
|
|
580
|
+
|
|
581
|
+
import Menu from '@splunk/react-ui/Menu';
|
|
582
|
+
import Table from '@splunk/react-ui/Table';
|
|
583
|
+
|
|
584
|
+
interface Row {
|
|
585
|
+
email: string;
|
|
586
|
+
name: string;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const data: Row[] = [
|
|
590
|
+
{ name: 'Rylan', email: 'Angelita_Weimann42@gmail.com' },
|
|
591
|
+
{ name: 'Amelia', email: 'Dexter.Trantow57@hotmail.com' },
|
|
592
|
+
{ name: 'Estevan', email: 'Aimee7@hotmail.com' },
|
|
593
|
+
{ name: 'Florence', email: 'Jarrod.Bernier13@yahoo.com' },
|
|
594
|
+
{ name: 'Tressa', email: 'Yadira1@hotmail.com' },
|
|
595
|
+
];
|
|
596
|
+
|
|
597
|
+
interface Column {
|
|
598
|
+
align: 'left' | 'right';
|
|
599
|
+
label: string;
|
|
600
|
+
sortKey: 'email' | 'name';
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const initialColumns: Column[] = [
|
|
604
|
+
{ sortKey: 'name', label: 'Name', align: 'left' },
|
|
605
|
+
{ sortKey: 'email', label: 'Email', align: 'left' },
|
|
606
|
+
];
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
function HeadDropdownCell() {
|
|
610
|
+
const [columns, setColumns] = useState<Column[]>(initialColumns);
|
|
611
|
+
const [sortKey, setSortKey] = useState<'email' | 'name'>('name');
|
|
612
|
+
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');
|
|
613
|
+
|
|
614
|
+
const handleSort = useCallback(
|
|
615
|
+
(
|
|
616
|
+
e: React.MouseEvent,
|
|
617
|
+
{
|
|
618
|
+
sortKey: newSortKey,
|
|
619
|
+
sortDir: newSortDir,
|
|
620
|
+
}: { sortKey: 'email' | 'name'; sortDir: 'asc' | 'desc' }
|
|
621
|
+
) => {
|
|
622
|
+
setSortKey(newSortKey);
|
|
623
|
+
setSortDir(newSortDir);
|
|
624
|
+
},
|
|
625
|
+
[]
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
const handleAlign = useCallback(
|
|
629
|
+
(
|
|
630
|
+
e: React.MouseEvent,
|
|
631
|
+
{
|
|
632
|
+
sortKey: targetSortKey,
|
|
633
|
+
align,
|
|
634
|
+
}: { sortKey: 'email' | 'name'; align: 'left' | 'right' }
|
|
635
|
+
) => {
|
|
636
|
+
setColumns((prevColumns) =>
|
|
637
|
+
prevColumns.map((column) =>
|
|
638
|
+
column.sortKey !== targetSortKey ? column : { ...column, align }
|
|
639
|
+
)
|
|
640
|
+
);
|
|
641
|
+
},
|
|
642
|
+
[]
|
|
643
|
+
);
|
|
644
|
+
|
|
645
|
+
return (
|
|
646
|
+
<Table>
|
|
647
|
+
<Table.Head>
|
|
648
|
+
{columns.map((headData) => (
|
|
649
|
+
<Table.HeadDropdownCell
|
|
650
|
+
label={headData.label}
|
|
651
|
+
key={headData.sortKey}
|
|
652
|
+
align={headData.align}
|
|
653
|
+
>
|
|
654
|
+
<Menu>
|
|
655
|
+
<Menu.Item
|
|
656
|
+
selectable
|
|
657
|
+
selected={headData.sortKey === sortKey && sortDir === 'asc'}
|
|
658
|
+
onClick={(e: React.MouseEvent) =>
|
|
659
|
+
handleSort(e, { sortKey: headData.sortKey, sortDir: 'asc' })
|
|
660
|
+
}
|
|
661
|
+
>
|
|
662
|
+
Sort Ascending
|
|
663
|
+
</Menu.Item>
|
|
664
|
+
<Menu.Item
|
|
665
|
+
selectable
|
|
666
|
+
selected={headData.sortKey === sortKey && sortDir === 'desc'}
|
|
667
|
+
onClick={(e: React.MouseEvent) =>
|
|
668
|
+
handleSort(e, { sortKey: headData.sortKey, sortDir: 'desc' })
|
|
669
|
+
}
|
|
670
|
+
>
|
|
671
|
+
Sort Descending
|
|
672
|
+
</Menu.Item>
|
|
673
|
+
<Menu.Divider />
|
|
674
|
+
<Menu.Item
|
|
675
|
+
selectable
|
|
676
|
+
selected={headData.align === 'left'}
|
|
677
|
+
onClick={(e: React.MouseEvent) =>
|
|
678
|
+
handleAlign(e, { sortKey: headData.sortKey, align: 'left' })
|
|
679
|
+
}
|
|
680
|
+
>
|
|
681
|
+
Align Left
|
|
682
|
+
</Menu.Item>
|
|
683
|
+
<Menu.Item
|
|
684
|
+
selectable
|
|
685
|
+
selected={headData.align === 'right'}
|
|
686
|
+
onClick={(e: React.MouseEvent) =>
|
|
687
|
+
handleAlign(e, { sortKey: headData.sortKey, align: 'right' })
|
|
688
|
+
}
|
|
689
|
+
>
|
|
690
|
+
Align Right
|
|
691
|
+
</Menu.Item>
|
|
692
|
+
</Menu>
|
|
693
|
+
</Table.HeadDropdownCell>
|
|
694
|
+
))}
|
|
695
|
+
</Table.Head>
|
|
696
|
+
<Table.Body>
|
|
697
|
+
{data
|
|
698
|
+
.sort((rowA, rowB) => {
|
|
699
|
+
if (sortDir === 'asc') {
|
|
700
|
+
return rowA[sortKey] > rowB[sortKey] ? 1 : -1;
|
|
701
|
+
}
|
|
702
|
+
return rowB[sortKey] > rowA[sortKey] ? 1 : -1;
|
|
703
|
+
})
|
|
704
|
+
.map((row) => (
|
|
705
|
+
<Table.Row key={row.email}>
|
|
706
|
+
<Table.Cell align={columns[0].align}>{row.name}</Table.Cell>
|
|
707
|
+
<Table.Cell align={columns[1].align}>{row.email}</Table.Cell>
|
|
708
|
+
</Table.Row>
|
|
709
|
+
))}
|
|
710
|
+
</Table.Body>
|
|
711
|
+
</Table>
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
export default HeadDropdownCell;
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
### Filter Column Values
|
|
721
|
+
|
|
722
|
+
```typescript
|
|
723
|
+
import React, { useState } from 'react';
|
|
724
|
+
|
|
725
|
+
import Filter from '@splunk/react-icons/Filter';
|
|
726
|
+
import Menu from '@splunk/react-ui/Menu';
|
|
727
|
+
import Table from '@splunk/react-ui/Table';
|
|
728
|
+
|
|
729
|
+
const kindValues = ['Amazon S3', 'Index', 'Indexer', 'Lookup', 'View'] as const;
|
|
730
|
+
|
|
731
|
+
type Kind = (typeof kindValues)[number];
|
|
732
|
+
|
|
733
|
+
interface Row {
|
|
734
|
+
name: string;
|
|
735
|
+
kind: Kind;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const data: Row[] = [
|
|
739
|
+
{ name: 'S3_bucket_1', kind: 'Amazon S3' },
|
|
740
|
+
{ name: 'S3_bucket_2', kind: 'Amazon S3' },
|
|
741
|
+
{ name: 'Splunk_CMP_1', kind: 'Index' },
|
|
742
|
+
{ name: 'EC_1', kind: 'View' },
|
|
743
|
+
{ name: 'EC_2', kind: 'View' },
|
|
744
|
+
];
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
function FilterableTable() {
|
|
748
|
+
const [filter, setFilter] = useState<Kind[]>([]);
|
|
749
|
+
|
|
750
|
+
const toggleFilterValue = (filterValue: Kind) => {
|
|
751
|
+
setFilter((prevFilter) =>
|
|
752
|
+
prevFilter.includes(filterValue)
|
|
753
|
+
? prevFilter.filter((f) => f !== filterValue)
|
|
754
|
+
: [...prevFilter, filterValue]
|
|
755
|
+
);
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
return (
|
|
759
|
+
<Table>
|
|
760
|
+
<Table.Head>
|
|
761
|
+
<Table.HeadCell>Name</Table.HeadCell>
|
|
762
|
+
<Table.HeadDropdownCell
|
|
763
|
+
label={
|
|
764
|
+
<>
|
|
765
|
+
<Filter variant={filter.length > 0 ? 'filled' : 'outlined'} />
|
|
766
|
+
Kind
|
|
767
|
+
{filter.length > 0 ? ` ${filter.length}/${kindValues.length}` : ''}
|
|
768
|
+
</>
|
|
769
|
+
}
|
|
770
|
+
>
|
|
771
|
+
<Menu>
|
|
772
|
+
{kindValues.map((kind) => (
|
|
773
|
+
<Menu.Item
|
|
774
|
+
key={kind}
|
|
775
|
+
selectableAppearance="checkbox"
|
|
776
|
+
selectable
|
|
777
|
+
selected={filter.includes(kind)}
|
|
778
|
+
onClick={() => toggleFilterValue(kind)}
|
|
779
|
+
>
|
|
780
|
+
{kind}
|
|
781
|
+
</Menu.Item>
|
|
782
|
+
))}
|
|
783
|
+
</Menu>
|
|
784
|
+
</Table.HeadDropdownCell>
|
|
785
|
+
</Table.Head>
|
|
786
|
+
<Table.Body>
|
|
787
|
+
{data
|
|
788
|
+
.filter((row) => filter.length === 0 || filter.includes(row.kind))
|
|
789
|
+
.map((row) => (
|
|
790
|
+
<Table.Row key={row.name}>
|
|
791
|
+
<Table.Cell>{row.name}</Table.Cell>
|
|
792
|
+
<Table.Cell>{row.kind}</Table.Cell>
|
|
793
|
+
</Table.Row>
|
|
794
|
+
))}
|
|
795
|
+
</Table.Body>
|
|
796
|
+
</Table>
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
export default FilterableTable;
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
### Fixed Header
|
|
806
|
+
|
|
807
|
+
```typescript
|
|
808
|
+
import React from 'react';
|
|
809
|
+
|
|
810
|
+
import Table from '@splunk/react-ui/Table';
|
|
811
|
+
|
|
812
|
+
interface Row {
|
|
813
|
+
email: string;
|
|
814
|
+
name: string;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const data: Row[] = [
|
|
818
|
+
{ name: 'Rylan', email: 'Angelita_Weimann42@gmail.com' },
|
|
819
|
+
{ name: 'Amelia', email: 'Dexter.Trantow57@hotmail.com' },
|
|
820
|
+
{ name: 'Estevan', email: 'Aimee7@hotmail.com' },
|
|
821
|
+
{ name: 'Florence', email: 'Jarrod.Bernier13@yahoo.com' },
|
|
822
|
+
{ name: 'Tressa', email: 'Yadira1@hotmail.com' },
|
|
823
|
+
{ name: 'Bernice', email: 'bernice.Gilbert@gmail.com' },
|
|
824
|
+
{ name: 'Adrian', email: 'adrian7456@gmail.com' },
|
|
825
|
+
{ name: 'Ester', email: 'esternyc@gmail.com' },
|
|
826
|
+
{ name: 'Andrew', email: 'andrew.fillmore2@gmail.com' },
|
|
827
|
+
{ name: 'Felix', email: 'felixfelix@hotmail.com' },
|
|
828
|
+
];
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
function FixedHeader() {
|
|
832
|
+
return (
|
|
833
|
+
<Table headType="fixed" innerStyle={{ maxHeight: 160 }}>
|
|
834
|
+
<Table.Head>
|
|
835
|
+
<Table.HeadCell>Name</Table.HeadCell>
|
|
836
|
+
<Table.HeadCell>Email</Table.HeadCell>
|
|
837
|
+
</Table.Head>
|
|
838
|
+
<Table.Body>
|
|
839
|
+
{data.map((row) => (
|
|
840
|
+
<Table.Row key={row.email}>
|
|
841
|
+
<Table.Cell>{row.name}</Table.Cell>
|
|
842
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
843
|
+
</Table.Row>
|
|
844
|
+
))}
|
|
845
|
+
</Table.Body>
|
|
846
|
+
</Table>
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
export default FixedHeader;
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
### Expandable Rows
|
|
856
|
+
|
|
857
|
+
```typescript
|
|
858
|
+
import React from 'react';
|
|
859
|
+
|
|
860
|
+
import DL from '@splunk/react-ui/DefinitionList';
|
|
861
|
+
import Table from '@splunk/react-ui/Table';
|
|
862
|
+
|
|
863
|
+
interface Row {
|
|
864
|
+
email: string;
|
|
865
|
+
name: string;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
const data: Row[] = [
|
|
869
|
+
{ name: 'Rylan', email: 'Angelita_Weimann42@gmail.com' },
|
|
870
|
+
{ name: 'Amelia', email: 'Dexter.Trantow57@hotmail.com' },
|
|
871
|
+
{ name: 'Estevan', email: 'Aimee7@hotmail.com' },
|
|
872
|
+
{ name: 'Florence', email: 'Jarrod.Bernier13@yahoo.com' },
|
|
873
|
+
{ name: 'Tressa', email: 'Yadira1@hotmail.com' },
|
|
874
|
+
];
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
function getExpansionRow(row: Row) {
|
|
878
|
+
return (
|
|
879
|
+
<Table.Row key={`${row.email}-expansion`}>
|
|
880
|
+
<Table.Cell style={{ borderTop: 'none' }} colSpan={2}>
|
|
881
|
+
<DL>
|
|
882
|
+
<DL.Term>Name</DL.Term>
|
|
883
|
+
<DL.Description>{row.name}</DL.Description>
|
|
884
|
+
<DL.Term>Email</DL.Term>
|
|
885
|
+
<DL.Description>{row.email}</DL.Description>
|
|
886
|
+
</DL>
|
|
887
|
+
</Table.Cell>
|
|
888
|
+
</Table.Row>
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
function ExpandableRows() {
|
|
893
|
+
return (
|
|
894
|
+
<Table rowExpansion="single">
|
|
895
|
+
<Table.Head>
|
|
896
|
+
<Table.HeadCell>Name</Table.HeadCell>
|
|
897
|
+
<Table.HeadCell>Email</Table.HeadCell>
|
|
898
|
+
</Table.Head>
|
|
899
|
+
<Table.Body>
|
|
900
|
+
{data.map((row) => (
|
|
901
|
+
<Table.Row key={row.email} expansionRow={getExpansionRow(row)}>
|
|
902
|
+
<Table.Cell>{row.name}</Table.Cell>
|
|
903
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
904
|
+
</Table.Row>
|
|
905
|
+
))}
|
|
906
|
+
</Table.Body>
|
|
907
|
+
</Table>
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
export default ExpandableRows;
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
### Expandable Rows - Controlled
|
|
917
|
+
|
|
918
|
+
```typescript
|
|
919
|
+
import React, { useState } from 'react';
|
|
920
|
+
|
|
921
|
+
import DL from '@splunk/react-ui/DefinitionList';
|
|
922
|
+
import Table from '@splunk/react-ui/Table';
|
|
923
|
+
|
|
924
|
+
interface Row {
|
|
925
|
+
id: string;
|
|
926
|
+
email: string;
|
|
927
|
+
name: string;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
const data: Row[] = [
|
|
931
|
+
{ id: 'unique-0', name: 'Rylan', email: 'Angelita_Weimann42@gmail.com' },
|
|
932
|
+
{ id: 'unique-1', name: 'Amelia', email: 'Dexter.Trantow57@hotmail.com' },
|
|
933
|
+
{ id: 'unique-2', name: 'Estevan', email: 'Aimee7@hotmail.com' },
|
|
934
|
+
{ id: 'unique-3', name: 'Florence', email: 'Jarrod.Bernier13@yahoo.com' },
|
|
935
|
+
{ id: 'unique-4', name: 'Tressa', email: 'Yadira1@hotmail.com' },
|
|
936
|
+
];
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
function getExpansionRow(row: Row) {
|
|
940
|
+
return (
|
|
941
|
+
<Table.Row key={`${row.email}-expansion`}>
|
|
942
|
+
<Table.Cell style={{ borderTop: 'none' }} colSpan={2}>
|
|
943
|
+
<DL>
|
|
944
|
+
<DL.Term>Name</DL.Term>
|
|
945
|
+
<DL.Description>{row.name}</DL.Description>
|
|
946
|
+
<DL.Term>Email</DL.Term>
|
|
947
|
+
<DL.Description>{row.email}</DL.Description>
|
|
948
|
+
</DL>
|
|
949
|
+
</Table.Cell>
|
|
950
|
+
</Table.Row>
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
function ExpandableRowsControlled() {
|
|
955
|
+
const [expandedRowId, setExpandedRowId] = useState(null as string | null);
|
|
956
|
+
|
|
957
|
+
const handleRowExpansion = (rowId: string) => {
|
|
958
|
+
if (expandedRowId === rowId) {
|
|
959
|
+
setExpandedRowId(null);
|
|
960
|
+
} else {
|
|
961
|
+
setExpandedRowId(rowId);
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
return (
|
|
966
|
+
<Table rowExpansion="controlled">
|
|
967
|
+
<Table.Head>
|
|
968
|
+
<Table.HeadCell>Name</Table.HeadCell>
|
|
969
|
+
<Table.HeadCell>Email</Table.HeadCell>
|
|
970
|
+
</Table.Head>
|
|
971
|
+
<Table.Body>
|
|
972
|
+
{data.map((row) => (
|
|
973
|
+
<Table.Row
|
|
974
|
+
key={row.id}
|
|
975
|
+
expansionRow={getExpansionRow(row)}
|
|
976
|
+
onExpansion={() => handleRowExpansion(row.id)}
|
|
977
|
+
expanded={row.id === expandedRowId}
|
|
978
|
+
>
|
|
979
|
+
<Table.Cell>{row.name}</Table.Cell>
|
|
980
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
981
|
+
</Table.Row>
|
|
982
|
+
))}
|
|
983
|
+
</Table.Body>
|
|
984
|
+
</Table>
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
export default ExpandableRowsControlled;
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
### Row actions
|
|
994
|
+
|
|
995
|
+
```typescript
|
|
996
|
+
import React, { useState, useCallback } from 'react';
|
|
997
|
+
|
|
998
|
+
import Cog from '@splunk/react-icons/Cog';
|
|
999
|
+
import Pencil from '@splunk/react-icons/Pencil';
|
|
1000
|
+
import Button from '@splunk/react-ui/Button';
|
|
1001
|
+
import DL, { Term as DT, Description as DD } from '@splunk/react-ui/DefinitionList';
|
|
1002
|
+
import Dropdown from '@splunk/react-ui/Dropdown';
|
|
1003
|
+
import Menu from '@splunk/react-ui/Menu';
|
|
1004
|
+
import Table, {
|
|
1005
|
+
RowActionPrimaryClickHandler,
|
|
1006
|
+
RowActionSecondaryClickHandler,
|
|
1007
|
+
} from '@splunk/react-ui/Table';
|
|
1008
|
+
import Tooltip from '@splunk/react-ui/Tooltip';
|
|
1009
|
+
import Typography from '@splunk/react-ui/Typography';
|
|
1010
|
+
import { _ } from '@splunk/ui-utils/i18n';
|
|
1011
|
+
|
|
1012
|
+
interface Row {
|
|
1013
|
+
age: number;
|
|
1014
|
+
country: string;
|
|
1015
|
+
email: string;
|
|
1016
|
+
favoriteColor: string;
|
|
1017
|
+
favoriteDay: string;
|
|
1018
|
+
industry: string;
|
|
1019
|
+
name: string;
|
|
1020
|
+
occupation: string;
|
|
1021
|
+
state: string;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
interface Column {
|
|
1025
|
+
label: string;
|
|
1026
|
+
name: keyof Row;
|
|
1027
|
+
visible: boolean;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
const initialData: Row[] = [
|
|
1031
|
+
{
|
|
1032
|
+
name: 'Rylan',
|
|
1033
|
+
age: 42,
|
|
1034
|
+
email: 'Angelita_Weimann42@gmail.com',
|
|
1035
|
+
state: 'CA',
|
|
1036
|
+
country: 'US',
|
|
1037
|
+
favoriteColor: 'Orange',
|
|
1038
|
+
favoriteDay: 'Monday',
|
|
1039
|
+
occupation: 'Engineer',
|
|
1040
|
+
industry: 'Software Development',
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
name: 'Amelia',
|
|
1044
|
+
age: 24,
|
|
1045
|
+
email: 'Dexter.Trantow57@hotmail.com',
|
|
1046
|
+
state: 'NY',
|
|
1047
|
+
country: 'US',
|
|
1048
|
+
favoriteColor: 'White',
|
|
1049
|
+
favoriteDay: 'Friday',
|
|
1050
|
+
occupation: 'Engineer',
|
|
1051
|
+
industry: 'Software',
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
name: 'Estevan',
|
|
1055
|
+
age: 56,
|
|
1056
|
+
email: 'Aimee7@hotmail.com',
|
|
1057
|
+
state: 'IL',
|
|
1058
|
+
country: 'US',
|
|
1059
|
+
favoriteColor: 'Green',
|
|
1060
|
+
favoriteDay: 'Saturday',
|
|
1061
|
+
occupation: 'Engineer',
|
|
1062
|
+
industry: 'Software',
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
name: 'Florence',
|
|
1066
|
+
age: 71,
|
|
1067
|
+
email: 'Jarrod.Bernier13@yahoo.com',
|
|
1068
|
+
state: 'CA',
|
|
1069
|
+
country: 'US',
|
|
1070
|
+
favoriteColor: 'Red',
|
|
1071
|
+
favoriteDay: 'Tuesday',
|
|
1072
|
+
occupation: 'Engineer',
|
|
1073
|
+
industry: 'Software',
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
name: 'Teresa',
|
|
1077
|
+
age: 38,
|
|
1078
|
+
email: 'Yadira1@hotmail.com',
|
|
1079
|
+
state: 'NJ',
|
|
1080
|
+
country: 'US',
|
|
1081
|
+
favoriteColor: 'Blue',
|
|
1082
|
+
favoriteDay: 'Wednesday',
|
|
1083
|
+
occupation: 'Engineer',
|
|
1084
|
+
industry: 'Software',
|
|
1085
|
+
},
|
|
1086
|
+
];
|
|
1087
|
+
|
|
1088
|
+
const initialColumns: Column[] = [
|
|
1089
|
+
{ name: 'name', label: 'Name', visible: true },
|
|
1090
|
+
{ name: 'age', label: 'Age', visible: true },
|
|
1091
|
+
{ name: 'email', label: 'Email', visible: true },
|
|
1092
|
+
{ name: 'state', label: 'State', visible: true },
|
|
1093
|
+
{ name: 'country', label: 'Country', visible: true },
|
|
1094
|
+
{ name: 'favoriteColor', label: 'Favorite Color', visible: true },
|
|
1095
|
+
{ name: 'favoriteDay', label: 'Favorite Day', visible: true },
|
|
1096
|
+
{ name: 'occupation', label: 'Occupation', visible: true },
|
|
1097
|
+
{ name: 'industry', label: 'Industry', visible: true },
|
|
1098
|
+
];
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
function RowAction() {
|
|
1102
|
+
const [data] = useState<Row[]>(initialData);
|
|
1103
|
+
const [columns, setColumns] = useState<Column[]>(initialColumns);
|
|
1104
|
+
const [primaryAction, setPrimaryAction] = useState<string | undefined>();
|
|
1105
|
+
const [primaryActionRowData, setPrimaryActionRowData] = useState<string | undefined>();
|
|
1106
|
+
const [secondaryAction, setSecondaryAction] = useState<string | undefined>();
|
|
1107
|
+
const [secondaryActionRowData, setSecondaryActionRowData] = useState<string | undefined>();
|
|
1108
|
+
|
|
1109
|
+
const handleShowHide = useCallback((e: React.MouseEvent, { name }: { name: string }) => {
|
|
1110
|
+
setColumns((prevColumns) =>
|
|
1111
|
+
prevColumns.map((col) => (col.name === name ? { ...col, visible: !col.visible } : col))
|
|
1112
|
+
);
|
|
1113
|
+
}, []);
|
|
1114
|
+
|
|
1115
|
+
const handleEditActionClick: RowActionPrimaryClickHandler = useCallback((e, rowData) => {
|
|
1116
|
+
setPrimaryAction('Edit');
|
|
1117
|
+
setPrimaryActionRowData(JSON.stringify(rowData));
|
|
1118
|
+
}, []);
|
|
1119
|
+
|
|
1120
|
+
const handleSaveActionClick: RowActionSecondaryClickHandler = useCallback((e, rowData) => {
|
|
1121
|
+
setSecondaryAction('Save');
|
|
1122
|
+
setSecondaryActionRowData(JSON.stringify(rowData));
|
|
1123
|
+
}, []);
|
|
1124
|
+
|
|
1125
|
+
const handleAddActionClick: RowActionSecondaryClickHandler = useCallback((e, rowData) => {
|
|
1126
|
+
setSecondaryAction('Add');
|
|
1127
|
+
setSecondaryActionRowData(JSON.stringify(rowData));
|
|
1128
|
+
}, []);
|
|
1129
|
+
|
|
1130
|
+
const handleDeleteActionClick: RowActionSecondaryClickHandler = useCallback((e, rowData) => {
|
|
1131
|
+
setSecondaryAction('Delete');
|
|
1132
|
+
setSecondaryActionRowData(JSON.stringify(rowData));
|
|
1133
|
+
}, []);
|
|
1134
|
+
|
|
1135
|
+
const toggle = (
|
|
1136
|
+
<Button appearance="subtle" data-test="actions-toggle" icon={<Cog variant="filled" />} />
|
|
1137
|
+
);
|
|
1138
|
+
|
|
1139
|
+
const actions = [
|
|
1140
|
+
<Dropdown toggle={toggle} key="settings">
|
|
1141
|
+
<Menu>
|
|
1142
|
+
<Menu.Heading>Show/Hide Columns</Menu.Heading>
|
|
1143
|
+
{columns.map((col) => (
|
|
1144
|
+
<Menu.Item
|
|
1145
|
+
key={col.name}
|
|
1146
|
+
selectable
|
|
1147
|
+
selected={col.visible}
|
|
1148
|
+
onClick={(e: React.MouseEvent) => handleShowHide(e, { name: col.name })}
|
|
1149
|
+
>
|
|
1150
|
+
{col.label}
|
|
1151
|
+
</Menu.Item>
|
|
1152
|
+
))}
|
|
1153
|
+
<Menu.Divider />
|
|
1154
|
+
<Menu.Heading>More actions</Menu.Heading>
|
|
1155
|
+
<Menu.Item>Add new item</Menu.Item>
|
|
1156
|
+
</Menu>
|
|
1157
|
+
</Dropdown>,
|
|
1158
|
+
];
|
|
1159
|
+
|
|
1160
|
+
const rowActionPrimaryButton = (
|
|
1161
|
+
<Tooltip
|
|
1162
|
+
content={_('Edit')}
|
|
1163
|
+
contentRelationship="label"
|
|
1164
|
+
onClick={handleEditActionClick}
|
|
1165
|
+
style={{ marginRight: 8 }}
|
|
1166
|
+
>
|
|
1167
|
+
<Button appearance="subtle" icon={<Pencil variant="filled" />} />
|
|
1168
|
+
</Tooltip>
|
|
1169
|
+
);
|
|
1170
|
+
|
|
1171
|
+
const rowActionsSecondaryMenu = (
|
|
1172
|
+
<Menu>
|
|
1173
|
+
<Menu.Item onClick={handleSaveActionClick}>Save</Menu.Item>
|
|
1174
|
+
<Menu.Item onClick={handleAddActionClick}>Add</Menu.Item>
|
|
1175
|
+
<Menu.Item onClick={handleDeleteActionClick}>Delete</Menu.Item>
|
|
1176
|
+
</Menu>
|
|
1177
|
+
);
|
|
1178
|
+
|
|
1179
|
+
return (
|
|
1180
|
+
<div>
|
|
1181
|
+
<Table actions={actions} actionsColumnWidth={104}>
|
|
1182
|
+
<Table.Head>
|
|
1183
|
+
{columns.map(
|
|
1184
|
+
(c) => c.visible && <Table.HeadCell key={c.name}>{c.label}</Table.HeadCell>
|
|
1185
|
+
)}
|
|
1186
|
+
</Table.Head>
|
|
1187
|
+
<Table.Body>
|
|
1188
|
+
{data.map((row) => (
|
|
1189
|
+
<Table.Row
|
|
1190
|
+
data={row}
|
|
1191
|
+
key={row.email}
|
|
1192
|
+
onClick={() => {}}
|
|
1193
|
+
actionPrimary={rowActionPrimaryButton}
|
|
1194
|
+
actionsSecondary={rowActionsSecondaryMenu}
|
|
1195
|
+
>
|
|
1196
|
+
{columns.map(
|
|
1197
|
+
(c) =>
|
|
1198
|
+
c.visible && <Table.Cell key={c.name}>{row[c.name]}</Table.Cell>
|
|
1199
|
+
)}
|
|
1200
|
+
</Table.Row>
|
|
1201
|
+
))}
|
|
1202
|
+
</Table.Body>
|
|
1203
|
+
</Table>
|
|
1204
|
+
<aside style={{ marginTop: 20 }} aria-live="polite" aria-relevant="text">
|
|
1205
|
+
<Typography as="p">
|
|
1206
|
+
Click a primary action or secondary action to see the returned data
|
|
1207
|
+
</Typography>
|
|
1208
|
+
{primaryActionRowData && (
|
|
1209
|
+
<div style={{ overflow: 'scroll', marginBottom: 10 }}>
|
|
1210
|
+
<DL>
|
|
1211
|
+
<DT>Primary action:</DT>
|
|
1212
|
+
<DD>'{primaryAction}'</DD>
|
|
1213
|
+
<DT>Data:</DT>
|
|
1214
|
+
<DD>
|
|
1215
|
+
<code>{primaryActionRowData}</code>
|
|
1216
|
+
</DD>
|
|
1217
|
+
</DL>
|
|
1218
|
+
</div>
|
|
1219
|
+
)}
|
|
1220
|
+
{secondaryActionRowData && (
|
|
1221
|
+
<div style={{ overflow: 'scroll' }}>
|
|
1222
|
+
<DL>
|
|
1223
|
+
<DT>Secondary action:</DT>
|
|
1224
|
+
<DD>'{secondaryAction}'</DD>
|
|
1225
|
+
<DT>Data:</DT>
|
|
1226
|
+
<DD>
|
|
1227
|
+
<code>{secondaryActionRowData}</code>
|
|
1228
|
+
</DD>
|
|
1229
|
+
</DL>
|
|
1230
|
+
</div>
|
|
1231
|
+
)}
|
|
1232
|
+
</aside>
|
|
1233
|
+
</div>
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
export default RowAction;
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
### Pin action column
|
|
1243
|
+
|
|
1244
|
+
```typescript
|
|
1245
|
+
import React, { useState } from 'react';
|
|
1246
|
+
|
|
1247
|
+
import { noop } from 'lodash';
|
|
1248
|
+
|
|
1249
|
+
import Pencil from '@splunk/react-icons/Pencil';
|
|
1250
|
+
import Button from '@splunk/react-ui/Button';
|
|
1251
|
+
import Table from '@splunk/react-ui/Table';
|
|
1252
|
+
import Tooltip from '@splunk/react-ui/Tooltip';
|
|
1253
|
+
import { _ } from '@splunk/ui-utils/i18n';
|
|
1254
|
+
|
|
1255
|
+
interface Row {
|
|
1256
|
+
age: number;
|
|
1257
|
+
country: string;
|
|
1258
|
+
email: string;
|
|
1259
|
+
favoriteColor: string;
|
|
1260
|
+
favoriteDay: string;
|
|
1261
|
+
industry: string;
|
|
1262
|
+
name: string;
|
|
1263
|
+
occupation: string;
|
|
1264
|
+
state: string;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
interface Column {
|
|
1268
|
+
label: string;
|
|
1269
|
+
name: keyof Row;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
const initialData: Row[] = [
|
|
1273
|
+
{
|
|
1274
|
+
name: 'Rylan',
|
|
1275
|
+
age: 42,
|
|
1276
|
+
email: 'Angelita_Weimann42@gmail.com',
|
|
1277
|
+
state: 'CA',
|
|
1278
|
+
country: 'US',
|
|
1279
|
+
favoriteColor: 'Orange',
|
|
1280
|
+
favoriteDay: 'Monday',
|
|
1281
|
+
occupation: 'Engineer',
|
|
1282
|
+
industry: 'Software Development',
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
name: 'Amelia',
|
|
1286
|
+
age: 24,
|
|
1287
|
+
email: 'Dexter.Trantow57@hotmail.com',
|
|
1288
|
+
state: 'NY',
|
|
1289
|
+
country: 'US',
|
|
1290
|
+
favoriteColor: 'White',
|
|
1291
|
+
favoriteDay: 'Friday',
|
|
1292
|
+
occupation: 'Engineer',
|
|
1293
|
+
industry: 'Software',
|
|
1294
|
+
},
|
|
1295
|
+
{
|
|
1296
|
+
name: 'Estevan',
|
|
1297
|
+
age: 56,
|
|
1298
|
+
email: 'Aimee7@hotmail.com',
|
|
1299
|
+
state: 'IL',
|
|
1300
|
+
country: 'US',
|
|
1301
|
+
favoriteColor: 'Green',
|
|
1302
|
+
favoriteDay: 'Saturday',
|
|
1303
|
+
occupation: 'Engineer',
|
|
1304
|
+
industry: 'Software',
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
name: 'Florence',
|
|
1308
|
+
age: 71,
|
|
1309
|
+
email: 'Jarrod.Bernier13@yahoo.com',
|
|
1310
|
+
state: 'CA',
|
|
1311
|
+
country: 'US',
|
|
1312
|
+
favoriteColor: 'Red',
|
|
1313
|
+
favoriteDay: 'Tuesday',
|
|
1314
|
+
occupation: 'Engineer',
|
|
1315
|
+
industry: 'Software',
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
name: 'Teresa',
|
|
1319
|
+
age: 38,
|
|
1320
|
+
email: 'Yadira1@hotmail.com',
|
|
1321
|
+
state: 'NJ',
|
|
1322
|
+
country: 'US',
|
|
1323
|
+
favoriteColor: 'Blue',
|
|
1324
|
+
favoriteDay: 'Wednesday',
|
|
1325
|
+
occupation: 'Engineer',
|
|
1326
|
+
industry: 'Software',
|
|
1327
|
+
},
|
|
1328
|
+
];
|
|
1329
|
+
|
|
1330
|
+
const initialColumns: Column[] = [
|
|
1331
|
+
{ name: 'name', label: 'Name' },
|
|
1332
|
+
{ name: 'age', label: 'Age' },
|
|
1333
|
+
{ name: 'email', label: 'Email' },
|
|
1334
|
+
{ name: 'state', label: 'State' },
|
|
1335
|
+
{ name: 'country', label: 'Country' },
|
|
1336
|
+
{ name: 'favoriteColor', label: 'Favorite Color' },
|
|
1337
|
+
{ name: 'favoriteDay', label: 'Favorite Day' },
|
|
1338
|
+
{ name: 'occupation', label: 'Occupation' },
|
|
1339
|
+
{ name: 'industry', label: 'Industry' },
|
|
1340
|
+
];
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
function PinActionColumn() {
|
|
1344
|
+
const [data] = useState<Row[]>(initialData);
|
|
1345
|
+
const [columns] = useState<Column[]>(initialColumns);
|
|
1346
|
+
|
|
1347
|
+
const rowActionPrimaryButton = (
|
|
1348
|
+
<Tooltip content={_('Edit')} contentRelationship="label" onClick={noop}>
|
|
1349
|
+
<Button appearance="subtle" icon={<Pencil variant="filled" />} />
|
|
1350
|
+
</Tooltip>
|
|
1351
|
+
);
|
|
1352
|
+
|
|
1353
|
+
return (
|
|
1354
|
+
<div>
|
|
1355
|
+
<Table
|
|
1356
|
+
outerStyle={{ width: 800 }}
|
|
1357
|
+
horizontalOverflow="scroll"
|
|
1358
|
+
pinnedColumns={{ actions: true }}
|
|
1359
|
+
actionsColumnWidth={45}
|
|
1360
|
+
>
|
|
1361
|
+
<Table.Head>
|
|
1362
|
+
{columns.map((c) => (
|
|
1363
|
+
<Table.HeadCell key={c.name}>{c.label}</Table.HeadCell>
|
|
1364
|
+
))}
|
|
1365
|
+
</Table.Head>
|
|
1366
|
+
<Table.Body>
|
|
1367
|
+
{data.map((row) => (
|
|
1368
|
+
<Table.Row
|
|
1369
|
+
data={row}
|
|
1370
|
+
key={row.email}
|
|
1371
|
+
actionPrimary={rowActionPrimaryButton}
|
|
1372
|
+
>
|
|
1373
|
+
{columns.map((c) => (
|
|
1374
|
+
<Table.Cell key={c.name}>{row[c.name]}</Table.Cell>
|
|
1375
|
+
))}
|
|
1376
|
+
</Table.Row>
|
|
1377
|
+
))}
|
|
1378
|
+
</Table.Body>
|
|
1379
|
+
</Table>
|
|
1380
|
+
</div>
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
export default PinActionColumn;
|
|
1385
|
+
```
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
|
|
1389
|
+
### Reorder Rows
|
|
1390
|
+
|
|
1391
|
+
```typescript
|
|
1392
|
+
import React, { useState, useCallback } from 'react';
|
|
1393
|
+
|
|
1394
|
+
import { cloneDeep } from 'lodash';
|
|
1395
|
+
|
|
1396
|
+
import Table, { TableRequestMoveRowHandler } from '@splunk/react-ui/Table';
|
|
1397
|
+
|
|
1398
|
+
interface Row {
|
|
1399
|
+
age: number;
|
|
1400
|
+
email: string;
|
|
1401
|
+
name: string;
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
interface Header {
|
|
1405
|
+
key: 'age' | 'email' | 'name';
|
|
1406
|
+
label: string;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
const initialHeaders: Header[] = [
|
|
1410
|
+
{ label: 'Name', key: 'name' },
|
|
1411
|
+
{ label: 'Age', key: 'age' },
|
|
1412
|
+
{ label: 'Email', key: 'email' },
|
|
1413
|
+
];
|
|
1414
|
+
|
|
1415
|
+
const initialData: Row[] = [
|
|
1416
|
+
{ name: 'Rylan', age: 12, email: 'Angelita_Weimann42@gmail.com' },
|
|
1417
|
+
{ name: 'Amelia', age: 23, email: 'Dexter.Trantow57@hotmail.com' },
|
|
1418
|
+
{ name: 'Estevan', age: 19, email: 'Aimee7@hotmail.com' },
|
|
1419
|
+
{ name: 'Florence', age: 20, email: 'Jarrod.Bernier13@yahoo.com' },
|
|
1420
|
+
{ name: 'Tressa', age: 22, email: 'Yadira1@hotmail.com' },
|
|
1421
|
+
];
|
|
1422
|
+
|
|
1423
|
+
|
|
1424
|
+
function ReorderRows() {
|
|
1425
|
+
const [headers] = useState<Header[]>(initialHeaders);
|
|
1426
|
+
const [data, setData] = useState<Row[]>(initialData);
|
|
1427
|
+
|
|
1428
|
+
const handleRequestMoveRow: TableRequestMoveRowHandler = useCallback(
|
|
1429
|
+
({ fromIndex, toIndex }) => {
|
|
1430
|
+
setData((prevData) => {
|
|
1431
|
+
const updatedData = cloneDeep(prevData);
|
|
1432
|
+
const rowToMove = data[fromIndex];
|
|
1433
|
+
const insertionIndex = toIndex < fromIndex ? toIndex : toIndex + 1;
|
|
1434
|
+
updatedData.splice(insertionIndex, 0, rowToMove);
|
|
1435
|
+
|
|
1436
|
+
const removalIndex = toIndex < fromIndex ? fromIndex + 1 : fromIndex;
|
|
1437
|
+
updatedData.splice(removalIndex, 1);
|
|
1438
|
+
|
|
1439
|
+
return updatedData;
|
|
1440
|
+
});
|
|
1441
|
+
},
|
|
1442
|
+
[data]
|
|
1443
|
+
);
|
|
1444
|
+
|
|
1445
|
+
return (
|
|
1446
|
+
<Table onRequestMoveRow={handleRequestMoveRow}>
|
|
1447
|
+
<Table.Head>
|
|
1448
|
+
{headers.map((header) => (
|
|
1449
|
+
<Table.HeadCell key={header.key}>{header.label}</Table.HeadCell>
|
|
1450
|
+
))}
|
|
1451
|
+
</Table.Head>
|
|
1452
|
+
<Table.Body>
|
|
1453
|
+
{data.map((row) => (
|
|
1454
|
+
<Table.Row key={row.email}>
|
|
1455
|
+
{headers.map((header) => (
|
|
1456
|
+
<Table.Cell key={row[header.key]}>{row[header.key]}</Table.Cell>
|
|
1457
|
+
))}
|
|
1458
|
+
</Table.Row>
|
|
1459
|
+
))}
|
|
1460
|
+
</Table.Body>
|
|
1461
|
+
</Table>
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
export default ReorderRows;
|
|
1466
|
+
```
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
|
|
1470
|
+
### Reorder Columns
|
|
1471
|
+
|
|
1472
|
+
```typescript
|
|
1473
|
+
import React, { useState, useCallback } from 'react';
|
|
1474
|
+
|
|
1475
|
+
import { cloneDeep } from 'lodash';
|
|
1476
|
+
|
|
1477
|
+
import Table, { TableRequestMoveColumnHandler } from '@splunk/react-ui/Table';
|
|
1478
|
+
|
|
1479
|
+
interface Row {
|
|
1480
|
+
age: number;
|
|
1481
|
+
email: string;
|
|
1482
|
+
name: string;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
interface Header {
|
|
1486
|
+
key: 'age' | 'email' | 'name';
|
|
1487
|
+
label: string;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
const initialHeaders: Header[] = [
|
|
1491
|
+
{ label: 'Name', key: 'name' },
|
|
1492
|
+
{ label: 'Age', key: 'age' },
|
|
1493
|
+
{ label: 'Email', key: 'email' },
|
|
1494
|
+
];
|
|
1495
|
+
|
|
1496
|
+
const initialData: Row[] = [
|
|
1497
|
+
{ name: 'Rylan', age: 12, email: 'Angelita_Weimann42@gmail.com' },
|
|
1498
|
+
{ name: 'Amelia', age: 23, email: 'Dexter.Trantow57@hotmail.com' },
|
|
1499
|
+
{ name: 'Estevan', age: 19, email: 'Aimee7@hotmail.com' },
|
|
1500
|
+
{ name: 'Florence', age: 20, email: 'Jarrod.Bernier13@yahoo.com' },
|
|
1501
|
+
{ name: 'Tressa', age: 22, email: 'Yadira1@hotmail.com' },
|
|
1502
|
+
];
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
function ReorderColumns() {
|
|
1506
|
+
const [headers, setHeaders] = useState<Header[]>(initialHeaders);
|
|
1507
|
+
const [data] = useState<Row[]>(initialData);
|
|
1508
|
+
|
|
1509
|
+
const handleRequestMoveColumn: TableRequestMoveColumnHandler = useCallback(
|
|
1510
|
+
({ fromIndex, toIndex }) => {
|
|
1511
|
+
setHeaders((prevHeaders) => {
|
|
1512
|
+
const updatedHeaders = cloneDeep(prevHeaders);
|
|
1513
|
+
const headerToMove = updatedHeaders[fromIndex];
|
|
1514
|
+
|
|
1515
|
+
const insertionIndex = toIndex < fromIndex ? toIndex : toIndex + 1;
|
|
1516
|
+
updatedHeaders.splice(insertionIndex, 0, headerToMove);
|
|
1517
|
+
|
|
1518
|
+
const removalIndex = toIndex < fromIndex ? fromIndex + 1 : fromIndex;
|
|
1519
|
+
updatedHeaders.splice(removalIndex, 1);
|
|
1520
|
+
|
|
1521
|
+
return updatedHeaders;
|
|
1522
|
+
});
|
|
1523
|
+
},
|
|
1524
|
+
[]
|
|
1525
|
+
);
|
|
1526
|
+
|
|
1527
|
+
return (
|
|
1528
|
+
<Table onRequestMoveColumn={handleRequestMoveColumn}>
|
|
1529
|
+
<Table.Head>
|
|
1530
|
+
{headers.map((header) => (
|
|
1531
|
+
<Table.HeadCell key={header.key}>{header.label}</Table.HeadCell>
|
|
1532
|
+
))}
|
|
1533
|
+
</Table.Head>
|
|
1534
|
+
<Table.Body>
|
|
1535
|
+
{data.map((row) => (
|
|
1536
|
+
<Table.Row key={row.email}>
|
|
1537
|
+
{headers.map((header) => (
|
|
1538
|
+
<Table.Cell key={`${row.email}-${header.key}`}>
|
|
1539
|
+
{row[header.key]}
|
|
1540
|
+
</Table.Cell>
|
|
1541
|
+
))}
|
|
1542
|
+
</Table.Row>
|
|
1543
|
+
))}
|
|
1544
|
+
</Table.Body>
|
|
1545
|
+
</Table>
|
|
1546
|
+
);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
export default ReorderColumns;
|
|
1550
|
+
```
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
|
|
1554
|
+
### Resizable Columns
|
|
1555
|
+
|
|
1556
|
+
```typescript
|
|
1557
|
+
import React, { useState, useCallback } from 'react';
|
|
1558
|
+
|
|
1559
|
+
import { cloneDeep } from 'lodash';
|
|
1560
|
+
|
|
1561
|
+
import Table, { TableRequestResizeColumnHandler } from '@splunk/react-ui/Table';
|
|
1562
|
+
|
|
1563
|
+
interface Row {
|
|
1564
|
+
age: number;
|
|
1565
|
+
email: string;
|
|
1566
|
+
name: string;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
interface Header {
|
|
1570
|
+
key: 'age' | 'email' | 'name';
|
|
1571
|
+
label: string;
|
|
1572
|
+
minWidth: number;
|
|
1573
|
+
width: number;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
const initialHeaders: Header[] = [
|
|
1577
|
+
{ label: 'Name', key: 'name', width: 200, minWidth: 80 },
|
|
1578
|
+
{ label: 'Age', key: 'age', width: 60, minWidth: 40 },
|
|
1579
|
+
{ label: 'Email Address', key: 'email', width: 400, minWidth: 120 },
|
|
1580
|
+
];
|
|
1581
|
+
|
|
1582
|
+
const initialData: Row[] = [
|
|
1583
|
+
{ name: 'Rylan', age: 12, email: 'Angelita_Weimann42@gmail.com' },
|
|
1584
|
+
{ name: 'Amelia', age: 23, email: 'Dexter.Trantow57@hotmail.com' },
|
|
1585
|
+
{ name: 'Estevan', age: 19, email: 'Aimee7@hotmail.com' },
|
|
1586
|
+
{ name: 'Florence', age: 20, email: 'Jarrod.Bernier13@yahoo.com' },
|
|
1587
|
+
{ name: 'Tressa', age: 22, email: 'Yadira1@hotmail.com' },
|
|
1588
|
+
];
|
|
1589
|
+
|
|
1590
|
+
|
|
1591
|
+
function Resizable() {
|
|
1592
|
+
const [headers, setHeaders] = useState<Header[]>(initialHeaders);
|
|
1593
|
+
const [data] = useState<Row[]>(initialData);
|
|
1594
|
+
|
|
1595
|
+
const handleResizeColumn: TableRequestResizeColumnHandler = useCallback(
|
|
1596
|
+
(event, { columnId, index, width }) => {
|
|
1597
|
+
setHeaders((prevHeaders) => {
|
|
1598
|
+
const updatedHeaders = cloneDeep(prevHeaders);
|
|
1599
|
+
|
|
1600
|
+
// min and max widths can be controlled in the callback.
|
|
1601
|
+
const selectedColumn = updatedHeaders.find(({ key }) => key === columnId);
|
|
1602
|
+
if (selectedColumn) {
|
|
1603
|
+
const widthAboveMinimum = Math.max(width, selectedColumn.minWidth);
|
|
1604
|
+
updatedHeaders[index].width = widthAboveMinimum;
|
|
1605
|
+
|
|
1606
|
+
return updatedHeaders;
|
|
1607
|
+
}
|
|
1608
|
+
return [];
|
|
1609
|
+
});
|
|
1610
|
+
},
|
|
1611
|
+
[]
|
|
1612
|
+
);
|
|
1613
|
+
|
|
1614
|
+
return (
|
|
1615
|
+
<Table onRequestResizeColumn={handleResizeColumn}>
|
|
1616
|
+
<Table.Head>
|
|
1617
|
+
{headers.map((header) => (
|
|
1618
|
+
<Table.HeadCell key={header.key} columnId={header.key} width={header.width}>
|
|
1619
|
+
{header.label}
|
|
1620
|
+
</Table.HeadCell>
|
|
1621
|
+
))}
|
|
1622
|
+
</Table.Head>
|
|
1623
|
+
<Table.Body>
|
|
1624
|
+
{data.map((row) => (
|
|
1625
|
+
<Table.Row key={row.email}>
|
|
1626
|
+
{headers.map((header) => (
|
|
1627
|
+
<Table.Cell key={`${row.email}-${header.key}`}>
|
|
1628
|
+
{row[header.key]}
|
|
1629
|
+
</Table.Cell>
|
|
1630
|
+
))}
|
|
1631
|
+
</Table.Row>
|
|
1632
|
+
))}
|
|
1633
|
+
</Table.Body>
|
|
1634
|
+
</Table>
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
export default Resizable;
|
|
1639
|
+
```
|
|
1640
|
+
|
|
1641
|
+
|
|
1642
|
+
|
|
1643
|
+
### Fill Layout with Resizable Columns
|
|
1644
|
+
|
|
1645
|
+
```typescript
|
|
1646
|
+
import React, { useState, useCallback } from 'react';
|
|
1647
|
+
|
|
1648
|
+
import { cloneDeep } from 'lodash';
|
|
1649
|
+
|
|
1650
|
+
import Table, { TableRequestResizeColumnHandler } from '@splunk/react-ui/Table';
|
|
1651
|
+
|
|
1652
|
+
interface Row {
|
|
1653
|
+
age: number;
|
|
1654
|
+
email: string;
|
|
1655
|
+
name: string;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
interface Header {
|
|
1659
|
+
key: 'age' | 'email' | 'name';
|
|
1660
|
+
label: string;
|
|
1661
|
+
minWidth: number;
|
|
1662
|
+
width: number | 'auto';
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
const initialHeaders: Header[] = [
|
|
1666
|
+
{ label: 'Name', key: 'name', width: 100, minWidth: 80 },
|
|
1667
|
+
{ label: 'Age', key: 'age', width: 'auto', minWidth: 60 },
|
|
1668
|
+
{ label: 'Email Address', key: 'email', width: 'auto', minWidth: 120 },
|
|
1669
|
+
];
|
|
1670
|
+
|
|
1671
|
+
const initialData: Row[] = [
|
|
1672
|
+
{ name: 'Rylan', age: 12, email: 'Angelita_Weimann42@gmail.com' },
|
|
1673
|
+
{ name: 'Amelia', age: 23, email: 'Dexter.Trantow57@hotmail.com' },
|
|
1674
|
+
{ name: 'Estevan', age: 19, email: 'Aimee7@hotmail.com' },
|
|
1675
|
+
{ name: 'Florence', age: 20, email: 'Jarrod.Bernier13@yahoo.com' },
|
|
1676
|
+
{ name: 'Tressa', age: 22, email: 'Yadira1@hotmail.com' },
|
|
1677
|
+
];
|
|
1678
|
+
|
|
1679
|
+
|
|
1680
|
+
function ResizableFill() {
|
|
1681
|
+
const [headers, setHeaders] = useState<Header[]>(initialHeaders);
|
|
1682
|
+
const [data] = useState<Row[]>(initialData);
|
|
1683
|
+
|
|
1684
|
+
const handleResizeColumn: TableRequestResizeColumnHandler = useCallback(
|
|
1685
|
+
(event, { columnId, index, width }) => {
|
|
1686
|
+
setHeaders((prevHeaders) => {
|
|
1687
|
+
const updatedHeaders = cloneDeep(prevHeaders);
|
|
1688
|
+
|
|
1689
|
+
// min and max widths can be controlled in the callback.
|
|
1690
|
+
const selectedColumn = updatedHeaders.find(({ key }) => key === columnId);
|
|
1691
|
+
|
|
1692
|
+
if (selectedColumn) {
|
|
1693
|
+
const widthAboveMinimum = Math.max(width, selectedColumn.minWidth);
|
|
1694
|
+
updatedHeaders[index].width = widthAboveMinimum;
|
|
1695
|
+
|
|
1696
|
+
return updatedHeaders;
|
|
1697
|
+
}
|
|
1698
|
+
return [];
|
|
1699
|
+
});
|
|
1700
|
+
},
|
|
1701
|
+
[]
|
|
1702
|
+
);
|
|
1703
|
+
|
|
1704
|
+
return (
|
|
1705
|
+
<section style={{ display: 'block', margin: '0 auto', width: '500px' }}>
|
|
1706
|
+
<Table onRequestResizeColumn={handleResizeColumn} resizableFillLayout>
|
|
1707
|
+
<Table.Head>
|
|
1708
|
+
{headers.map((header) => (
|
|
1709
|
+
<Table.HeadCell key={header.key} columnId={header.key} width={header.width}>
|
|
1710
|
+
{header.label}
|
|
1711
|
+
</Table.HeadCell>
|
|
1712
|
+
))}
|
|
1713
|
+
</Table.Head>
|
|
1714
|
+
<Table.Body>
|
|
1715
|
+
{data.map((row) => (
|
|
1716
|
+
<Table.Row key={row.email}>
|
|
1717
|
+
{headers.map((header) => (
|
|
1718
|
+
<Table.Cell key={row[header.key]}>{row[header.key]}</Table.Cell>
|
|
1719
|
+
))}
|
|
1720
|
+
</Table.Row>
|
|
1721
|
+
))}
|
|
1722
|
+
</Table.Body>
|
|
1723
|
+
</Table>
|
|
1724
|
+
</section>
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
export default ResizableFill;
|
|
1729
|
+
```
|
|
1730
|
+
|
|
1731
|
+
|
|
1732
|
+
|
|
1733
|
+
### Stripe rows
|
|
1734
|
+
|
|
1735
|
+
```typescript
|
|
1736
|
+
import React from 'react';
|
|
1737
|
+
|
|
1738
|
+
import Table from '@splunk/react-ui/Table';
|
|
1739
|
+
|
|
1740
|
+
const data = [
|
|
1741
|
+
{ name: 'Rylan', age: 42, email: 'Angelita_Weimann42@gmail.com' },
|
|
1742
|
+
{ name: 'Amelia', age: 24, email: 'Dexter.Trantow57@hotmail.com' },
|
|
1743
|
+
{ name: 'Estevan', age: 56, email: 'Aimee7@hotmail.com' },
|
|
1744
|
+
{ name: 'Florence', age: 71, email: 'Jarrod.Bernier13@yahoo.com' },
|
|
1745
|
+
{ name: 'Tressa', age: 38, email: 'Yadira1@hotmail.com' },
|
|
1746
|
+
];
|
|
1747
|
+
|
|
1748
|
+
|
|
1749
|
+
function StripeRows() {
|
|
1750
|
+
return (
|
|
1751
|
+
<Table stripeRows>
|
|
1752
|
+
<Table.Head>
|
|
1753
|
+
<Table.HeadCell>Name</Table.HeadCell>
|
|
1754
|
+
<Table.HeadCell align="right">Age</Table.HeadCell>
|
|
1755
|
+
<Table.HeadCell>Email</Table.HeadCell>
|
|
1756
|
+
</Table.Head>
|
|
1757
|
+
<Table.Body>
|
|
1758
|
+
{data.map((row) => (
|
|
1759
|
+
<Table.Row key={row.email}>
|
|
1760
|
+
<Table.Cell>{row.name}</Table.Cell>
|
|
1761
|
+
<Table.Cell align="right">{row.age}</Table.Cell>
|
|
1762
|
+
<Table.Cell>{row.email}</Table.Cell>
|
|
1763
|
+
</Table.Row>
|
|
1764
|
+
))}
|
|
1765
|
+
</Table.Body>
|
|
1766
|
+
</Table>
|
|
1767
|
+
);
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
export default StripeRows;
|
|
1771
|
+
```
|
|
1772
|
+
|
|
1773
|
+
|
|
1774
|
+
|
|
1775
|
+
### Horizontal overflow scroll
|
|
1776
|
+
|
|
1777
|
+
```typescript
|
|
1778
|
+
import React, { useState } from 'react';
|
|
1779
|
+
|
|
1780
|
+
import Table from '@splunk/react-ui/Table';
|
|
1781
|
+
|
|
1782
|
+
interface Column {
|
|
1783
|
+
label: string;
|
|
1784
|
+
name: keyof Row;
|
|
1785
|
+
width?: number;
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
interface Row {
|
|
1789
|
+
name: string;
|
|
1790
|
+
age: number;
|
|
1791
|
+
email: string;
|
|
1792
|
+
state: string;
|
|
1793
|
+
country: string;
|
|
1794
|
+
favoriteColor: string;
|
|
1795
|
+
favoriteDay: string;
|
|
1796
|
+
occupation: string;
|
|
1797
|
+
industry: string;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
const initialData: Row[] = [
|
|
1801
|
+
{
|
|
1802
|
+
name: 'Amelia Trantow',
|
|
1803
|
+
age: 24,
|
|
1804
|
+
email: 'Dexter.Trantow57@hotmail.com',
|
|
1805
|
+
state: 'NY',
|
|
1806
|
+
country: 'US',
|
|
1807
|
+
favoriteColor: 'White',
|
|
1808
|
+
favoriteDay: 'Friday',
|
|
1809
|
+
occupation: 'Engineer',
|
|
1810
|
+
industry: 'Software',
|
|
1811
|
+
},
|
|
1812
|
+
{
|
|
1813
|
+
name: 'Estevan',
|
|
1814
|
+
age: 56,
|
|
1815
|
+
email: 'Aimee7@hotmail.com',
|
|
1816
|
+
state: 'IL',
|
|
1817
|
+
country: 'US',
|
|
1818
|
+
favoriteColor: 'Green',
|
|
1819
|
+
favoriteDay: 'Saturday',
|
|
1820
|
+
occupation: 'Engineer',
|
|
1821
|
+
industry: 'Software',
|
|
1822
|
+
},
|
|
1823
|
+
{
|
|
1824
|
+
name: 'Florence',
|
|
1825
|
+
age: 71,
|
|
1826
|
+
email: 'Jarrod.Bernier13@yahoo.com',
|
|
1827
|
+
state: 'CA',
|
|
1828
|
+
country: 'US',
|
|
1829
|
+
favoriteColor: 'Red',
|
|
1830
|
+
favoriteDay: 'Tuesday',
|
|
1831
|
+
occupation: 'Engineer',
|
|
1832
|
+
industry: 'Software',
|
|
1833
|
+
},
|
|
1834
|
+
{
|
|
1835
|
+
name: 'Teresa',
|
|
1836
|
+
age: 38,
|
|
1837
|
+
email: 'Yadira1@hotmail.com',
|
|
1838
|
+
state: 'NJ',
|
|
1839
|
+
country: 'US',
|
|
1840
|
+
favoriteColor: 'Blue',
|
|
1841
|
+
favoriteDay: 'Wednesday',
|
|
1842
|
+
occupation: 'Engineer',
|
|
1843
|
+
industry: 'Software',
|
|
1844
|
+
},
|
|
1845
|
+
];
|
|
1846
|
+
|
|
1847
|
+
const initialColumns: Column[] = [
|
|
1848
|
+
{ name: 'name', label: 'Name' },
|
|
1849
|
+
{ name: 'age', label: 'Age' },
|
|
1850
|
+
{ name: 'email', label: 'Email' },
|
|
1851
|
+
{ name: 'state', label: 'State' },
|
|
1852
|
+
{ name: 'country', label: 'Country' },
|
|
1853
|
+
{ name: 'favoriteColor', label: 'Favorite Color' },
|
|
1854
|
+
{ name: 'favoriteDay', label: 'Favorite Day' },
|
|
1855
|
+
{ name: 'occupation', label: 'Occupation' },
|
|
1856
|
+
{ name: 'industry', label: 'Industry' },
|
|
1857
|
+
];
|
|
1858
|
+
|
|
1859
|
+
|
|
1860
|
+
function HorizontalOverflowScroll() {
|
|
1861
|
+
const [data] = useState<Row[]>(initialData);
|
|
1862
|
+
|
|
1863
|
+
return (
|
|
1864
|
+
<Table outerStyle={{ width: 800 }} horizontalOverflow="scroll">
|
|
1865
|
+
<Table.Head>
|
|
1866
|
+
{initialColumns.map((c) => (
|
|
1867
|
+
<Table.HeadCell key={c.name} width={c.width}>
|
|
1868
|
+
{c.label}
|
|
1869
|
+
</Table.HeadCell>
|
|
1870
|
+
))}
|
|
1871
|
+
</Table.Head>
|
|
1872
|
+
<Table.Body>
|
|
1873
|
+
{data.map((row) => (
|
|
1874
|
+
<Table.Row data={row} key={row.email} onClick={() => {}}>
|
|
1875
|
+
{initialColumns.map((c) => (
|
|
1876
|
+
<Table.Cell key={c.name}>{row[c.name]}</Table.Cell>
|
|
1877
|
+
))}
|
|
1878
|
+
</Table.Row>
|
|
1879
|
+
))}
|
|
1880
|
+
</Table.Body>
|
|
1881
|
+
</Table>
|
|
1882
|
+
);
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
export default HorizontalOverflowScroll;
|
|
1886
|
+
```
|
|
1887
|
+
|
|
1888
|
+
|
|
1889
|
+
|
|
1890
|
+
### initialHeaders
|
|
1891
|
+
|
|
1892
|
+
```typescript
|
|
1893
|
+
import React, { useState, useCallback } from 'react';
|
|
1894
|
+
|
|
1895
|
+
import { cloneDeep } from 'lodash';
|
|
1896
|
+
import styled from 'styled-components';
|
|
1897
|
+
|
|
1898
|
+
import Gear from '@splunk/react-icons/enterprise/Gear';
|
|
1899
|
+
import Pencil from '@splunk/react-icons/enterprise/Pencil';
|
|
1900
|
+
import FilterArrowDown from '@splunk/react-icons/FilterArrowDown';
|
|
1901
|
+
import FilterArrowUp from '@splunk/react-icons/FilterArrowUp';
|
|
1902
|
+
import FilterArrowUpDown from '@splunk/react-icons/FilterArrowUpDown';
|
|
1903
|
+
import Button from '@splunk/react-ui/Button';
|
|
1904
|
+
import DL, { Term as DT, Description as DD } from '@splunk/react-ui/DefinitionList';
|
|
1905
|
+
import Dropdown from '@splunk/react-ui/Dropdown';
|
|
1906
|
+
import Heading from '@splunk/react-ui/Heading';
|
|
1907
|
+
import Menu from '@splunk/react-ui/Menu';
|
|
1908
|
+
import Table, {
|
|
1909
|
+
HeadCellSortHandler,
|
|
1910
|
+
RowActionPrimaryClickHandler,
|
|
1911
|
+
RowActionSecondaryClickHandler,
|
|
1912
|
+
RowRequestToggleHandler,
|
|
1913
|
+
TableRequestMoveColumnHandler,
|
|
1914
|
+
TableRequestResizeColumnHandler,
|
|
1915
|
+
RowClickHandler,
|
|
1916
|
+
} from '@splunk/react-ui/Table';
|
|
1917
|
+
import Text from '@splunk/react-ui/Text';
|
|
1918
|
+
import Tooltip from '@splunk/react-ui/Tooltip';
|
|
1919
|
+
import Typography from '@splunk/react-ui/Typography';
|
|
1920
|
+
import { variables } from '@splunk/themes';
|
|
1921
|
+
import { _ } from '@splunk/ui-utils/i18n';
|
|
1922
|
+
|
|
1923
|
+
interface Row {
|
|
1924
|
+
age: number;
|
|
1925
|
+
birthState: string;
|
|
1926
|
+
disabled: boolean;
|
|
1927
|
+
email: string;
|
|
1928
|
+
name: string;
|
|
1929
|
+
selected: boolean;
|
|
1930
|
+
status: string;
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
interface Header {
|
|
1934
|
+
align: 'left' | 'center' | 'right';
|
|
1935
|
+
key: keyof Row;
|
|
1936
|
+
label: string;
|
|
1937
|
+
minWidth: number;
|
|
1938
|
+
visible: boolean;
|
|
1939
|
+
width: number;
|
|
1940
|
+
tooltip?: string;
|
|
1941
|
+
filterAndSort?: boolean;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
const initialHeaders: Header[] = [
|
|
1945
|
+
{
|
|
1946
|
+
label: 'Name',
|
|
1947
|
+
key: 'name',
|
|
1948
|
+
align: 'left',
|
|
1949
|
+
width: 180,
|
|
1950
|
+
minWidth: 80,
|
|
1951
|
+
visible: true,
|
|
1952
|
+
tooltip: 'Legal first name',
|
|
1953
|
+
},
|
|
1954
|
+
{ label: 'Status', key: 'status', align: 'left', width: 100, minWidth: 40, visible: true },
|
|
1955
|
+
{
|
|
1956
|
+
label: 'Birth State',
|
|
1957
|
+
key: 'birthState',
|
|
1958
|
+
align: 'left',
|
|
1959
|
+
width: 120,
|
|
1960
|
+
minWidth: 40,
|
|
1961
|
+
visible: true,
|
|
1962
|
+
},
|
|
1963
|
+
{ label: 'Age', key: 'age', align: 'left', width: 100, minWidth: 40, visible: true },
|
|
1964
|
+
{
|
|
1965
|
+
label: 'Email Address',
|
|
1966
|
+
key: 'email',
|
|
1967
|
+
align: 'left',
|
|
1968
|
+
width: 400,
|
|
1969
|
+
minWidth: 120,
|
|
1970
|
+
visible: true,
|
|
1971
|
+
filterAndSort: true,
|
|
1972
|
+
},
|
|
1973
|
+
];
|
|
1974
|
+
|
|
1975
|
+
const initialData: Row[] = [
|
|
1976
|
+
{
|
|
1977
|
+
name: 'Rylan',
|
|
1978
|
+
status: 'single',
|
|
1979
|
+
birthState: 'HI',
|
|
1980
|
+
age: 12,
|
|
1981
|
+
email: 'Angelita_Weimann42@gmail.com',
|
|
1982
|
+
selected: false,
|
|
1983
|
+
disabled: true,
|
|
1984
|
+
},
|
|
1985
|
+
{
|
|
1986
|
+
name: 'Amelia',
|
|
1987
|
+
status: 'married',
|
|
1988
|
+
birthState: 'UT',
|
|
1989
|
+
age: 23,
|
|
1990
|
+
email: 'Dexter.Trantow57@hotmail.com',
|
|
1991
|
+
selected: false,
|
|
1992
|
+
disabled: false,
|
|
1993
|
+
},
|
|
1994
|
+
{
|
|
1995
|
+
name: 'Estevan',
|
|
1996
|
+
status: 'single',
|
|
1997
|
+
birthState: 'NY',
|
|
1998
|
+
age: 19,
|
|
1999
|
+
email: 'Aimee7@hotmail.com',
|
|
2000
|
+
selected: false,
|
|
2001
|
+
disabled: false,
|
|
2002
|
+
},
|
|
2003
|
+
{
|
|
2004
|
+
name: 'Florence',
|
|
2005
|
+
status: 'single',
|
|
2006
|
+
birthState: 'AZ',
|
|
2007
|
+
age: 20,
|
|
2008
|
+
email: 'Jarrod.Bernier13@yahoo.com',
|
|
2009
|
+
selected: false,
|
|
2010
|
+
disabled: false,
|
|
2011
|
+
},
|
|
2012
|
+
{
|
|
2013
|
+
name: 'Tressa',
|
|
2014
|
+
status: 'married',
|
|
2015
|
+
birthState: 'CA',
|
|
2016
|
+
age: 22,
|
|
2017
|
+
email: 'yadira1@hotmail.com',
|
|
2018
|
+
selected: false,
|
|
2019
|
+
disabled: false,
|
|
2020
|
+
},
|
|
2021
|
+
{
|
|
2022
|
+
name: 'Bernice',
|
|
2023
|
+
status: 'single',
|
|
2024
|
+
birthState: 'TX',
|
|
2025
|
+
age: 17,
|
|
2026
|
+
email: 'bernice.Gilbert@gmail.com',
|
|
2027
|
+
selected: false,
|
|
2028
|
+
disabled: false,
|
|
2029
|
+
},
|
|
2030
|
+
{
|
|
2031
|
+
name: 'Adrian',
|
|
2032
|
+
status: 'married',
|
|
2033
|
+
birthState: 'MA',
|
|
2034
|
+
age: 23,
|
|
2035
|
+
email: 'adrian7456@gmail.com',
|
|
2036
|
+
selected: false,
|
|
2037
|
+
disabled: false,
|
|
2038
|
+
},
|
|
2039
|
+
{
|
|
2040
|
+
name: 'Ester',
|
|
2041
|
+
status: 'single',
|
|
2042
|
+
birthState: 'NY',
|
|
2043
|
+
age: 88,
|
|
2044
|
+
email: 'esternyc@gmail.com',
|
|
2045
|
+
selected: false,
|
|
2046
|
+
disabled: false,
|
|
2047
|
+
},
|
|
2048
|
+
{
|
|
2049
|
+
name: 'Andrew',
|
|
2050
|
+
status: 'single',
|
|
2051
|
+
birthState: 'NM',
|
|
2052
|
+
age: 16,
|
|
2053
|
+
email: 'andrew.fillmore2@gmail.com',
|
|
2054
|
+
selected: false,
|
|
2055
|
+
disabled: false,
|
|
2056
|
+
},
|
|
2057
|
+
{
|
|
2058
|
+
name: 'Felix',
|
|
2059
|
+
status: 'married',
|
|
2060
|
+
birthState: 'CA',
|
|
2061
|
+
age: 36,
|
|
2062
|
+
email: 'felixfelix@hotmail.com',
|
|
2063
|
+
selected: false,
|
|
2064
|
+
disabled: false,
|
|
2065
|
+
},
|
|
2066
|
+
];
|
|
2067
|
+
|
|
2068
|
+
const StyledFilterContainer = styled.div`
|
|
2069
|
+
display: grid;
|
|
2070
|
+
gap: ${variables.spacingSmall};
|
|
2071
|
+
padding: ${variables.spacingSmall} ${variables.spacingLarge};
|
|
2072
|
+
`;
|
|
2073
|
+
|
|
2074
|
+
|
|
2075
|
+
function Complex() {
|
|
2076
|
+
const [headers, setHeaders] = useState<Header[]>(initialHeaders);
|
|
2077
|
+
const [data, setData] = useState<Row[]>(initialData);
|
|
2078
|
+
const [sortKey, setSortKey] = useState<keyof Row>('name');
|
|
2079
|
+
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');
|
|
2080
|
+
const [columnFilter, setColumnFilter] = useState<{ [key: string]: string }>({});
|
|
2081
|
+
const [activeRow, setActiveRow] = useState<string | undefined>(undefined);
|
|
2082
|
+
const [activeRowData, setActiveRowData] = useState<string | undefined>(undefined);
|
|
2083
|
+
|
|
2084
|
+
const [primaryAction, setPrimaryAction] = useState<string | undefined>(undefined);
|
|
2085
|
+
const [primaryActionRowData, setPrimaryActionRowData] = useState<string | undefined>(undefined);
|
|
2086
|
+
const [secondaryAction, setSecondaryAction] = useState<string | undefined>(undefined);
|
|
2087
|
+
const [secondaryActionRowData, setSecondaryActionRowData] = useState<string | undefined>(
|
|
2088
|
+
undefined
|
|
2089
|
+
);
|
|
2090
|
+
|
|
2091
|
+
const handleRequestMoveColumn: TableRequestMoveColumnHandler = useCallback(
|
|
2092
|
+
({ fromIndex, toIndex }) => {
|
|
2093
|
+
setHeaders((prevHeaders) => {
|
|
2094
|
+
const updatedHeaders = cloneDeep(prevHeaders);
|
|
2095
|
+
const headerToMove = updatedHeaders[fromIndex];
|
|
2096
|
+
|
|
2097
|
+
const insertionIndex = toIndex < fromIndex ? toIndex : toIndex + 1;
|
|
2098
|
+
updatedHeaders.splice(insertionIndex, 0, headerToMove);
|
|
2099
|
+
|
|
2100
|
+
const removalIndex = toIndex < fromIndex ? fromIndex + 1 : fromIndex;
|
|
2101
|
+
updatedHeaders.splice(removalIndex, 1);
|
|
2102
|
+
return updatedHeaders;
|
|
2103
|
+
});
|
|
2104
|
+
},
|
|
2105
|
+
[]
|
|
2106
|
+
);
|
|
2107
|
+
|
|
2108
|
+
const handleSort: HeadCellSortHandler = useCallback(
|
|
2109
|
+
(e, { sortKey: newSortKey }) => {
|
|
2110
|
+
setSortKey((prevSortKey) => {
|
|
2111
|
+
const prevSortDir = prevSortKey === newSortKey ? sortDir : 'none';
|
|
2112
|
+
const nextSortDir = prevSortDir === 'asc' ? 'desc' : 'asc';
|
|
2113
|
+
setSortDir(nextSortDir);
|
|
2114
|
+
return newSortKey as keyof Row;
|
|
2115
|
+
});
|
|
2116
|
+
},
|
|
2117
|
+
[sortDir]
|
|
2118
|
+
);
|
|
2119
|
+
|
|
2120
|
+
const handleResizeColumn: TableRequestResizeColumnHandler = useCallback(
|
|
2121
|
+
(event, { columnId, index, width }) => {
|
|
2122
|
+
setHeaders((prevHeaders) => {
|
|
2123
|
+
const updatedHeaders = cloneDeep(prevHeaders);
|
|
2124
|
+
|
|
2125
|
+
// min and max widths can be controlled in the callback.
|
|
2126
|
+
const selectedColumn = updatedHeaders.find(({ key }) => key === columnId);
|
|
2127
|
+
if (selectedColumn) {
|
|
2128
|
+
const widthAboveMinimum = Math.max(width, selectedColumn.minWidth);
|
|
2129
|
+
updatedHeaders[index].width = widthAboveMinimum;
|
|
2130
|
+
|
|
2131
|
+
return updatedHeaders;
|
|
2132
|
+
}
|
|
2133
|
+
return [];
|
|
2134
|
+
});
|
|
2135
|
+
},
|
|
2136
|
+
[setHeaders]
|
|
2137
|
+
);
|
|
2138
|
+
|
|
2139
|
+
const handleToggle: RowRequestToggleHandler = useCallback((event, { email }) => {
|
|
2140
|
+
setData((prevData) => {
|
|
2141
|
+
const updatedData = cloneDeep(prevData);
|
|
2142
|
+
|
|
2143
|
+
const selectedRow = updatedData.find(({ email: rowEmail }) => rowEmail === email);
|
|
2144
|
+
if (selectedRow) {
|
|
2145
|
+
selectedRow.selected = !selectedRow.selected;
|
|
2146
|
+
|
|
2147
|
+
return updatedData;
|
|
2148
|
+
}
|
|
2149
|
+
return [];
|
|
2150
|
+
});
|
|
2151
|
+
}, []);
|
|
2152
|
+
|
|
2153
|
+
const getRowSelectionState = useCallback((rowData: Row[]): 'none' | 'all' | 'some' => {
|
|
2154
|
+
const selectedCount = rowData.filter((row) => row.selected).length;
|
|
2155
|
+
const disabledCount = rowData.filter((row) => row.disabled).length;
|
|
2156
|
+
|
|
2157
|
+
if (selectedCount === 0) return 'none';
|
|
2158
|
+
if (selectedCount + disabledCount === rowData.length) return 'all';
|
|
2159
|
+
return 'some';
|
|
2160
|
+
}, []);
|
|
2161
|
+
|
|
2162
|
+
const handleToggleAll = useCallback(() => {
|
|
2163
|
+
setData((prevData) => {
|
|
2164
|
+
const updatedData = cloneDeep(prevData);
|
|
2165
|
+
const selected = getRowSelectionState(updatedData) !== 'all';
|
|
2166
|
+
const finalData = updatedData.map((row) => ({
|
|
2167
|
+
...row,
|
|
2168
|
+
selected: row.disabled ? false : selected,
|
|
2169
|
+
}));
|
|
2170
|
+
|
|
2171
|
+
return finalData;
|
|
2172
|
+
});
|
|
2173
|
+
}, [getRowSelectionState]);
|
|
2174
|
+
|
|
2175
|
+
const handleRowClick: RowClickHandler = useCallback((event, rowData) => {
|
|
2176
|
+
setActiveRow(rowData.name);
|
|
2177
|
+
setActiveRowData(JSON.stringify(rowData));
|
|
2178
|
+
}, []);
|
|
2179
|
+
|
|
2180
|
+
const handleShowHide = useCallback((e: React.MouseEvent, { label }: { label: string }) => {
|
|
2181
|
+
setHeaders((prevHeaders) =>
|
|
2182
|
+
prevHeaders.map((header) =>
|
|
2183
|
+
header.label === label ? { ...header, visible: !header.visible } : header
|
|
2184
|
+
)
|
|
2185
|
+
);
|
|
2186
|
+
}, []);
|
|
2187
|
+
|
|
2188
|
+
const handleEditActionClick: RowActionPrimaryClickHandler = useCallback((e, rowData) => {
|
|
2189
|
+
setPrimaryAction('Edit');
|
|
2190
|
+
setPrimaryActionRowData(JSON.stringify(rowData));
|
|
2191
|
+
}, []);
|
|
2192
|
+
|
|
2193
|
+
const handleSaveActionClick: RowActionSecondaryClickHandler = useCallback((e, rowData) => {
|
|
2194
|
+
setSecondaryAction('Save');
|
|
2195
|
+
setSecondaryActionRowData(JSON.stringify(rowData));
|
|
2196
|
+
}, []);
|
|
2197
|
+
|
|
2198
|
+
const handleAddActionClick: RowActionSecondaryClickHandler = useCallback((e, rowData) => {
|
|
2199
|
+
setSecondaryAction('Add');
|
|
2200
|
+
setSecondaryActionRowData(JSON.stringify(rowData));
|
|
2201
|
+
}, []);
|
|
2202
|
+
|
|
2203
|
+
const handleDeleteActionClick: RowActionSecondaryClickHandler = useCallback((e, rowData) => {
|
|
2204
|
+
setSecondaryAction('Delete');
|
|
2205
|
+
setSecondaryActionRowData(JSON.stringify(rowData));
|
|
2206
|
+
}, []);
|
|
2207
|
+
|
|
2208
|
+
const getFilterIcon = (header: Header) => {
|
|
2209
|
+
const hasFilter = columnFilter[header.key]?.length > 0;
|
|
2210
|
+
const iconProps = { variant: hasFilter ? 'filled' : 'outlined' } as const;
|
|
2211
|
+
|
|
2212
|
+
if (sortKey === header.key && sortDir === 'asc') {
|
|
2213
|
+
return <FilterArrowUp {...iconProps} />;
|
|
2214
|
+
}
|
|
2215
|
+
if (sortKey === header.key && sortDir === 'desc') {
|
|
2216
|
+
return <FilterArrowDown {...iconProps} />;
|
|
2217
|
+
}
|
|
2218
|
+
return <FilterArrowUpDown {...iconProps} />;
|
|
2219
|
+
};
|
|
2220
|
+
|
|
2221
|
+
const toggle = (
|
|
2222
|
+
<Button appearance="subtle" data-test="actions-toggle" icon={<Gear hideDefaultTooltip />} />
|
|
2223
|
+
);
|
|
2224
|
+
|
|
2225
|
+
const actions = [
|
|
2226
|
+
<Dropdown toggle={toggle} key="settings">
|
|
2227
|
+
<Menu>
|
|
2228
|
+
<Menu.Heading>Show/Hide Columns</Menu.Heading>
|
|
2229
|
+
{headers.map((header) => (
|
|
2230
|
+
<Menu.Item
|
|
2231
|
+
key={header.label}
|
|
2232
|
+
selectable
|
|
2233
|
+
selected={header.visible}
|
|
2234
|
+
onClick={(e: React.MouseEvent) =>
|
|
2235
|
+
handleShowHide(e, {
|
|
2236
|
+
label: header.label,
|
|
2237
|
+
})
|
|
2238
|
+
}
|
|
2239
|
+
>
|
|
2240
|
+
{header.label}
|
|
2241
|
+
</Menu.Item>
|
|
2242
|
+
))}
|
|
2243
|
+
<Menu.Divider />
|
|
2244
|
+
<Menu.Heading>More actions</Menu.Heading>
|
|
2245
|
+
<Menu.Item>Add new item</Menu.Item>
|
|
2246
|
+
</Menu>
|
|
2247
|
+
</Dropdown>,
|
|
2248
|
+
];
|
|
2249
|
+
|
|
2250
|
+
const rowActionPrimaryButton = (
|
|
2251
|
+
<Tooltip
|
|
2252
|
+
content={_('Edit')}
|
|
2253
|
+
contentRelationship="label"
|
|
2254
|
+
onClick={handleEditActionClick}
|
|
2255
|
+
style={{ marginRight: 8 }}
|
|
2256
|
+
>
|
|
2257
|
+
<Button
|
|
2258
|
+
appearance="subtle"
|
|
2259
|
+
icon={<Pencil hideDefaultTooltip screenReaderText={null} />}
|
|
2260
|
+
/>
|
|
2261
|
+
</Tooltip>
|
|
2262
|
+
);
|
|
2263
|
+
|
|
2264
|
+
const rowActionsSecondaryMenu = (
|
|
2265
|
+
<Menu>
|
|
2266
|
+
<Menu.Item onClick={handleSaveActionClick}>Save</Menu.Item>
|
|
2267
|
+
<Menu.Item onClick={handleAddActionClick}>Add</Menu.Item>
|
|
2268
|
+
<Menu.Item onClick={handleDeleteActionClick}>Delete</Menu.Item>
|
|
2269
|
+
</Menu>
|
|
2270
|
+
);
|
|
2271
|
+
|
|
2272
|
+
return (
|
|
2273
|
+
<div>
|
|
2274
|
+
<Table
|
|
2275
|
+
onRequestMoveColumn={handleRequestMoveColumn}
|
|
2276
|
+
onRequestResizeColumn={handleResizeColumn}
|
|
2277
|
+
onRequestToggleAllRows={handleToggleAll}
|
|
2278
|
+
rowSelection={getRowSelectionState(data)}
|
|
2279
|
+
headType="fixed"
|
|
2280
|
+
innerStyle={{ maxHeight: 160 }}
|
|
2281
|
+
actions={actions}
|
|
2282
|
+
actionsColumnWidth={104}
|
|
2283
|
+
>
|
|
2284
|
+
<Table.Head>
|
|
2285
|
+
{headers.map((header) =>
|
|
2286
|
+
header.filterAndSort ? (
|
|
2287
|
+
<Table.HeadDropdownCell
|
|
2288
|
+
key={header.key}
|
|
2289
|
+
columnId={header.key}
|
|
2290
|
+
align={header.align}
|
|
2291
|
+
width={header.width}
|
|
2292
|
+
label={
|
|
2293
|
+
<>
|
|
2294
|
+
{getFilterIcon(header)} {header.label}
|
|
2295
|
+
</>
|
|
2296
|
+
}
|
|
2297
|
+
>
|
|
2298
|
+
<StyledFilterContainer>
|
|
2299
|
+
<Heading level={4}>Filter</Heading>
|
|
2300
|
+
<Text
|
|
2301
|
+
onChange={(e, { value }) =>
|
|
2302
|
+
setColumnFilter((prev) => ({
|
|
2303
|
+
...prev,
|
|
2304
|
+
[header.key]: value,
|
|
2305
|
+
}))
|
|
2306
|
+
}
|
|
2307
|
+
value={columnFilter[header.key] || ''}
|
|
2308
|
+
/>
|
|
2309
|
+
</StyledFilterContainer>
|
|
2310
|
+
|
|
2311
|
+
<Menu>
|
|
2312
|
+
<Menu.Heading title>Sort</Menu.Heading>
|
|
2313
|
+
<Menu.Divider />
|
|
2314
|
+
<Menu.Item
|
|
2315
|
+
selectable
|
|
2316
|
+
selected={sortKey === header.key && sortDir === 'asc'}
|
|
2317
|
+
onClick={() => {
|
|
2318
|
+
setSortKey(header.key);
|
|
2319
|
+
setSortDir('asc');
|
|
2320
|
+
}}
|
|
2321
|
+
>
|
|
2322
|
+
Ascending
|
|
2323
|
+
</Menu.Item>
|
|
2324
|
+
<Menu.Item
|
|
2325
|
+
selectable
|
|
2326
|
+
selected={sortKey === header.key && sortDir === 'desc'}
|
|
2327
|
+
onClick={() => {
|
|
2328
|
+
setSortKey(header.key);
|
|
2329
|
+
setSortDir('desc');
|
|
2330
|
+
}}
|
|
2331
|
+
>
|
|
2332
|
+
Descending
|
|
2333
|
+
</Menu.Item>
|
|
2334
|
+
</Menu>
|
|
2335
|
+
</Table.HeadDropdownCell>
|
|
2336
|
+
) : (
|
|
2337
|
+
<Table.HeadCell
|
|
2338
|
+
key={header.key}
|
|
2339
|
+
columnId={header.key}
|
|
2340
|
+
align={header.align}
|
|
2341
|
+
width={header.width}
|
|
2342
|
+
onSort={handleSort}
|
|
2343
|
+
sortKey={header.key}
|
|
2344
|
+
sortDir={header.key === sortKey ? sortDir : 'none'}
|
|
2345
|
+
tooltip={header.tooltip}
|
|
2346
|
+
>
|
|
2347
|
+
{header.label}
|
|
2348
|
+
</Table.HeadCell>
|
|
2349
|
+
)
|
|
2350
|
+
)}
|
|
2351
|
+
</Table.Head>
|
|
2352
|
+
<Table.Body>
|
|
2353
|
+
{data
|
|
2354
|
+
.filter((row) => {
|
|
2355
|
+
return Object.entries(columnFilter).every(([key, filterValue]) => {
|
|
2356
|
+
if (!filterValue) return true;
|
|
2357
|
+
const cellValue = String(row[key as keyof Row]).toLowerCase();
|
|
2358
|
+
return cellValue.startsWith(filterValue.toLowerCase());
|
|
2359
|
+
});
|
|
2360
|
+
})
|
|
2361
|
+
.sort((rowA, rowB) => {
|
|
2362
|
+
if (sortDir === 'asc') {
|
|
2363
|
+
return rowA[sortKey] > rowB[sortKey] ? 1 : -1;
|
|
2364
|
+
}
|
|
2365
|
+
if (sortDir === 'desc') {
|
|
2366
|
+
return rowB[sortKey] > rowA[sortKey] ? 1 : -1;
|
|
2367
|
+
}
|
|
2368
|
+
return 0;
|
|
2369
|
+
})
|
|
2370
|
+
.map((row) => (
|
|
2371
|
+
<Table.Row
|
|
2372
|
+
key={row.email}
|
|
2373
|
+
actionPrimary={row.disabled ? undefined : rowActionPrimaryButton}
|
|
2374
|
+
actionsSecondary={
|
|
2375
|
+
row.disabled ? undefined : rowActionsSecondaryMenu
|
|
2376
|
+
}
|
|
2377
|
+
onRequestToggle={handleToggle}
|
|
2378
|
+
onClick={row.disabled ? undefined : handleRowClick}
|
|
2379
|
+
data={row}
|
|
2380
|
+
selected={row.selected}
|
|
2381
|
+
disabled={row.disabled}
|
|
2382
|
+
>
|
|
2383
|
+
{headers.map((header) => (
|
|
2384
|
+
<Table.Cell
|
|
2385
|
+
key={`${row.email}-${header.key}`}
|
|
2386
|
+
align={header.align}
|
|
2387
|
+
>
|
|
2388
|
+
{row[header.key]}
|
|
2389
|
+
</Table.Cell>
|
|
2390
|
+
))}
|
|
2391
|
+
</Table.Row>
|
|
2392
|
+
))}
|
|
2393
|
+
</Table.Body>
|
|
2394
|
+
</Table>
|
|
2395
|
+
<aside style={{ marginTop: 20 }} aria-live="polite" aria-relevant="text">
|
|
2396
|
+
<Typography as="p">
|
|
2397
|
+
Click a primary action, secondary action, or row to see the returned data
|
|
2398
|
+
</Typography>
|
|
2399
|
+
{activeRowData && (
|
|
2400
|
+
<div style={{ overflow: 'scroll' }}>
|
|
2401
|
+
<DL>
|
|
2402
|
+
<DT>Row:</DT>
|
|
2403
|
+
<DD>'{activeRow}'</DD>
|
|
2404
|
+
<DT>Data:</DT>
|
|
2405
|
+
<DD>
|
|
2406
|
+
<code>{activeRowData}</code>
|
|
2407
|
+
</DD>
|
|
2408
|
+
</DL>
|
|
2409
|
+
</div>
|
|
2410
|
+
)}
|
|
2411
|
+
{primaryActionRowData && (
|
|
2412
|
+
<div style={{ overflow: 'scroll', marginBottom: 10 }}>
|
|
2413
|
+
<DL>
|
|
2414
|
+
<DT>Primary action:</DT>
|
|
2415
|
+
<DD>'{primaryAction}'</DD>
|
|
2416
|
+
<DT>Data:</DT>
|
|
2417
|
+
<DD>
|
|
2418
|
+
<code>{primaryActionRowData}</code>
|
|
2419
|
+
</DD>
|
|
2420
|
+
</DL>
|
|
2421
|
+
</div>
|
|
2422
|
+
)}
|
|
2423
|
+
{secondaryActionRowData && (
|
|
2424
|
+
<div style={{ overflow: 'scroll', marginBottom: 10 }}>
|
|
2425
|
+
<DL>
|
|
2426
|
+
<DT>Secondary action:</DT>
|
|
2427
|
+
<DD>'{secondaryAction}'</DD>
|
|
2428
|
+
<DT>Data:</DT>
|
|
2429
|
+
<DD>
|
|
2430
|
+
<code>{secondaryActionRowData}</code>
|
|
2431
|
+
</DD>
|
|
2432
|
+
</DL>
|
|
2433
|
+
</div>
|
|
2434
|
+
)}
|
|
2435
|
+
</aside>
|
|
2436
|
+
</div>
|
|
2437
|
+
);
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
export default Complex;
|
|
2441
|
+
```
|
|
2442
|
+
|
|
2443
|
+
|
|
2444
|
+
|
|
2445
|
+
|
|
2446
|
+
## API
|
|
2447
|
+
|
|
2448
|
+
|
|
2449
|
+
### Table API
|
|
2450
|
+
|
|
2451
|
+
#### Props
|
|
2452
|
+
|
|
2453
|
+
| Name | Type | Required | Default | Description |
|
|
2454
|
+
|------|------|------|------|------|
|
|
2455
|
+
| actions | React.ReactElement[] | no | | Adds table-level actions. Not compatible with `onRequestResize`. |
|
|
2456
|
+
| actionsColumnWidth | number | no | | Specifies the width of the actions column. Adds an empty header for row actions if no table-level actions are present. |
|
|
2457
|
+
| children | React.ReactNode | no | | Must be `Table.Head`, `Table.Body`, or `Table.Caption`. |
|
|
2458
|
+
| dockOffset | number | no | | Sets the offset from the top of the window. Only applies when `headType` is 'docked'. |
|
|
2459
|
+
| dockScrollBar | boolean | no | | Docks the horizontal scroll bar at the bottom of the window when the bottom of the table is below the viewport. |
|
|
2460
|
+
| elementRef | React.Ref<HTMLDivElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
|
|
2461
|
+
| headType | 'docked' \| 'fixed' \| 'inline' | no | | Sets the table head type: * `docked`: The head is docked against the window * `fixed` : The head is fixed in the table. The table can scroll independently from the head. * `inline`: The head isn't fixed, but can scroll with the rest of the table. |
|
|
2462
|
+
| horizontalOverflow | 'auto' \| 'scroll' | no | | Controls how the Table handles horizontal content overflow: * `auto`: The default behavior for overflow. `HeadCell` content will truncate and `Cell` content will wrap. The Table will scroll horizontally when the container's width exceeds the Table's width. * `scroll`: The Table will scroll horizontally. `HeadCell` content will not truncate and `Cell` content will wrap only for word breaks. |
|
|
2463
|
+
| innerStyle | React.CSSProperties | no | | Style specification for the inner container, which is the scrolling container. |
|
|
2464
|
+
| onRequestMoveColumn | TableRequestMoveColumnHandler | no | | An event handler for handle the re-order action of Table. The function is passed an options object with `fromIndex` and `toIndex`. |
|
|
2465
|
+
| onRequestMoveRow | TableRequestMoveRowHandler | no | | An event handler to handle the reorder rows action of Table. The function is passed an options object with `fromIndex` and `toIndex`. |
|
|
2466
|
+
| onRequestResizeColumn | TableRequestResizeColumnHandler | no | | An event handler for resize of columns for the current column being resized. The function is passed an event and a data object with `columnId`, `id`, `index`, and `width`. Every Table.HeadCell must have a width prop when using onRequestResizeColumn. Table with resizableFillLayout supports width of "auto". |
|
|
2467
|
+
| onRequestToggleAllRows | () => void | no | | Callback invoked when a user clicks the row selection toggle in the header. |
|
|
2468
|
+
| onScroll | React.UIEventHandler<HTMLDivElement> | no | | Callback invoked when a scroll event occurs on the inner scrolling container. |
|
|
2469
|
+
| outerStyle | React.CSSProperties | no | | Style specification for the outer container. |
|
|
2470
|
+
| pinnedColumns | PinnedColumnsProp | no | | Optionally pin the actions column to the end of the table by passing `pinnedColumns={{ actions: true }}.` When using pinned columns `horizontalOverflow` should be set to `scroll`. |
|
|
2471
|
+
| primaryColumnIndex | number | no | | Indicates the column to use as the primary label for each row. |
|
|
2472
|
+
| resizableFillLayout | boolean | no | | Table will fill parent container. Resizable columns can have a `width` of `auto` only with this prop enabled. |
|
|
2473
|
+
| rowExpansion | 'single' \| 'multi' \| 'controlled' \| 'none' | no | | Adds a column to the table with an expansion button for each row that has expansion content. Supported values: * `single`: Only one row can be expanded at a time. If another expansion button is clicked, the currently expanded row closes and the new one opens. * `multi`: Allows multiple rows to be expanded at the same time. * `controlled`: Allows the expanded state to be externally managed by `expanded` prop of `Row`. * `none`: The default with no row expansion. |
|
|
2474
|
+
| rowSelection | 'all' \| 'some' \| 'none' | no | | When an `onRequestToggleAllRows` handler is defined, this prop determines the appearance of the toggle all rows button. |
|
|
2475
|
+
| stripeRows | boolean | no | | Alternate rows are given a darker background to improve readability. |
|
|
2476
|
+
| tableStyle | React.CSSProperties | no | | The style attribute for the table. This is primarily useful for setting the CSS table-layout property. |
|
|
2477
|
+
|
|
2478
|
+
#### Types
|
|
2479
|
+
|
|
2480
|
+
| Name | Type | Description |
|
|
2481
|
+
|------|------|------|
|
|
2482
|
+
| PinnedColumnsProp | { actions?: boolean; } | |
|
|
2483
|
+
| TableRequestMoveColumnHandler | (data: { fromIndex: number; toIndex: number }) => void | |
|
|
2484
|
+
| TableRequestMoveRowHandler | (data: { fromIndex: number; toIndex: number }) => void | |
|
|
2485
|
+
| TableRequestResizeColumnHandler | ( event: React.MouseEvent<HTMLHRElement> \| React.KeyboardEvent<HTMLHRElement> \| MouseEvent, data: { columnId?: string; id?: string; index: number; width: number; } ) => void | |
|
|
2486
|
+
|
|
2487
|
+
|
|
2488
|
+
|
|
2489
|
+
### Table.Head API
|
|
2490
|
+
|
|
2491
|
+
#### Props
|
|
2492
|
+
|
|
2493
|
+
| Name | Type | Required | Default | Description |
|
|
2494
|
+
|------|------|------|------|------|
|
|
2495
|
+
| children | React.ReactNode | no | | Must be `Table.HeadCell`s or `Table.HeadDropdownCell`s. |
|
|
2496
|
+
| elementRef | React.Ref<HTMLTableSectionElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
|
|
2497
|
+
|
|
2498
|
+
|
|
2499
|
+
|
|
2500
|
+
### Table.Body API
|
|
2501
|
+
|
|
2502
|
+
#### Props
|
|
2503
|
+
|
|
2504
|
+
| Name | Type | Required | Default | Description |
|
|
2505
|
+
|------|------|------|------|------|
|
|
2506
|
+
| children | React.ReactNode | no | | Must be `Table.Row`. |
|
|
2507
|
+
| elementRef | React.Ref<HTMLTableSectionElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
|
|
2508
|
+
|
|
2509
|
+
|
|
2510
|
+
|
|
2511
|
+
### Table.Row API
|
|
2512
|
+
|
|
2513
|
+
#### Props
|
|
2514
|
+
|
|
2515
|
+
| Name | Type | Required | Default | Description |
|
|
2516
|
+
|------|------|------|------|------|
|
|
2517
|
+
| actionPrimary | React.ReactElement | no | | Adds primary actions. For best results, use an icon-only button style. The `onClick` handler of each action is passed the event and the `data` prop of this row. |
|
|
2518
|
+
| actionsSecondary | React.ReactElement | no | | Adds a secondary actions dropdown menu. This prop must be a `Menu`. The `onClick` handler of each action is passed the event and the `data` prop of this row. |
|
|
2519
|
+
| children | React.ReactNode | no | | Must be `Table.Cell`. |
|
|
2520
|
+
| data | any | no | | This data is returned with the onClick and toggle events as the second argument. |
|
|
2521
|
+
| disabled | boolean | no | | Indicates whether the row selection is disabled. |
|
|
2522
|
+
| elementRef | React.Ref<HTMLTableRowElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
|
|
2523
|
+
| expanded | boolean | no | | Allows row expansion to be controlled programmatically if the `rowExpansion` prop is set to `controlled` in `Table`. |
|
|
2524
|
+
| expansionRow | React.ReactElement \| React.ReactElement[] | no | | An optional row that is displayed when this row is expanded, or an array of rows. |
|
|
2525
|
+
| onClick | RowClickHandler | no | | Providing an `onClick` handler enables focus, hover, and related styles. |
|
|
2526
|
+
| onExpansion | ( event: React.MouseEvent<HTMLButtonElement> \| React.KeyboardEvent<HTMLButtonElement>, data?: any // eslint-disable-line @typescript-eslint/no-explicit-any ) => void | no | | An event handler that triggers when the row expansion element is selected. |
|
|
2527
|
+
| onRequestToggle | RowRequestToggleHandler | no | | An event handler for toggle of the row. resize of columns. The function is passed the event and the `data` prop for this row. |
|
|
2528
|
+
| rowScreenReaderText | string | no | | Indicates the row's label when selected or unselected. |
|
|
2529
|
+
| selected | boolean | no | | When an `onRequestToggle` handler is defined, this prop determines the appearance of the toggle. |
|
|
2530
|
+
|
|
2531
|
+
#### Types
|
|
2532
|
+
|
|
2533
|
+
| Name | Type | Description |
|
|
2534
|
+
|------|------|------|
|
|
2535
|
+
| RowActionPrimaryClickHandler | ( event: React.MouseEvent, data?: any // eslint-disable-line @typescript-eslint/no-explicit-any ) => void | |
|
|
2536
|
+
| RowActionSecondaryClickHandler | ( event: React.MouseEvent, data?: any // eslint-disable-line @typescript-eslint/no-explicit-any ) => void | |
|
|
2537
|
+
| RowClickHandler | ( event: React.MouseEvent<HTMLTableRowElement> \| React.KeyboardEvent<HTMLTableRowElement>, data?: any // eslint-disable-line @typescript-eslint/no-explicit-any ) => void | |
|
|
2538
|
+
| RowRequestExpansionHandler | ( event: React.MouseEvent<HTMLButtonElement> \| React.KeyboardEvent<HTMLButtonElement>, data?: any // eslint-disable-line @typescript-eslint/no-explicit-any ) => void | |
|
|
2539
|
+
| RowRequestToggleHandler | ( event: React.MouseEvent<HTMLButtonElement \| HTMLAnchorElement>, data?: any // eslint-disable-line @typescript-eslint/no-explicit-any ) => void | |
|
|
2540
|
+
|
|
2541
|
+
|
|
2542
|
+
|
|
2543
|
+
### Table.Cell API
|
|
2544
|
+
|
|
2545
|
+
#### Props
|
|
2546
|
+
|
|
2547
|
+
| Name | Type | Required | Default | Description |
|
|
2548
|
+
|------|------|------|------|------|
|
|
2549
|
+
| align | 'left' \| 'center' \| 'right' | no | 'left' | Align the text in the cell. |
|
|
2550
|
+
| children | React.ReactNode | no | | |
|
|
2551
|
+
| data | any | no | | This data is returned with the onClick events as the second argument. |
|
|
2552
|
+
| elementRef | React.Ref<HTMLTableCellElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
|
|
2553
|
+
| onClick | CellClickHandler | no | | Providing an `onClick` handler enables focus, hover, and related styles. |
|
|
2554
|
+
|
|
2555
|
+
#### Types
|
|
2556
|
+
|
|
2557
|
+
| Name | Type | Description |
|
|
2558
|
+
|------|------|------|
|
|
2559
|
+
| CellClickHandler | ( event: React.MouseEvent<HTMLTableCellElement> \| React.KeyboardEvent<HTMLTableCellElement>, data?: any // eslint-disable-line @typescript-eslint/no-explicit-any ) => void | |
|
|
2560
|
+
|
|
2561
|
+
|
|
2562
|
+
|
|
2563
|
+
### Table.HeadCell API
|
|
2564
|
+
|
|
2565
|
+
#### Props
|
|
2566
|
+
|
|
2567
|
+
| Name | Type | Required | Default | Description |
|
|
2568
|
+
|------|------|------|------|------|
|
|
2569
|
+
| align | 'left' \| 'center' \| 'right' | no | 'left' | Align the text in the label. |
|
|
2570
|
+
| children | React.ReactNode | no | | |
|
|
2571
|
+
| columnId | string | no | | An id that is passed to the `onSort` callback and as `Table`'s `onRequestResizeColumn` callback. |
|
|
2572
|
+
| elementRef | React.Ref<HTMLTableCellElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
|
|
2573
|
+
| headCellScreenReaderText | string | no | | A string used to generate the `aria-label` for the head cell during column reordering. When the `children` prop is not a string, providing `headCellScreenReaderText` is recommended to improve the screen reader announcements during the reordering interaction. |
|
|
2574
|
+
| onSort | HeadCellSortHandler | no | | A callback invoked when this head cell is clicked. If provided, this HeadCell is sortable and renders the appropriate user interface. |
|
|
2575
|
+
| resizable | boolean | no | true | Allows the user to resize the column when onRequestResize is passed to the `Table`. Set resizable to `false` to prevent some columns for resizing. |
|
|
2576
|
+
| sortDir | HeadCellSortDir | no | 'none' | The current sort direction of this column. |
|
|
2577
|
+
| sortKey | string | no | | The `sortKey` is passed in the data object to the `onSort` callback, if provided. |
|
|
2578
|
+
| tooltip | React.ReactNode | no | | Content to show in a tooltip. |
|
|
2579
|
+
| truncate | boolean | no | true | Truncate the text in the label. |
|
|
2580
|
+
| width | number \| 'auto' | no | | The width of the column in pixels. |
|
|
2581
|
+
|
|
2582
|
+
#### Types
|
|
2583
|
+
|
|
2584
|
+
| Name | Type | Description |
|
|
2585
|
+
|------|------|------|
|
|
2586
|
+
| HeadCellSortDir | 'asc' \| 'desc' \| 'none' | |
|
|
2587
|
+
| HeadCellSortHandler | ( event: \| React.MouseEvent<HTMLButtonElement \| HTMLDivElement> \| React.KeyboardEvent<HTMLButtonElement \| HTMLDivElement>, data: { columnId?: string; id?: string; index: number; sortDir: HeadCellSortDir; sortKey?: string; } ) => void | |
|
|
2588
|
+
|
|
2589
|
+
|
|
2590
|
+
|
|
2591
|
+
### Table.HeadDropdownCell API
|
|
2592
|
+
|
|
2593
|
+
#### Props
|
|
2594
|
+
|
|
2595
|
+
| Name | Type | Required | Default | Description |
|
|
2596
|
+
|------|------|------|------|------|
|
|
2597
|
+
| align | 'left' \| 'center' \| 'right' | no | 'left' | Align the text in the label. |
|
|
2598
|
+
| buttonRef | React.Ref<HTMLButtonElement> | no | | A React ref which is set to the button element when the component mounts and null when it unmounts. |
|
|
2599
|
+
| canCoverHead | boolean | no | true | If there is not enough room to render the `Popover` in a direction, this option enables it to be rendered over the Head. |
|
|
2600
|
+
| children | React.ReactNode | yes | | |
|
|
2601
|
+
| closeReasons | HeadDropdownCellPossibleCloseReason[] | no | [ 'clickAway', 'contentClick', 'escapeKey', 'offScreen', 'tabKey', 'toggleClick', ] | An array of reasons for which this `Popover` should close. |
|
|
2602
|
+
| columnId | string | no | | An id that is passed to the `onRequestOpen`, `onRequestClose` callback and as `Table`'s `onRequestResizeColumn` callback. |
|
|
2603
|
+
| elementRef | React.Ref<HTMLTableCellElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
|
|
2604
|
+
| focusToggleReasons | HeadDropdownCellPossibleCloseReason[] | no | [ 'contentClick', 'escapeKey', 'toggleClick', ] | An array of reasons for which to set focus on the toggle. Only subset of `closeReasons` will be honored. When Menu.Items open a Modal or other dialog, it may be necessary to remove the 'contentClick' reason to allow focus to be passed to the dialog. |
|
|
2605
|
+
| headCellScreenReaderText | string | no | | A string used to generate the `aria-label` for the head cell during column reordering. When the `label` prop is not a string, providing `headCellScreenReaderText` is recommended to improve the screen reader announcements during the reordering interaction. |
|
|
2606
|
+
| label | React.ReactNode | no | | The label on the heading, which may simply be text or may contain an element with icons or other markup. |
|
|
2607
|
+
| onRequestClose | HeadDropdownCellRequestCloseHandler | no | | A callback function invoked with a data object containing the event (if applicable) and a reason for the close request. |
|
|
2608
|
+
| onRequestOpen | HeadDropdownCellRequestOpenHandler | no | | A callback function invoked with a data object containing the event. (The reason is always toggleClick). |
|
|
2609
|
+
| open | boolean | no | | If an open prop is provided, this component will behave as a [controlled component](https://reactjs.org/docs/forms.html#controlled-components). This means that the consumer is responsible for handling the open/close state. If no open prop is provided, the component will handle the open/close state internally. |
|
|
2610
|
+
| repositionMode | 'none' \| 'flip' \| 'any' | no | 'flip' | See `repositionMode` on `Popover` for details. |
|
|
2611
|
+
| resizable | boolean | no | true | Allow the user to resize the column when onRequestResize is passed to the `Table`. Set resizable to false to prevent some columns for resizing. |
|
|
2612
|
+
| retainFocus | boolean | no | | Keep focus within the Popover while open. Note, Menu handles it's own focus by default, so this is only necessary when the popover contains other types of content. |
|
|
2613
|
+
| takeFocus | boolean | no | true | When true, the Popover will automatically take focus when 'open' changes to true. Disable this for a Popover that has shows on hover, such as a tooltip. |
|
|
2614
|
+
| truncate | boolean | no | true | Truncate the text in the label. `truncate=false` is not compatible with `Table`'s `onRequestResize`. |
|
|
2615
|
+
| width | number | no | | The width of the column in pixels. |
|
|
2616
|
+
|
|
2617
|
+
#### Types
|
|
2618
|
+
|
|
2619
|
+
| Name | Type | Description |
|
|
2620
|
+
|------|------|------|
|
|
2621
|
+
| HeadDropdownCellPossibleCloseReason | \| 'clickAway' \| 'contentClick' \| 'escapeKey' \| 'offScreen' \| 'tabKey' \| 'toggleClick' | |
|
|
2622
|
+
| HeadDropdownCellRequestCloseHandler | ( event: \| React.MouseEvent<HTMLDivElement \| HTMLButtonElement> \| React.KeyboardEvent<HTMLDivElement \| HTMLButtonElement> \| MouseEvent \| KeyboardEvent \| TouchEvent \| undefined, data: { columnId?: string; index: number; reason: HeadDropdownCellPossibleCloseReason; } ) => void | |
|
|
2623
|
+
| HeadDropdownCellRequestOpenHandler | ( event: \| React.MouseEvent<HTMLButtonElement \| HTMLDivElement> \| React.KeyboardEvent<HTMLButtonElement \| HTMLDivElement>, data: { columnId?: string; index: number; reason: 'toggleClick'; } ) => void | |
|
|
2624
|
+
|
|
2625
|
+
|
|
2626
|
+
|
|
2627
|
+
### Table.Caption API
|
|
2628
|
+
|
|
2629
|
+
Tables that use a docked header must place the caption on the bottom side.
|
|
2630
|
+
Tables that use a fixed header cannot use captions.
|
|
2631
|
+
|
|
2632
|
+
#### Props
|
|
2633
|
+
|
|
2634
|
+
| Name | Type | Required | Default | Description |
|
|
2635
|
+
|------|------|------|------|------|
|
|
2636
|
+
| children | React.ReactNode | yes | | |
|
|
2637
|
+
| side | 'top' \| 'bottom' | no | 'top' | The location of the caption relative to the table. |
|
|
2638
|
+
|
|
2639
|
+
|
|
2640
|
+
|
|
2641
|
+
|
|
2642
|
+
|