@czi-sds/mcp 0.2.1 → 0.3.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/data/zeroheight-cache.json +5195 -0
- package/data/zeroheight-exports/Accordion.md +675 -0
- package/data/zeroheight-exports/Banner.md +576 -0
- package/data/zeroheight-exports/Borders.md +118 -0
- package/data/zeroheight-exports/Buttons.md +2260 -0
- package/data/zeroheight-exports/Callout.md +756 -0
- package/data/zeroheight-exports/Colors.md +633 -0
- package/data/zeroheight-exports/Content-Card.md +162 -0
- package/data/zeroheight-exports/Contributing-to-SDS.md +99 -0
- package/data/zeroheight-exports/Control-Inputs.md +2066 -0
- package/data/zeroheight-exports/Corners.md +112 -0
- package/data/zeroheight-exports/Cover-page.md +4 -0
- package/data/zeroheight-exports/Dialog.md +1133 -0
- package/data/zeroheight-exports/Downloads.md +121 -0
- package/data/zeroheight-exports/Drop-Shadows.md +80 -0
- package/data/zeroheight-exports/Dropdown-Input.md +1149 -0
- package/data/zeroheight-exports/Dropdown-Menu.md +3787 -0
- package/data/zeroheight-exports/Element-Status-Tracker.md +268 -0
- package/data/zeroheight-exports/Field-Inputs.md +965 -0
- package/data/zeroheight-exports/Filters.md +868 -0
- package/data/zeroheight-exports/Getting-Started.md +1296 -0
- package/data/zeroheight-exports/Hero.md +256 -0
- package/data/zeroheight-exports/Icons.md +432 -0
- package/data/zeroheight-exports/Intent.md +47 -0
- package/data/zeroheight-exports/Introduction-to-Bases.md +36 -0
- package/data/zeroheight-exports/Introduction-to-Cells.md +36 -0
- package/data/zeroheight-exports/Introduction-to-DNA.md +36 -0
- package/data/zeroheight-exports/Introduction-to-Genes.md +36 -0
- package/data/zeroheight-exports/Introduction.md +116 -0
- package/data/zeroheight-exports/Link.md +373 -0
- package/data/zeroheight-exports/Lists.md +617 -0
- package/data/zeroheight-exports/Loading-Indicator.md +38 -0
- package/data/zeroheight-exports/Navigation.md +853 -0
- package/data/zeroheight-exports/Notification.md +858 -0
- package/data/zeroheight-exports/Panel.md +862 -0
- package/data/zeroheight-exports/README.md +124 -0
- package/data/zeroheight-exports/Responsive-Design.md +275 -0
- package/data/zeroheight-exports/Search-Input.md +54 -0
- package/data/zeroheight-exports/Search.md +609 -0
- package/data/zeroheight-exports/Segmented-Control.md +561 -0
- package/data/zeroheight-exports/Spacing.md +298 -0
- package/data/zeroheight-exports/Table.md +4832 -0
- package/data/zeroheight-exports/Tabs.md +773 -0
- package/data/zeroheight-exports/Tags.md +1025 -0
- package/data/zeroheight-exports/Text-Link.md +185 -0
- package/data/zeroheight-exports/Theming.md +313 -0
- package/data/zeroheight-exports/Tooltips.md +1564 -0
- package/data/zeroheight-exports/Typography.md +769 -0
- package/dist/stdio.js +515 -12
- package/package.json +4 -3
|
@@ -0,0 +1,4832 @@
|
|
|
1
|
+
# Tables
|
|
2
|
+
|
|
3
|
+
Tables are used to store and visually organize data and other content into a series of cells grouped into a collection of rows and columns, helping users better understand and analyze a given set of information.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
## Table elements
|
|
8
|
+
|
|
9
|
+
| Tables are made up of a Table Header with as many Table Rows stacked below it as needed. Table Headers contain multiple Header Cells which serve as top-level indicators for the type of content found in the column of cells below each. Similarly, Table Rows are comprised of Basic Cells which contain the bulk of the data within a Table. Follow the usage criteria accompanying each variant as a guide for building a Table for any use case. | | **Jump to element:** Table Anatomy Basic Cell Header Cell Table Row Table Header |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table Anatomy
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Basic Cell
|
|
19
|
+
|
|
20
|
+
| | In Figma | | | Meets Accessibility | | | In napari hub + .org Codebases |
|
|
21
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
22
|
+
|
|
23
|
+
The Basic Cell is the most common cell type comprising a Table. It is intended to be filled with strings of text or numerical values.
|
|
24
|
+
|
|
25
|
+
Use the `horizontalAlign` and `verticalAlign` props to control how the content within the cell is positioned. To follow Table best practices, when displaying strings of text, set the `horizontalAlign` prop to `left`, when displaying numerical values, set the `horizontalAlign` prop to `right`.
|
|
26
|
+
|
|
27
|
+
Click on each variant below to enter inspect view and get details on its code attributes and any global styles it uses.
|
|
28
|
+
|
|
29
|
+
**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### Basic Cell Spacing
|
|
36
|
+
|
|
37
|
+
These rules establish how much margin should exist between and around elements. Multiple Basic Cells are placed beside each other to create a Table Row. Stack Table Rows below a Table Header to create a full Table.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Header Cell
|
|
44
|
+
|
|
45
|
+
| | In Figma | | | Meets Accessibility | | | In napari hub + .org Codebases |
|
|
46
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
47
|
+
|
|
48
|
+
Header Cells are placed at the top of Table columns and serve as the header or indicator of the content found in the cells below it.
|
|
49
|
+
|
|
50
|
+
Use the `horizontalAlign` prop to control how the content within the cell is positioned. The Header Cell's text position should match the horizontal alignment of the content cells below it. For content cells with centered-aligned content, set the Header Cell to `horizontalAlign: left`. Header Cells' vertical alignment is centered and cannot be adjusted.
|
|
51
|
+
|
|
52
|
+
Click on each variant below to enter inspect view and get details on its code attributes and any global styles it uses.
|
|
53
|
+
|
|
54
|
+
**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
### Header Cell Spacing
|
|
61
|
+
|
|
62
|
+
These rules establish how much margin should exist between and around elements. Multiple Header Cells are placed beside each other to create a Table Header. Stack Table Rows below a Table Header to create a full Table.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Table Row
|
|
67
|
+
|
|
68
|
+
| | In Figma | | | Meets Accessibility | | | In napari hub + .org Codebases |
|
|
69
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
70
|
+
|
|
71
|
+
A Table Row is a container filled with multiple Basic Cells placed side-by-side to form a row of different, but related pieces of information that all connect back to the same data source, such as a collected sample. Each data source should have its own row, meaning the Table will have as many Table Rows as there are data sources.
|
|
72
|
+
|
|
73
|
+
Click on each variant below to enter inspect view and get details on its code attributes and any global styles it uses.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### Table Row Spacing
|
|
78
|
+
|
|
79
|
+
These rules establish how much margin should exist between and around elements. Stack Table Rows below a Table Header to create a full Table.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Table Header
|
|
84
|
+
|
|
85
|
+
| | In Figma | | | Meets Accessibility | | | In napari hub + .org Codebases |
|
|
86
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
87
|
+
|
|
88
|
+
A Table Header is a container filled with multiple Header Cells placed side-by-side to form a header row. This row is placed at the top of the Table with each Header Cell in the row serving as the column header for the data in the content cells below it. There can only be one Table Header per Table.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### Table Header Spacing
|
|
93
|
+
|
|
94
|
+
These rules establish how much margin should exist between and around elements. Stack Table Rows below a Table Header to create a full Table.
|
|
95
|
+
|
|
96
|
+
## Code
|
|
97
|
+
|
|
98
|
+
## Table Components
|
|
99
|
+
|
|
100
|
+
| Below you will find an interactive Storybook iframe for Tables. Tabs within each iframe provide a different set of tools: The Controls tab allows you to see and adjust all available props. The Actions tab shows you data displayed by event handler arguments. | | **Jump to component:** CellBasic CellHeader TableRow TableHeader Table |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
>This component appears as it is in the default SDS codebase. Once imported into the napari hub or napari.org codebases, use a theme file to enable the napari visual appearance customization.
|
|
106
|
+
|
|
107
|
+
The technical documentation and code demos can be found here.
|
|
108
|
+
|
|
109
|
+
### CellBasic
|
|
110
|
+
|
|
111
|
+
Storybook
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### CellHeader
|
|
116
|
+
|
|
117
|
+
Storybook
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### TableRow
|
|
122
|
+
|
|
123
|
+
Storybook
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### TableHeader
|
|
128
|
+
|
|
129
|
+
Storybook
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### Table
|
|
134
|
+
|
|
135
|
+
Storybook
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
Tables are used to store and visually organize data and other content into a series of cells grouped into a collection of rows and columns, helping users better understand and analyze a given set of information.
|
|
140
|
+
|
|
141
|
+
## Overview
|
|
142
|
+
|
|
143
|
+
## Table elements
|
|
144
|
+
|
|
145
|
+
| Tables can be sorted, either ascending or descending, by clicking on any Header Cell that has sorting enabled. This will rearrange all of the content in the Table by the category in that column. Filters are often used in conjunction with Tables, allowing users to manipulate what content is viewable in the Table by the parameters indicated in the Filter. Follow the usage criteria accompanying each variant as a guide for building a Table for any use case. | | **Jump to element:** Table Anatomy Content Cells Header Cell Table Row Table Header CZ ID Tables |
|
|
146
|
+
| --- | --- | --- |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Table Anatomy
|
|
151
|
+
|
|
152
|
+
Tables are made up of a Table Header with as many Table Rows stacked below it as needed. Table Headers contain multiple Header Cells which serve as top-level indicators for the type of content found in the column of cells below each. Similarly, Table Rows are comprised of two different types of content cells, Basic Cells and Component Cells; these cell types contain the bulk of the data within a Table and are designed to be flexible in their ability to hold nearly any type of content. For Tables with large amounts of data, only data from visible cells will be loaded (colloquially known as *lazy load*); as the user scrolls down the Table, additional data will be pulled from the server and displayed once the data has fully loaded.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Content Cells
|
|
157
|
+
|
|
158
|
+
| | In Figma | | | Meets Accessibility | | | In CZ ID Codebase |
|
|
159
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
160
|
+
|
|
161
|
+
### Basic Cell
|
|
162
|
+
|
|
163
|
+
The Basic Cell is the most common cell type comprising a Table. It is intended to be filled with strings of text or numerical values. There is child node built in where designers can insert a range of elements, such as Icon Buttons or Tags, to provide additional actions users can take in relation to the content within the cell. There is also the option to include an Icon to the left of the cell text to communicate additional context.
|
|
164
|
+
|
|
165
|
+
Basic Cells have three levels of content hierarchy available within them: primary, secondary, and tertiary—providing designers flexibility to delineate content within the cell as needed by their datasets.
|
|
166
|
+
|
|
167
|
+
Use the `horizontalAlign` and `verticalAlign` props to control how the content within the cell is positioned. To follow Table best practices, when displaying strings of text, set the `horizontalAlign` prop to `left`, when displaying numerical values, set the `horizontalAlign` prop to `right`.
|
|
168
|
+
|
|
169
|
+
By default, the `wrap` prop is set to `true`, meaning text entered into the cell will wrap onto the next line if it is longer than the width of the cell. The height of the cell (and the corresponding Table Row) will expand to accommodate any additional lines of text that get wrapped. Use the `...TextWrapLineCount` props to establish how many lines text strings are allowed to wrap before they are truncated with an ellipsis. Changing the `wrap` prop to `false` will prevent overflow text from wrapping onto the next line and instead be truncated with an ellipsis. Enable the `shouldShowTooltipOnHover` prop to have truncated text appear in a Tooltip when the cell is hovered over.
|
|
170
|
+
|
|
171
|
+
### Basic Cell Variants
|
|
172
|
+
|
|
173
|
+
Click on each variant below to enter inspect view and get details on its code attributes and any global styles it uses.
|
|
174
|
+
|
|
175
|
+
**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### Component Cell
|
|
192
|
+
|
|
193
|
+
Use a Component Cell to construct more complex or custom cells that some products may have a need for as it can house multiple components in one cell. As an example, it can be used to construct a cell where there is both an icon on the left, primary text, secondary text, and a tag in the tertiary area of the cell, or to the right of the primary and secondary text. It can also be used for something simpler like placing Buttons, Toggles, etc. within a Table. This cell type can accept any type of element that is not a text string via the built-in child node and can be used as designers and engineers see necessary.
|
|
194
|
+
|
|
195
|
+
Component Cells have the same alignment options as Basic Cells; alignment is adjusted using the props `horizontalAlign` and `verticalAlign`.
|
|
196
|
+
|
|
197
|
+
Use the `fitCellToComponent` prop to have the cell size automatically adjust to accommodate the element placed within it.
|
|
198
|
+
|
|
199
|
+
### Component Cell Variants
|
|
200
|
+
|
|
201
|
+
Click on each variant below to enter inspect view and get details on its code attributes and any global styles it uses.
|
|
202
|
+
|
|
203
|
+
**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### Content Cell Spacing
|
|
212
|
+
|
|
213
|
+
These rules establish how much margin should exist between and around elements. Multiple content cells are placed beside each other to create a Table Row. Stack Table Rows below a Table Header to create a full Table.
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Header Cell
|
|
220
|
+
|
|
221
|
+
| | In Figma | | | Meets Accessibility | | | In CZ ID Codebase |
|
|
222
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
223
|
+
|
|
224
|
+
Header Cells are placed at the top of Table columns and serve as the header or indicator of the content found in the cells below it.
|
|
225
|
+
|
|
226
|
+
Use the `horizontalAlign` prop to control how the content within the cell is positioned. The Header Cell's text position should match the horizontal alignment of the content cells below it. For content cells with centered-aligned content, set the Header Cell to `horizontalAlign: left`. Header Cells' vertical alignment is centered and cannot be adjusted.
|
|
227
|
+
|
|
228
|
+
Users are able to manually sort columns in ascending or descending order by clicking on the Header Cell. On first click, the Table will rearrange itself and sort in the direction indicated on the sort icon. Click again and the sort direction will flip. Click a third time and the content will return to its non-sorted order. Use the `hideSortIcon` prop to show or hide the column sort icon that indicates the direction the column's content is sorted in.
|
|
229
|
+
|
|
230
|
+
Each Header Cell can have an optional Tooltip appear when hovered over by using the `shouldShowTooltipOnHover` prop. This can be used to display supplemental information or help further define the contents of the column.
|
|
231
|
+
|
|
232
|
+
| **Default** | **Hover** |
|
|
233
|
+
| --- | --- |
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
### Header Cell Variants
|
|
240
|
+
|
|
241
|
+
Click on each variant below to enter inspect view and get details on its code attributes and any global styles it uses.
|
|
242
|
+
|
|
243
|
+
**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
### Header Cell Spacing
|
|
256
|
+
|
|
257
|
+
These rules establish how much margin should exist between and around elements. Multiple Header Cells are placed beside each other to create a Table Header. Stack Table Rows below a Table Header to create a full Table.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Table Row
|
|
262
|
+
|
|
263
|
+
| | In Figma | | | Meets Accessibility | | | In CZ ID Codebase |
|
|
264
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
265
|
+
|
|
266
|
+
A Table Row is a container filled with multiple content cells placed side-by-side to form a row of different, but related pieces of information that all connect back to the same data source, such as a collected sample. Each data source should have its own row, meaning the Table will have as many Table Rows as there are data sources.
|
|
267
|
+
|
|
268
|
+
While each content cell has props that allow designers and engineers to adjust the features and functionality for each cell individually, the Table Row also has its own set of props that control how the row as a whole behaves.
|
|
269
|
+
|
|
270
|
+
To help users identify exactly which row they are hovering over at any given time, use the `hover` prop to trigger a visual change in the row on hover; this includes a shift in the row's background color and changing the user's cursor to a pointer. This can be coupled with the `shouldShowTooltipOnHover` prop, resulting in the appearance of a Tooltip when the row is hovered over, useful in instances where there's a need to communicate information about an entire row to users. Because individual content cells can have Tooltips enabled on them, it is best to only enable Tooltips at either the row level or individual content cell level and not both, otherwise users will trigger two Tooltips simultaneously when hovering which may cause confusion.
|
|
271
|
+
|
|
272
|
+
Some Table designs will allow users to select entire rows via Checkbox Inputs located in the first cell of the row and complete bulk actions on the content within selected rows (such as downloading, editing, etc. See the Table Row Actions section below for more details). In response to the input being checked, enable the Table Row's `selected` prop to change its background color to indicate that the row has been selected.
|
|
273
|
+
|
|
274
|
+
Enable the `useDivider` prop to help visually distinguish between Table Rows by adding a horizontal line along the bottom edge of the row.
|
|
275
|
+
|
|
276
|
+
Use the `disabled` prop to gray out all of the cells in the row and make it so users cannot interact with their content.
|
|
277
|
+
|
|
278
|
+
Click on each variant below to enter inspect view and get details on its code attributes and any global styles it uses.
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### Table Row Actions
|
|
293
|
+
|
|
294
|
+
For advanced Tables, Table Rows may have actions tied to them, such as the ability to edit, delete, download, etc., which allow users to modify or manipulate the contents of the cells within that row. Users complete these actions by clicking on corresponding Icon Buttons that are placed within a Component Cell in the final column at the end of a Table Row (1).
|
|
295
|
+
|
|
296
|
+
An overflow Icon Button, represented by the `DotsHorizontal3` icon (2), can be used to surface a Dropdown Menu (3) that contains additional actions. This should be used in instances where a large number of actions are needed (generally more than three) or the designer wants to save space in wide Tables that contain many columns. Table Row Actions can either be exposed on hover or can be persistent in the Table at all times.
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
For efficiency, Tables can be designed to allow users to complete actions across multiple Table Rows at the same time, known as bulk actions. Similar to row actions, bulk actions are completed by clicking on corresponding Icon Buttons that are located at the top right of the Table, above the Table Header; they should be inactive when no rows are selected (1).
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
To make Table Rows selectable, designers must include Checkbox Inputs in the first column of the row, providing an input for users to click on and select the row (2); enable the Table Row's `selected` prop to indicate when rows are selected. Once rows are selected, any applicable bulk action Icon Button should become active to allow users to take action across all selected rows (3). In addition to these Icon Buttons, there should be a counter present that indicates how many rows have been selected (4).
|
|
305
|
+
|
|
306
|
+
Because actions taken at the row level will impact stored data in the application, row and bulk actions are completely controlled by the application itself and not the SDS component.
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
### Table Row Spacing
|
|
313
|
+
|
|
314
|
+
These rules establish how much margin should exist between and around elements. Stack Table Rows below a Table Header to create a full Table.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Table Header
|
|
319
|
+
|
|
320
|
+
| | In Figma | | | Meets Accessibility | | | In CZ ID Codebase |
|
|
321
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
322
|
+
|
|
323
|
+
A Table Header is a container filled with multiple Header Cells placed side-by-side to form a header row. This row is placed at the top of the Table with each Header Cell in the row serving as the column header for the data in the content cells below it. There can only be one Table Header per Table.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
### Table Header Spacing
|
|
328
|
+
|
|
329
|
+
These rules establish how much margin should exist between and around elements. Stack Table Rows below a Table Header to create a full Table.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## CZ ID Tables
|
|
334
|
+
|
|
335
|
+
The following Table configurations are currently in use in CZ ID.
|
|
336
|
+
|
|
337
|
+
### Download Table
|
|
338
|
+
|
|
339
|
+
### Project Table
|
|
340
|
+
|
|
341
|
+
### Sample Table – Consensus Genome
|
|
342
|
+
|
|
343
|
+
### Sample Table – Metagenomic
|
|
344
|
+
|
|
345
|
+
### Taxon Table
|
|
346
|
+
|
|
347
|
+
## Code
|
|
348
|
+
|
|
349
|
+
## Table Components
|
|
350
|
+
|
|
351
|
+
| Below you will find an interactive Storybook iframe for Tables. Tabs within each iframe provide a different set of tools: The Controls tab allows you to see and adjust all available props. The Actions tab shows you data displayed by event handler arguments. | | **Jump to component:** CellBasic CellComponent CellHeader TableRow TableHeader Table |
|
|
352
|
+
| --- | --- | --- |
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
The technical documentation and code demos can be found here.
|
|
357
|
+
|
|
358
|
+
### CellBasic
|
|
359
|
+
|
|
360
|
+
Storybook
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
### CellComponent
|
|
367
|
+
|
|
368
|
+
Storybook
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
### CellHeader
|
|
373
|
+
|
|
374
|
+
Storybook
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
### TableRow
|
|
379
|
+
|
|
380
|
+
Storybook
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
###
|
|
385
|
+
|
|
386
|
+
### TableHeader
|
|
387
|
+
|
|
388
|
+
Storybook
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
### Table
|
|
393
|
+
|
|
394
|
+
Storybook
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
Tables are used to store and visually organize data and other content into a series of cells grouped into a collection of rows and columns, helping users better understand and analyze a given set of information.
|
|
401
|
+
|
|
402
|
+
## Guidelines
|
|
403
|
+
|
|
404
|
+
## Table Components
|
|
405
|
+
|
|
406
|
+
| Tables are built by combining Header Cells with Basic or Component Cells, providing flexibility for teams to create Tables that house any type of content their product's require. Tables can be sorted, either ascending or descending, by clicking on any Header Cell that has sorting enabled. To avoid slow loading speeds in Tables that contain large amounts of data, they should be split across multiple pages; incorporate a Pagination component to help users navigate between the pages of multi-page Tables. Filters are often used in conjunction with Tables, allowing users to manipulate what content is viewable in the Table by the parameters indicated in the Filter. | | **Jump to element:** Table Anatomy Basic Cell Component Cell Header Cell Table Row Table Header Pagination |
|
|
407
|
+
| --- | --- | --- |
|
|
408
|
+
|
|
409
|
+
### Visual Preview
|
|
410
|
+
|
|
411
|
+
>**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
### Table Anatomy
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Basic Cell
|
|
420
|
+
|
|
421
|
+
| | In Figma | | | Meets Accessibility | | | In Code |
|
|
422
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
423
|
+
|
|
424
|
+
### Live Instance
|
|
425
|
+
|
|
426
|
+
Interact with the live instance below and adjust the available props to better conceptualize how it will appear in product:
|
|
427
|
+
|
|
428
|
+
Default
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
### Overview
|
|
433
|
+
|
|
434
|
+
The Basic Cell is intended to be filled with strings of text or numerical values. A range of elements can be included alongside text, such as Icon Buttons or Tags, to provide additional actions users can take in relation to the content within the cell.
|
|
435
|
+
|
|
436
|
+
>**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
437
|
+
|
|
438
|
+
| Default | Default + Secondary text | Default + Secondary text + Tertiary text | Default (right aligned) |
|
|
439
|
+
| --- | --- | --- | --- |
|
|
440
|
+
|
|
441
|
+
| Default (wrapped) | Hover (truncated) + Tooltip | Basic Cell + Icon | Basic Cell + Tag |
|
|
442
|
+
| --- | --- | --- | --- |
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
### Basic Cell Spacing
|
|
447
|
+
|
|
448
|
+
These rules establish how much margin should exist between and around elements. Multiple Basic and/or Component Cells are placed beside each other to create a Table Row. Stack Table Rows below a Table Header to create a full Table.
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## Component Cell
|
|
453
|
+
|
|
454
|
+
| | In Figma | | | Meets Accessibility | | | In Code |
|
|
455
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
456
|
+
|
|
457
|
+
### Live Instance
|
|
458
|
+
|
|
459
|
+
Interact with the live instance below and adjust the available props to better conceptualize how it will appear in product:
|
|
460
|
+
|
|
461
|
+
Default
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
### Overview
|
|
466
|
+
|
|
467
|
+
Component Cells can accept any type of element, including any combination or number of components, but should not be used to display text strings (use a Basic Cell for this). Most commonly it is used to hold Buttons, Toggles, and Checkbox Inputs.
|
|
468
|
+
|
|
469
|
+
>**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
470
|
+
|
|
471
|
+
| Default | Filled (left aligned) | Filled (center aligned) | Filled (right aligned) | Filled (fit contents) |
|
|
472
|
+
| --- | --- | --- | --- | --- |
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
### Component Cell Spacing
|
|
477
|
+
|
|
478
|
+
These rules establish how much margin should exist between and around elements. Multiple Basic and/or Component Cells are placed beside each other to create a Table Row. Stack Table Rows below a Table Header to create a full Table.
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Header Cell
|
|
483
|
+
|
|
484
|
+
| | In Figma | | | Meets Accessibility | | | In Code |
|
|
485
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
486
|
+
|
|
487
|
+
### Live Instance
|
|
488
|
+
|
|
489
|
+
Interact with the live instance below and adjust the available props to better conceptualize how it will appear in product:
|
|
490
|
+
|
|
491
|
+
Default
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
### Overview
|
|
496
|
+
|
|
497
|
+
Header Cells are placed at the top of Table columns and serve as the header for the content found in the cells below it. The Header Cell's horizontal alignment should match the text position of the Basic or Component Cell below it. For Basic or Component Cells with centered-aligned content, the Header Cell should be left aligned. Header Cells' vertical alignment is centered and cannot be adjusted.
|
|
498
|
+
|
|
499
|
+
Users are able to manually sort columns in ascending or descending order by clicking on the Header Cell. On first click, the Table will rearrange itself and sort in the direction indicated on the sort icon. A second click and the sort direction will flip. A third click and the content will return to its non-sorted order.
|
|
500
|
+
|
|
501
|
+
Each Header Cell can have an optional Tooltip appear when hovered over. This can be used to display supplemental information or help further define the contents of the column below it.
|
|
502
|
+
|
|
503
|
+
>**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
504
|
+
|
|
505
|
+
| Default | Default (center aligned) | Default (right aligned) | Hover + Tooltip | Active (ascending) | Active (descending) |
|
|
506
|
+
| --- | --- | --- | --- | --- | --- |
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
### Header Cell Spacing
|
|
511
|
+
|
|
512
|
+
These rules establish how much margin should exist between and around elements. Multiple Header Cells are placed beside each other to create a Table Header. Stack Table Rows below a Table Header to create a full Table.
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## Table Row
|
|
517
|
+
|
|
518
|
+
| | In Figma | | | Meets Accessibility | | | In Code |
|
|
519
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
520
|
+
|
|
521
|
+
### Live Instance
|
|
522
|
+
|
|
523
|
+
Interact with the live instance below and adjust the available props to better conceptualize how it will appear in product:
|
|
524
|
+
|
|
525
|
+
Default
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
### Overview
|
|
530
|
+
|
|
531
|
+
A Table Row is a container filled with multiple Basic or Component Cells placed side-by-side to form a row of different, but related pieces of information, for example, data related to a collected sample.
|
|
532
|
+
|
|
533
|
+
The Table Row also has its own set of props that control how the row as a whole behaves. For example, Table Rows can have Tooltips enabled on them that appear when the row is hovered over, communicating information about an entire row to users. Be sure to only enable Tooltips on either the row or cell and not both, so users don't trigger two Tooltips simultaneously.
|
|
534
|
+
|
|
535
|
+
Additionally, Checkbox Inputs can be placed within a Component Cell in the first column of a row, giving users the ability to select entire rows and complete bulk actions on them, such as downloading, editing, etc. See the Table Row Actions section below for more details.
|
|
536
|
+
|
|
537
|
+
| Default | Default (no divider) | Hover + Tooltip | Selected | Disabled |
|
|
538
|
+
| --- | --- | --- | --- | --- |
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
### Table Row Actions
|
|
543
|
+
|
|
544
|
+
Table Rows may have actions tied to them, such as the ability to edit, delete, download, etc. Users complete these actions by clicking on corresponding Icon Buttons that are placed within a Component Cell in the final column at the end of a Table Row (1). Table Row Actions can either be exposed on hover or can be persistent in the Table at all times.
|
|
545
|
+
|
|
546
|
+
#### Overflow Menu
|
|
547
|
+
|
|
548
|
+
An overflow Icon Button, represented by the `DotsHorizontal3` icon (2), can be used to surface a Dropdown Menu (3) that contains additional actions. This should be used in instances where a large number of actions are needed (generally more than three) or the designer wants to save space in wide Tables that contain many columns.
|
|
549
|
+
|
|
550
|
+
#### Bulk Actions
|
|
551
|
+
|
|
552
|
+
Tables can be designed to allow users to complete actions across multiple Table Rows at the same time, known as bulk actions. Similar to row actions, bulk actions are completed by clicking on corresponding Icon Buttons that are located at the top right of the Table, above the Table Header; they should be inactive when no rows are selected (4).
|
|
553
|
+
|
|
554
|
+
#### Selectable Rows
|
|
555
|
+
|
|
556
|
+
To make Table Rows selectable, designers must include Checkbox Inputs in the first column of the row (5). It is recommended that only once rows are selected should applicable bulk action Icon Buttons become enabled, allowing users to take action across all selected rows (6). How this functionality is implemented is ultimately up to the product team. Add a counter to indicate how many rows have been selected (7).
|
|
557
|
+
|
|
558
|
+
Because actions taken at the row level will impact stored data in the application, row and bulk actions are completely controlled by the application itself and not the SDS component.
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
### Table Row Spacing
|
|
563
|
+
|
|
564
|
+
These rules establish how much margin should exist between and around elements. Stack Table Rows below a Table Header to create a full Table.
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## Table Header
|
|
569
|
+
|
|
570
|
+
| | In Figma | | | Meets Accessibility | | | In Code |
|
|
571
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
572
|
+
|
|
573
|
+
### Live Instance
|
|
574
|
+
|
|
575
|
+
Interact with the live instance below and adjust the available props to better conceptualize how it will appear in product:
|
|
576
|
+
|
|
577
|
+
Default
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
### Overview
|
|
582
|
+
|
|
583
|
+
A Table Header is a container filled with multiple Header Cells placed side-by-side to form a header row. This row is placed at the top of a Table with each Header Cell serving as the column header for the content below it; a 2px border should be placed below it. There can only be one Table Header per Table.
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
### Table Header Spacing
|
|
588
|
+
|
|
589
|
+
These rules establish how much margin should exist between and around elements. Stack Table Rows below a Table Header to create a full Table.
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
## Pagination
|
|
594
|
+
|
|
595
|
+
| | In Figma | | | Meets Accessibility | | | In Code |
|
|
596
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
597
|
+
|
|
598
|
+
### Live Instance
|
|
599
|
+
|
|
600
|
+
Interact with the live instance below and adjust the available props to better conceptualize how it will appear in product:
|
|
601
|
+
|
|
602
|
+
Default
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
### Overview
|
|
607
|
+
|
|
608
|
+
Large Tables may have long loading times. To mitigate this, Tables can be split across multiple pages with the addition of a Pagination component to navigate between these pages. Place the Pagination component below the final Table Row on each page. It is right-aligned by default, but can optionally be center or left-aligned.
|
|
609
|
+
|
|
610
|
+
Users are able to click on any page number to jump immediately to that page; otherwise they can use the forward and back Icon Buttons to navigate page-by-page. For Tables that are longer than seven pages, ellipses are displayed to represent any additional pages not viewable. When enabled, the ellipses between page numbers can be clicked on, allowing users to select a page to jump to via a Dropdown Menu. When disabled, the ellipses will still be visible but not interactive.
|
|
611
|
+
|
|
612
|
+
| Default | Default (square) | Hover | Default (ellipsis disabled) | Default (ellipsis enabled) |
|
|
613
|
+
| --- | --- | --- | --- | --- |
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
### Pagination Spacing
|
|
618
|
+
|
|
619
|
+
These rules establish how much margin should exist between and around elements. Place Pagination below the final Table Row on each page of the Table.
|
|
620
|
+
|
|
621
|
+
## Code
|
|
622
|
+
|
|
623
|
+
## Table Components
|
|
624
|
+
|
|
625
|
+
| `Tables` are built by combining `CellHeader` with `CellBasic` or `CellComponent`, providing flexibility for teams to create `Tables` that house any type of content their product's require. `Tables` can be sorted, either ascending or descending, by clicking on any `CellHeader` that has sorting enabled. To avoid slow loading speeds in `Tables` that contain large amounts of data, they should be split across multiple pages; incorporate `Pagination` to help users navigate between the pages of multi-page Tables. `Filters` are often used in conjunction with Tables, allowing users to manipulate what content is viewable in the `Table` by the parameters indicated in the `Filter`. | | **Jump to component:** CellBasic CellComponent CellHeader TableRow Pagination |
|
|
626
|
+
| --- | --- | --- |
|
|
627
|
+
|
|
628
|
+
### Visual Preview
|
|
629
|
+
|
|
630
|
+
>**Note:** Dotted lines around the cells below are only for demonstration purposes to show the cells' containers; in application, individual cells have no borders.
|
|
631
|
+
|
|
632
|
+
>**Note:** The code examples below must install dependencies before displaying and may take extra time to load
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
## CellBasic
|
|
637
|
+
|
|
638
|
+
### SDS Source Code
|
|
639
|
+
|
|
640
|
+
The component's source code in the SDS codebase can be found here.
|
|
641
|
+
|
|
642
|
+
### Props
|
|
643
|
+
|
|
644
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
645
|
+
|
|
646
|
+
| Name | Type | Default | Description |
|
|
647
|
+
| --- | --- | --- | --- |
|
|
648
|
+
| `horizontalAlign` | `"left"` `| "center"` `| "right"` | `"left"` | Horizontal alignment of cell content. |
|
|
649
|
+
| `icon` | `Node` | `-` | Places an icon at the left side of the cell component. |
|
|
650
|
+
| `iconVerticalAlign` | `"top"` `| "center"` `| "bottom"` | `"top"` | Vertical alignment of the icon component. |
|
|
651
|
+
| `primaryText` | `string` | `-` | Primary text value. |
|
|
652
|
+
| `primaryTextWrapLineCount` | `number` | `3` | Number of text wrap lines for primary text. |
|
|
653
|
+
| `primaryTextComponentSlotBottom` | `ReactNode` | `null` | Component slot below primary text. This will only show if there is no secondary or tertiary text. Remove the default values of secondary and tertiary text to see this in action. |
|
|
654
|
+
| `primaryTextComponentSlotRight` | `ReactNode` | `null` | Component slot to the right of primary text |
|
|
655
|
+
| `secondaryText` | `string` | `-` | Secondary Text. |
|
|
656
|
+
| `secondaryTextWrapLineCount` | `number` | `1` | Number of text wrap lines for secondary text. |
|
|
657
|
+
| `tertiaryText` | `string` | `-` | Tertiary Text. |
|
|
658
|
+
| `tertiaryTextWrapLineCount` | `number` | `1` | Number of text wrap lines for tertiary text. |
|
|
659
|
+
| `shouldShowTooltipOnHover` | `boolean` | `true` | When set to `true`, a tooltip will be shown on cell hover. |
|
|
660
|
+
| `shouldTextWrap` | `boolean` | `true` | When set to `true`, wraps the content texts by the number of defined wrap line numbers. |
|
|
661
|
+
| `tooltipProps` | `Partial<TooltipProps>` | `{}` | These props will be directly passed to the `Tooltip` component. |
|
|
662
|
+
| `verticalAlign` | `"top"` `| "center"` `| "bottom"` | `"top"` | Vertical alignment of the cell content. |
|
|
663
|
+
|
|
664
|
+
### Code examples
|
|
665
|
+
|
|
666
|
+
#### Default CellBasic
|
|
667
|
+
|
|
668
|
+
This example demonstrates the minimum required props for rendering a `CellBasic` component.
|
|
669
|
+
|
|
670
|
+
**/styles.css**
|
|
671
|
+
|
|
672
|
+
```css
|
|
673
|
+
body {
|
|
674
|
+
font-size: 13px;
|
|
675
|
+
font-weight: 400;
|
|
676
|
+
letter-spacing: 0.08px;
|
|
677
|
+
line-height: 20px;
|
|
678
|
+
text-transform: none;
|
|
679
|
+
font-family: "Inter", sans-serif;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
h1 {
|
|
683
|
+
font-size: 1.5rem;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.app {
|
|
687
|
+
padding: 20px;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
table {
|
|
691
|
+
border: dashed 1px #eee;
|
|
692
|
+
}
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
**tsconfig.json**
|
|
696
|
+
|
|
697
|
+
```json
|
|
698
|
+
{
|
|
699
|
+
"include": [
|
|
700
|
+
"./**/*"
|
|
701
|
+
],
|
|
702
|
+
"compilerOptions": {
|
|
703
|
+
"strict": true,
|
|
704
|
+
"esModuleInterop": true,
|
|
705
|
+
"lib": [ "dom", "es2015" ],
|
|
706
|
+
"jsx": "react-jsx"
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
**/App.tsx**
|
|
712
|
+
|
|
713
|
+
```tsx
|
|
714
|
+
import { CellBasic } from "@czi-sds/components";
|
|
715
|
+
import "./styles.css";
|
|
716
|
+
|
|
717
|
+
function App() {
|
|
718
|
+
return (
|
|
719
|
+
<div className="app">
|
|
720
|
+
<table>
|
|
721
|
+
<tbody>
|
|
722
|
+
<tr>
|
|
723
|
+
<CellBasic
|
|
724
|
+
primaryText="Primary Text"
|
|
725
|
+
secondaryText="Secondary Text"
|
|
726
|
+
/>
|
|
727
|
+
</tr>
|
|
728
|
+
</tbody>
|
|
729
|
+
</table>
|
|
730
|
+
</div>
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
export default App;
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
**/index.tsx**
|
|
738
|
+
|
|
739
|
+
```tsx
|
|
740
|
+
import React from "react";
|
|
741
|
+
import { createRoot } from "react-dom/client";
|
|
742
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
743
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
744
|
+
import { Theme } from "@czi-sds/components";
|
|
745
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
746
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
747
|
+
|
|
748
|
+
import App from "./App";
|
|
749
|
+
|
|
750
|
+
const container = document.getElementById("root");
|
|
751
|
+
const root = createRoot(container!);
|
|
752
|
+
|
|
753
|
+
const RootApp = () => {
|
|
754
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
755
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
756
|
+
|
|
757
|
+
return (
|
|
758
|
+
<StyledEngineProvider injectFirst>
|
|
759
|
+
<ThemeProvider theme={theme}>
|
|
760
|
+
<EmotionThemeProvider theme={theme}>
|
|
761
|
+
<CssBaseline />
|
|
762
|
+
<App />
|
|
763
|
+
</EmotionThemeProvider>
|
|
764
|
+
</ThemeProvider>
|
|
765
|
+
</StyledEngineProvider>
|
|
766
|
+
);
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
root.render(
|
|
770
|
+
<React.StrictMode>
|
|
771
|
+
<RootApp />
|
|
772
|
+
</React.StrictMode>
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
**/public/index.html**
|
|
778
|
+
|
|
779
|
+
```html
|
|
780
|
+
<!DOCTYPE html>
|
|
781
|
+
<html lang="en">
|
|
782
|
+
<head>
|
|
783
|
+
<meta charset="UTF-8" />
|
|
784
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
785
|
+
<title>Table</title>
|
|
786
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
787
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
788
|
+
<link
|
|
789
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
790
|
+
rel="stylesheet"
|
|
791
|
+
/>
|
|
792
|
+
<link
|
|
793
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
794
|
+
rel="stylesheet"
|
|
795
|
+
/>
|
|
796
|
+
</head>
|
|
797
|
+
<body>
|
|
798
|
+
<div id="root"></div>
|
|
799
|
+
</body>
|
|
800
|
+
</html>
|
|
801
|
+
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
**/package.json**
|
|
805
|
+
|
|
806
|
+
```json
|
|
807
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
---
|
|
811
|
+
|
|
812
|
+
#### CellBasic with Icon and Text Wrap
|
|
813
|
+
|
|
814
|
+
This example highlights a `CellBasic` component featuring an SDS icon and lengthy texts for Primary, Secondary, and Tertiary content, all thoughtfully wrapped for optimal presentation.
|
|
815
|
+
|
|
816
|
+
**/styles.css**
|
|
817
|
+
|
|
818
|
+
```css
|
|
819
|
+
body {
|
|
820
|
+
font-size: 13px;
|
|
821
|
+
font-weight: 400;
|
|
822
|
+
letter-spacing: 0.08px;
|
|
823
|
+
line-height: 20px;
|
|
824
|
+
text-transform: none;
|
|
825
|
+
font-family: "Inter", sans-serif;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
h1 {
|
|
829
|
+
font-size: 1.5rem;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
.app {
|
|
833
|
+
padding: 20px;
|
|
834
|
+
}
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
**tsconfig.json**
|
|
838
|
+
|
|
839
|
+
```json
|
|
840
|
+
{
|
|
841
|
+
"include": [
|
|
842
|
+
"./**/*"
|
|
843
|
+
],
|
|
844
|
+
"compilerOptions": {
|
|
845
|
+
"strict": true,
|
|
846
|
+
"esModuleInterop": true,
|
|
847
|
+
"lib": [ "dom", "es2015" ],
|
|
848
|
+
"jsx": "react-jsx"
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
**/App.tsx**
|
|
854
|
+
|
|
855
|
+
```tsx
|
|
856
|
+
import { CellBasic, Icon } from "@czi-sds/components";
|
|
857
|
+
import "./styles.css";
|
|
858
|
+
|
|
859
|
+
function App() {
|
|
860
|
+
const TableCellStyle = {
|
|
861
|
+
border: "dashed 1px #eee",
|
|
862
|
+
maxWidth: 160,
|
|
863
|
+
width: 160,
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
return (
|
|
867
|
+
<div className="app">
|
|
868
|
+
<table>
|
|
869
|
+
<tbody>
|
|
870
|
+
<tr>
|
|
871
|
+
<CellBasic
|
|
872
|
+
primaryText="Primary Text that is longer than expected."
|
|
873
|
+
secondaryText="Secondary Text that is londer than expected."
|
|
874
|
+
tertiaryText="Tertiary Text that is lonegrt than expected."
|
|
875
|
+
icon={<Icon sdsIcon="Bacteria" sdsSize="l" sdsType="static" color="notice" />}
|
|
876
|
+
style={TableCellStyle}
|
|
877
|
+
tooltipProps={{
|
|
878
|
+
placement: "right",
|
|
879
|
+
sdsStyle: "light"
|
|
880
|
+
}}
|
|
881
|
+
/>
|
|
882
|
+
</tr>
|
|
883
|
+
</tbody>
|
|
884
|
+
</table>
|
|
885
|
+
</div>
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
export default App;
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
**/index.tsx**
|
|
893
|
+
|
|
894
|
+
```tsx
|
|
895
|
+
import React from "react";
|
|
896
|
+
import { createRoot } from "react-dom/client";
|
|
897
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
898
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
899
|
+
import { Theme } from "@czi-sds/components";
|
|
900
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
901
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
902
|
+
|
|
903
|
+
import App from "./App";
|
|
904
|
+
|
|
905
|
+
const container = document.getElementById("root");
|
|
906
|
+
const root = createRoot(container!);
|
|
907
|
+
|
|
908
|
+
const RootApp = () => {
|
|
909
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
910
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
911
|
+
|
|
912
|
+
return (
|
|
913
|
+
<StyledEngineProvider injectFirst>
|
|
914
|
+
<ThemeProvider theme={theme}>
|
|
915
|
+
<EmotionThemeProvider theme={theme}>
|
|
916
|
+
<CssBaseline />
|
|
917
|
+
<App />
|
|
918
|
+
</EmotionThemeProvider>
|
|
919
|
+
</ThemeProvider>
|
|
920
|
+
</StyledEngineProvider>
|
|
921
|
+
);
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
root.render(
|
|
925
|
+
<React.StrictMode>
|
|
926
|
+
<RootApp />
|
|
927
|
+
</React.StrictMode>
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
**/public/index.html**
|
|
933
|
+
|
|
934
|
+
```html
|
|
935
|
+
<!DOCTYPE html>
|
|
936
|
+
<html lang="en">
|
|
937
|
+
<head>
|
|
938
|
+
<meta charset="UTF-8" />
|
|
939
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
940
|
+
<title>Table</title>
|
|
941
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
942
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
943
|
+
<link
|
|
944
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
945
|
+
rel="stylesheet"
|
|
946
|
+
/>
|
|
947
|
+
<link
|
|
948
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
949
|
+
rel="stylesheet"
|
|
950
|
+
/>
|
|
951
|
+
</head>
|
|
952
|
+
<body>
|
|
953
|
+
<div id="root"></div>
|
|
954
|
+
</body>
|
|
955
|
+
</html>
|
|
956
|
+
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
**/package.json**
|
|
960
|
+
|
|
961
|
+
```json
|
|
962
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
---
|
|
966
|
+
|
|
967
|
+
---
|
|
968
|
+
|
|
969
|
+
## CellComponent
|
|
970
|
+
|
|
971
|
+
### SDS Source Code
|
|
972
|
+
|
|
973
|
+
The component's source code in the SDS codebase can be found here.
|
|
974
|
+
|
|
975
|
+
### Props
|
|
976
|
+
|
|
977
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
978
|
+
|
|
979
|
+
| Name | Type | Default | Description |
|
|
980
|
+
| --- | --- | --- | --- |
|
|
981
|
+
| `horizontalAlign` | `"left"` `| "center"` `| "right"` | `"left"` | Specifies the horizontal alignment of the cell content. |
|
|
982
|
+
| `verticalAlign` | `"top"` `| "center"` `| "bottom"` | `"top"` | Specifies the vertical alignment of the cell content. |
|
|
983
|
+
|
|
984
|
+
### Code examples
|
|
985
|
+
|
|
986
|
+
#### **Default CellComponent**
|
|
987
|
+
|
|
988
|
+
This example demonstrates the minimum required props for rendering a `CellComponent` component.
|
|
989
|
+
|
|
990
|
+
**/styles.css**
|
|
991
|
+
|
|
992
|
+
```css
|
|
993
|
+
body {
|
|
994
|
+
font-size: 13px;
|
|
995
|
+
font-weight: 400;
|
|
996
|
+
letter-spacing: 0.08px;
|
|
997
|
+
line-height: 20px;
|
|
998
|
+
text-transform: none;
|
|
999
|
+
font-family: "Inter", sans-serif;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
h1 {
|
|
1003
|
+
font-size: 1.5rem;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.app {
|
|
1007
|
+
padding: 20px;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
table {
|
|
1011
|
+
border: dashed 1px #eee;
|
|
1012
|
+
}
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
**tsconfig.json**
|
|
1016
|
+
|
|
1017
|
+
```json
|
|
1018
|
+
{
|
|
1019
|
+
"include": [
|
|
1020
|
+
"./**/*"
|
|
1021
|
+
],
|
|
1022
|
+
"compilerOptions": {
|
|
1023
|
+
"strict": true,
|
|
1024
|
+
"esModuleInterop": true,
|
|
1025
|
+
"lib": [ "dom", "es2015" ],
|
|
1026
|
+
"jsx": "react-jsx"
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
**/App.tsx**
|
|
1032
|
+
|
|
1033
|
+
```tsx
|
|
1034
|
+
import { CellComponent, InputToggle } from "@czi-sds/components";
|
|
1035
|
+
import "./styles.css";
|
|
1036
|
+
|
|
1037
|
+
function App() {
|
|
1038
|
+
return (
|
|
1039
|
+
<div className="app">
|
|
1040
|
+
<table>
|
|
1041
|
+
<tbody>
|
|
1042
|
+
<tr>
|
|
1043
|
+
<CellComponent>
|
|
1044
|
+
<InputToggle />
|
|
1045
|
+
</CellComponent>
|
|
1046
|
+
</tr>
|
|
1047
|
+
</tbody>
|
|
1048
|
+
</table>
|
|
1049
|
+
</div>
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
export default App;
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
**/index.tsx**
|
|
1057
|
+
|
|
1058
|
+
```tsx
|
|
1059
|
+
import React from "react";
|
|
1060
|
+
import { createRoot } from "react-dom/client";
|
|
1061
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
1062
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
1063
|
+
import { Theme } from "@czi-sds/components";
|
|
1064
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
1065
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
1066
|
+
|
|
1067
|
+
import App from "./App";
|
|
1068
|
+
|
|
1069
|
+
const container = document.getElementById("root");
|
|
1070
|
+
const root = createRoot(container!);
|
|
1071
|
+
|
|
1072
|
+
const RootApp = () => {
|
|
1073
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
1074
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
1075
|
+
|
|
1076
|
+
return (
|
|
1077
|
+
<StyledEngineProvider injectFirst>
|
|
1078
|
+
<ThemeProvider theme={theme}>
|
|
1079
|
+
<EmotionThemeProvider theme={theme}>
|
|
1080
|
+
<CssBaseline />
|
|
1081
|
+
<App />
|
|
1082
|
+
</EmotionThemeProvider>
|
|
1083
|
+
</ThemeProvider>
|
|
1084
|
+
</StyledEngineProvider>
|
|
1085
|
+
);
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
root.render(
|
|
1089
|
+
<React.StrictMode>
|
|
1090
|
+
<RootApp />
|
|
1091
|
+
</React.StrictMode>
|
|
1092
|
+
);
|
|
1093
|
+
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
**/public/index.html**
|
|
1097
|
+
|
|
1098
|
+
```html
|
|
1099
|
+
<!DOCTYPE html>
|
|
1100
|
+
<html lang="en">
|
|
1101
|
+
<head>
|
|
1102
|
+
<meta charset="UTF-8" />
|
|
1103
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1104
|
+
<title>Table</title>
|
|
1105
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
1106
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
1107
|
+
<link
|
|
1108
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
1109
|
+
rel="stylesheet"
|
|
1110
|
+
/>
|
|
1111
|
+
<link
|
|
1112
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
1113
|
+
rel="stylesheet"
|
|
1114
|
+
/>
|
|
1115
|
+
</head>
|
|
1116
|
+
<body>
|
|
1117
|
+
<div id="root"></div>
|
|
1118
|
+
</body>
|
|
1119
|
+
</html>
|
|
1120
|
+
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
**/package.json**
|
|
1124
|
+
|
|
1125
|
+
```json
|
|
1126
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
---
|
|
1130
|
+
|
|
1131
|
+
---
|
|
1132
|
+
|
|
1133
|
+
## CellHeader
|
|
1134
|
+
|
|
1135
|
+
### SDS Source Code
|
|
1136
|
+
|
|
1137
|
+
The component's source code in the SDS codebase can be found here.
|
|
1138
|
+
|
|
1139
|
+
### Props
|
|
1140
|
+
|
|
1141
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
1142
|
+
|
|
1143
|
+
| Name | Type | Default | Description |
|
|
1144
|
+
| --- | --- | --- | --- |
|
|
1145
|
+
| `active` | `boolean` | `false` | When `true`, the component is in active state. |
|
|
1146
|
+
| `hover` | `boolean` | `false` | By default, the component is not interactive and does not have a hover state. When set to `true`, it displays a `sortIcon` and changes color on hover. |
|
|
1147
|
+
| `direction` | `"asc" | "desc"` | `"desc"` | Changes the chevron icon based on the sort direction. |
|
|
1148
|
+
| `hideSortIcon` | `boolean` | `false` | If set to `true`, the sort icon becomes hidden. |
|
|
1149
|
+
| `horizontalAlign` | `"left"` `| "center"` `| "right"` | `"left"` | Horizontally aligns the component's content. |
|
|
1150
|
+
| `shouldShowTooltipOnHover` | `boolean` | `true` | When `true`, show a tooltip on component hover. |
|
|
1151
|
+
| `tooltipText` | `string` | `-` | The content of the tooltip. |
|
|
1152
|
+
| `tooltipProps` | `Partial<TooltipProps>` | `{}` | These props are directly passed to the tooltip component. |
|
|
1153
|
+
|
|
1154
|
+
### Code examples
|
|
1155
|
+
|
|
1156
|
+
#### **Default CellHeader**
|
|
1157
|
+
|
|
1158
|
+
This example demonstrates the minimum required props for rendering a `CellHeader` component.
|
|
1159
|
+
|
|
1160
|
+
**/styles.css**
|
|
1161
|
+
|
|
1162
|
+
```css
|
|
1163
|
+
body {
|
|
1164
|
+
font-size: 13px;
|
|
1165
|
+
font-weight: 400;
|
|
1166
|
+
letter-spacing: 0.08px;
|
|
1167
|
+
line-height: 20px;
|
|
1168
|
+
text-transform: none;
|
|
1169
|
+
font-family: "Inter", sans-serif;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
h1 {
|
|
1173
|
+
font-size: 1.5rem;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
.app {
|
|
1177
|
+
padding: 20px;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
table {
|
|
1181
|
+
border: dashed 1px #eee;
|
|
1182
|
+
}
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
**tsconfig.json**
|
|
1186
|
+
|
|
1187
|
+
```json
|
|
1188
|
+
{
|
|
1189
|
+
"include": [
|
|
1190
|
+
"./**/*"
|
|
1191
|
+
],
|
|
1192
|
+
"compilerOptions": {
|
|
1193
|
+
"strict": true,
|
|
1194
|
+
"esModuleInterop": true,
|
|
1195
|
+
"lib": [ "dom", "es2015" ],
|
|
1196
|
+
"jsx": "react-jsx"
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
**/App.tsx**
|
|
1202
|
+
|
|
1203
|
+
```tsx
|
|
1204
|
+
import { CellHeader } from "@czi-sds/components";
|
|
1205
|
+
import "./styles.css";
|
|
1206
|
+
|
|
1207
|
+
function App() {
|
|
1208
|
+
return (
|
|
1209
|
+
<div className="app">
|
|
1210
|
+
<table>
|
|
1211
|
+
<thead>
|
|
1212
|
+
<tr>
|
|
1213
|
+
<CellHeader>
|
|
1214
|
+
Header
|
|
1215
|
+
</CellHeader>
|
|
1216
|
+
</tr>
|
|
1217
|
+
</thead>
|
|
1218
|
+
</table>
|
|
1219
|
+
</div>
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
export default App;
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
**/index.tsx**
|
|
1227
|
+
|
|
1228
|
+
```tsx
|
|
1229
|
+
import React from "react";
|
|
1230
|
+
import { createRoot } from "react-dom/client";
|
|
1231
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
1232
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
1233
|
+
import { Theme } from "@czi-sds/components";
|
|
1234
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
1235
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
1236
|
+
|
|
1237
|
+
import App from "./App";
|
|
1238
|
+
|
|
1239
|
+
const container = document.getElementById("root");
|
|
1240
|
+
const root = createRoot(container!);
|
|
1241
|
+
|
|
1242
|
+
const RootApp = () => {
|
|
1243
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
1244
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
1245
|
+
|
|
1246
|
+
return (
|
|
1247
|
+
<StyledEngineProvider injectFirst>
|
|
1248
|
+
<ThemeProvider theme={theme}>
|
|
1249
|
+
<EmotionThemeProvider theme={theme}>
|
|
1250
|
+
<CssBaseline />
|
|
1251
|
+
<App />
|
|
1252
|
+
</EmotionThemeProvider>
|
|
1253
|
+
</ThemeProvider>
|
|
1254
|
+
</StyledEngineProvider>
|
|
1255
|
+
);
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
root.render(
|
|
1259
|
+
<React.StrictMode>
|
|
1260
|
+
<RootApp />
|
|
1261
|
+
</React.StrictMode>
|
|
1262
|
+
);
|
|
1263
|
+
|
|
1264
|
+
```
|
|
1265
|
+
|
|
1266
|
+
**/public/index.html**
|
|
1267
|
+
|
|
1268
|
+
```html
|
|
1269
|
+
<!DOCTYPE html>
|
|
1270
|
+
<html lang="en">
|
|
1271
|
+
<head>
|
|
1272
|
+
<meta charset="UTF-8" />
|
|
1273
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1274
|
+
<title>Table</title>
|
|
1275
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
1276
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
1277
|
+
<link
|
|
1278
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
1279
|
+
rel="stylesheet"
|
|
1280
|
+
/>
|
|
1281
|
+
<link
|
|
1282
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
1283
|
+
rel="stylesheet"
|
|
1284
|
+
/>
|
|
1285
|
+
</head>
|
|
1286
|
+
<body>
|
|
1287
|
+
<div id="root"></div>
|
|
1288
|
+
</body>
|
|
1289
|
+
</html>
|
|
1290
|
+
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
**/package.json**
|
|
1294
|
+
|
|
1295
|
+
```json
|
|
1296
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
1297
|
+
```
|
|
1298
|
+
|
|
1299
|
+
---
|
|
1300
|
+
|
|
1301
|
+
---
|
|
1302
|
+
|
|
1303
|
+
## TableRow
|
|
1304
|
+
|
|
1305
|
+
### SDS Source Code
|
|
1306
|
+
|
|
1307
|
+
The component's source code in the SDS codebase can be found here.
|
|
1308
|
+
|
|
1309
|
+
### Props
|
|
1310
|
+
|
|
1311
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
1312
|
+
|
|
1313
|
+
| Name | Type | Default | Description |
|
|
1314
|
+
| --- | --- | --- | --- |
|
|
1315
|
+
| `disabled` | `boolean` | `false` | When `true`, the row is disabled. |
|
|
1316
|
+
| `hover` | `boolean` | `true` | When `true`, the component has a hover style. |
|
|
1317
|
+
| `selected` | `boolean` | `false` | When `true`, component is in the selected state. |
|
|
1318
|
+
| `rowHeight` | `number` | `-` | Sets a maximum height for the table row. |
|
|
1319
|
+
| `shouldShowTooltipOnHover` | `boolean` | `true` | When `true`, a tooltip shows up on component hover. |
|
|
1320
|
+
| `tooltipProps` | `Partial<TooltipProps>` | `{}` | These props are directly passed to the tooltip component. |
|
|
1321
|
+
| `tooltipText` | `string` | `-` | Tooltip text. |
|
|
1322
|
+
| `tooltipSubtitle` | `string` | `-` | Toltip subtitle. |
|
|
1323
|
+
| `useDivider` | `boolean` | `true` | If set to `true`, shows a bottom border under the table row. |
|
|
1324
|
+
|
|
1325
|
+
### Code examples
|
|
1326
|
+
|
|
1327
|
+
#### **Default TableRow**
|
|
1328
|
+
|
|
1329
|
+
This example demonstrates the minimum required props for rendering a `TableRow` component.
|
|
1330
|
+
|
|
1331
|
+
**/styles.css**
|
|
1332
|
+
|
|
1333
|
+
```css
|
|
1334
|
+
body {
|
|
1335
|
+
font-size: 13px;
|
|
1336
|
+
font-weight: 400;
|
|
1337
|
+
letter-spacing: 0.08px;
|
|
1338
|
+
line-height: 20px;
|
|
1339
|
+
text-transform: none;
|
|
1340
|
+
font-family: "Inter", sans-serif;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
h1 {
|
|
1344
|
+
font-size: 1.5rem;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
.app {
|
|
1348
|
+
padding: 20px;
|
|
1349
|
+
}
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
**tsconfig.json**
|
|
1353
|
+
|
|
1354
|
+
```json
|
|
1355
|
+
{
|
|
1356
|
+
"include": [
|
|
1357
|
+
"./**/*"
|
|
1358
|
+
],
|
|
1359
|
+
"compilerOptions": {
|
|
1360
|
+
"strict": true,
|
|
1361
|
+
"esModuleInterop": true,
|
|
1362
|
+
"lib": [ "dom", "es2015" ],
|
|
1363
|
+
"jsx": "react-jsx"
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
```
|
|
1367
|
+
|
|
1368
|
+
**/App.tsx**
|
|
1369
|
+
|
|
1370
|
+
```tsx
|
|
1371
|
+
import { TableRow, CellBasic } from "@czi-sds/components";
|
|
1372
|
+
import "./styles.css";
|
|
1373
|
+
|
|
1374
|
+
function App() {
|
|
1375
|
+
return (
|
|
1376
|
+
<div className="app">
|
|
1377
|
+
<table>
|
|
1378
|
+
<tbody>
|
|
1379
|
+
<TableRow>
|
|
1380
|
+
<CellBasic
|
|
1381
|
+
primaryText="Primary Text"
|
|
1382
|
+
secondaryText="Secondary Text"
|
|
1383
|
+
shouldShowTooltipOnHover={false}
|
|
1384
|
+
/>
|
|
1385
|
+
<CellBasic
|
|
1386
|
+
primaryText="Primary Text"
|
|
1387
|
+
secondaryText="Secondary Text"
|
|
1388
|
+
shouldShowTooltipOnHover={false}
|
|
1389
|
+
/>
|
|
1390
|
+
<CellBasic
|
|
1391
|
+
primaryText="Primary Text"
|
|
1392
|
+
secondaryText="Secondary Text"
|
|
1393
|
+
shouldShowTooltipOnHover={false}
|
|
1394
|
+
/>
|
|
1395
|
+
</TableRow>
|
|
1396
|
+
</tbody>
|
|
1397
|
+
</table>
|
|
1398
|
+
</div>
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
export default App;
|
|
1403
|
+
```
|
|
1404
|
+
|
|
1405
|
+
**/index.tsx**
|
|
1406
|
+
|
|
1407
|
+
```tsx
|
|
1408
|
+
import React from "react";
|
|
1409
|
+
import { createRoot } from "react-dom/client";
|
|
1410
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
1411
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
1412
|
+
import { Theme } from "@czi-sds/components";
|
|
1413
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
1414
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
1415
|
+
|
|
1416
|
+
import App from "./App";
|
|
1417
|
+
|
|
1418
|
+
const container = document.getElementById("root");
|
|
1419
|
+
const root = createRoot(container!);
|
|
1420
|
+
|
|
1421
|
+
const RootApp = () => {
|
|
1422
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
1423
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
1424
|
+
|
|
1425
|
+
return (
|
|
1426
|
+
<StyledEngineProvider injectFirst>
|
|
1427
|
+
<ThemeProvider theme={theme}>
|
|
1428
|
+
<EmotionThemeProvider theme={theme}>
|
|
1429
|
+
<CssBaseline />
|
|
1430
|
+
<App />
|
|
1431
|
+
</EmotionThemeProvider>
|
|
1432
|
+
</ThemeProvider>
|
|
1433
|
+
</StyledEngineProvider>
|
|
1434
|
+
);
|
|
1435
|
+
};
|
|
1436
|
+
|
|
1437
|
+
root.render(
|
|
1438
|
+
<React.StrictMode>
|
|
1439
|
+
<RootApp />
|
|
1440
|
+
</React.StrictMode>
|
|
1441
|
+
);
|
|
1442
|
+
|
|
1443
|
+
```
|
|
1444
|
+
|
|
1445
|
+
**/public/index.html**
|
|
1446
|
+
|
|
1447
|
+
```html
|
|
1448
|
+
<!DOCTYPE html>
|
|
1449
|
+
<html lang="en">
|
|
1450
|
+
<head>
|
|
1451
|
+
<meta charset="UTF-8" />
|
|
1452
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1453
|
+
<title>Table</title>
|
|
1454
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
1455
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
1456
|
+
<link
|
|
1457
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
1458
|
+
rel="stylesheet"
|
|
1459
|
+
/>
|
|
1460
|
+
<link
|
|
1461
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
1462
|
+
rel="stylesheet"
|
|
1463
|
+
/>
|
|
1464
|
+
</head>
|
|
1465
|
+
<body>
|
|
1466
|
+
<div id="root"></div>
|
|
1467
|
+
</body>
|
|
1468
|
+
</html>
|
|
1469
|
+
|
|
1470
|
+
```
|
|
1471
|
+
|
|
1472
|
+
**/package.json**
|
|
1473
|
+
|
|
1474
|
+
```json
|
|
1475
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
1476
|
+
```
|
|
1477
|
+
|
|
1478
|
+
---
|
|
1479
|
+
|
|
1480
|
+
---
|
|
1481
|
+
|
|
1482
|
+
## TableHeader
|
|
1483
|
+
|
|
1484
|
+
### SDS Source Code
|
|
1485
|
+
|
|
1486
|
+
The component's source code in the SDS codebase can be found here.
|
|
1487
|
+
|
|
1488
|
+
### Code examples
|
|
1489
|
+
|
|
1490
|
+
#### **Default TableHeader**
|
|
1491
|
+
|
|
1492
|
+
This example demonstrates the minimum required props for rendering a `TableHeader` component.
|
|
1493
|
+
|
|
1494
|
+
**/styles.css**
|
|
1495
|
+
|
|
1496
|
+
```css
|
|
1497
|
+
body {
|
|
1498
|
+
font-size: 13px;
|
|
1499
|
+
font-weight: 400;
|
|
1500
|
+
letter-spacing: 0.08px;
|
|
1501
|
+
line-height: 20px;
|
|
1502
|
+
text-transform: none;
|
|
1503
|
+
font-family: "Inter", sans-serif;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
h1 {
|
|
1507
|
+
font-size: 1.5rem;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
.app {
|
|
1511
|
+
padding: 20px;
|
|
1512
|
+
}
|
|
1513
|
+
```
|
|
1514
|
+
|
|
1515
|
+
**tsconfig.json**
|
|
1516
|
+
|
|
1517
|
+
```json
|
|
1518
|
+
{
|
|
1519
|
+
"include": [
|
|
1520
|
+
"./**/*"
|
|
1521
|
+
],
|
|
1522
|
+
"compilerOptions": {
|
|
1523
|
+
"strict": true,
|
|
1524
|
+
"esModuleInterop": true,
|
|
1525
|
+
"lib": [ "dom", "es2015" ],
|
|
1526
|
+
"jsx": "react-jsx"
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
```
|
|
1530
|
+
|
|
1531
|
+
**/App.tsx**
|
|
1532
|
+
|
|
1533
|
+
```tsx
|
|
1534
|
+
import { TableRow, CellHeader } from "@czi-sds/components";
|
|
1535
|
+
import "./styles.css";
|
|
1536
|
+
|
|
1537
|
+
function App() {
|
|
1538
|
+
return (
|
|
1539
|
+
<div className="app">
|
|
1540
|
+
<table>
|
|
1541
|
+
<thead>
|
|
1542
|
+
<tr>
|
|
1543
|
+
<CellHeader>
|
|
1544
|
+
Header
|
|
1545
|
+
</CellHeader>
|
|
1546
|
+
<CellHeader>
|
|
1547
|
+
Header
|
|
1548
|
+
</CellHeader>
|
|
1549
|
+
<CellHeader>
|
|
1550
|
+
Header
|
|
1551
|
+
</CellHeader>
|
|
1552
|
+
</tr>
|
|
1553
|
+
</thead>
|
|
1554
|
+
</table>
|
|
1555
|
+
</div>
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
export default App;
|
|
1560
|
+
```
|
|
1561
|
+
|
|
1562
|
+
**/index.tsx**
|
|
1563
|
+
|
|
1564
|
+
```tsx
|
|
1565
|
+
import React from "react";
|
|
1566
|
+
import { createRoot } from "react-dom/client";
|
|
1567
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
1568
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
1569
|
+
import { Theme } from "@czi-sds/components";
|
|
1570
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
1571
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
1572
|
+
|
|
1573
|
+
import App from "./App";
|
|
1574
|
+
|
|
1575
|
+
const container = document.getElementById("root");
|
|
1576
|
+
const root = createRoot(container!);
|
|
1577
|
+
|
|
1578
|
+
const RootApp = () => {
|
|
1579
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
1580
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
1581
|
+
|
|
1582
|
+
return (
|
|
1583
|
+
<StyledEngineProvider injectFirst>
|
|
1584
|
+
<ThemeProvider theme={theme}>
|
|
1585
|
+
<EmotionThemeProvider theme={theme}>
|
|
1586
|
+
<CssBaseline />
|
|
1587
|
+
<App />
|
|
1588
|
+
</EmotionThemeProvider>
|
|
1589
|
+
</ThemeProvider>
|
|
1590
|
+
</StyledEngineProvider>
|
|
1591
|
+
);
|
|
1592
|
+
};
|
|
1593
|
+
|
|
1594
|
+
root.render(
|
|
1595
|
+
<React.StrictMode>
|
|
1596
|
+
<RootApp />
|
|
1597
|
+
</React.StrictMode>
|
|
1598
|
+
);
|
|
1599
|
+
|
|
1600
|
+
```
|
|
1601
|
+
|
|
1602
|
+
**/public/index.html**
|
|
1603
|
+
|
|
1604
|
+
```html
|
|
1605
|
+
<!DOCTYPE html>
|
|
1606
|
+
<html lang="en">
|
|
1607
|
+
<head>
|
|
1608
|
+
<meta charset="UTF-8" />
|
|
1609
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1610
|
+
<title>Table</title>
|
|
1611
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
1612
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
1613
|
+
<link
|
|
1614
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
1615
|
+
rel="stylesheet"
|
|
1616
|
+
/>
|
|
1617
|
+
<link
|
|
1618
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
1619
|
+
rel="stylesheet"
|
|
1620
|
+
/>
|
|
1621
|
+
</head>
|
|
1622
|
+
<body>
|
|
1623
|
+
<div id="root"></div>
|
|
1624
|
+
</body>
|
|
1625
|
+
</html>
|
|
1626
|
+
|
|
1627
|
+
```
|
|
1628
|
+
|
|
1629
|
+
**/package.json**
|
|
1630
|
+
|
|
1631
|
+
```json
|
|
1632
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
1633
|
+
```
|
|
1634
|
+
|
|
1635
|
+
---
|
|
1636
|
+
|
|
1637
|
+
---
|
|
1638
|
+
|
|
1639
|
+
## Pagination
|
|
1640
|
+
|
|
1641
|
+
### SDS Source Code
|
|
1642
|
+
|
|
1643
|
+
The component's source code in the SDS codebase can be found here.
|
|
1644
|
+
|
|
1645
|
+
### Props
|
|
1646
|
+
|
|
1647
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
1648
|
+
|
|
1649
|
+
| Name | Type | Default | Description |
|
|
1650
|
+
| --- | --- | --- | --- |
|
|
1651
|
+
| `onPageChange` | `(page: number) => void;` | `-` | A callback function triggered when the page number is changed. |
|
|
1652
|
+
| `onNextPage` | `() => void;` | `-` | A callback function triggered when the "Next" page button is clicked. |
|
|
1653
|
+
| `onPreviousPage` | `() => void;` | `-` | A callback function triggered when the "Previous" page button is clicked. |
|
|
1654
|
+
| `pageSize` | `number` | `-` | Specifies the number of items to be shown on each page. |
|
|
1655
|
+
| `siblingCount` | `number` | `1` | Specifies the number of siblings for each page number. |
|
|
1656
|
+
| `sdsStyle` | `"rounded"` `| "square"` | `"rounded"` | The style of the component. |
|
|
1657
|
+
| `totalCount` | `number` | `-` | Total number of items to be paginated. |
|
|
1658
|
+
| `truncateDropdown` | `boolean` | `true` | If `true`, a dropdown menu is shown for hidden page numbers. |
|
|
1659
|
+
|
|
1660
|
+
### Code examples
|
|
1661
|
+
|
|
1662
|
+
#### **Default Pagination**
|
|
1663
|
+
|
|
1664
|
+
This example showcases a `Pagination` component with the minimum required props.
|
|
1665
|
+
|
|
1666
|
+
**/styles.css**
|
|
1667
|
+
|
|
1668
|
+
```css
|
|
1669
|
+
body {
|
|
1670
|
+
font-size: 13px;
|
|
1671
|
+
font-weight: 400;
|
|
1672
|
+
letter-spacing: 0.08px;
|
|
1673
|
+
line-height: 20px;
|
|
1674
|
+
text-transform: none;
|
|
1675
|
+
font-family: "Inter", sans-serif;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
h1 {
|
|
1679
|
+
font-size: 1.5rem;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
.app {
|
|
1683
|
+
padding: 20px;
|
|
1684
|
+
}
|
|
1685
|
+
```
|
|
1686
|
+
|
|
1687
|
+
**tsconfig.json**
|
|
1688
|
+
|
|
1689
|
+
```json
|
|
1690
|
+
{
|
|
1691
|
+
"include": [
|
|
1692
|
+
"./**/*"
|
|
1693
|
+
],
|
|
1694
|
+
"compilerOptions": {
|
|
1695
|
+
"strict": true,
|
|
1696
|
+
"esModuleInterop": true,
|
|
1697
|
+
"lib": [ "dom", "es2015" ],
|
|
1698
|
+
"jsx": "react-jsx"
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
```
|
|
1702
|
+
|
|
1703
|
+
**/App.tsx**
|
|
1704
|
+
|
|
1705
|
+
```tsx
|
|
1706
|
+
import { Pagination } from "@czi-sds/components";
|
|
1707
|
+
import { useState } from "react";
|
|
1708
|
+
import "./styles.css";
|
|
1709
|
+
|
|
1710
|
+
function App() {
|
|
1711
|
+
|
|
1712
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
1713
|
+
|
|
1714
|
+
return (
|
|
1715
|
+
<div className="app">
|
|
1716
|
+
<Pagination
|
|
1717
|
+
pageSize={5}
|
|
1718
|
+
onPageChange={(page: number) => {
|
|
1719
|
+
setCurrentPage(page);
|
|
1720
|
+
}}
|
|
1721
|
+
onNextPage={() => setCurrentPage(currentPage + 1)}
|
|
1722
|
+
onPreviousPage={() => setCurrentPage(currentPage - 1)}
|
|
1723
|
+
totalCount={250}
|
|
1724
|
+
siblingCount={2}
|
|
1725
|
+
currentPage={currentPage}
|
|
1726
|
+
truncateDropdown
|
|
1727
|
+
/>
|
|
1728
|
+
</div>
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
export default App;
|
|
1733
|
+
```
|
|
1734
|
+
|
|
1735
|
+
**/index.tsx**
|
|
1736
|
+
|
|
1737
|
+
```tsx
|
|
1738
|
+
import React from "react";
|
|
1739
|
+
import { createRoot } from "react-dom/client";
|
|
1740
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
1741
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
1742
|
+
import { Theme } from "@czi-sds/components";
|
|
1743
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
1744
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
1745
|
+
|
|
1746
|
+
import App from "./App";
|
|
1747
|
+
|
|
1748
|
+
const container = document.getElementById("root");
|
|
1749
|
+
const root = createRoot(container!);
|
|
1750
|
+
|
|
1751
|
+
const RootApp = () => {
|
|
1752
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
1753
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
1754
|
+
|
|
1755
|
+
return (
|
|
1756
|
+
<StyledEngineProvider injectFirst>
|
|
1757
|
+
<ThemeProvider theme={theme}>
|
|
1758
|
+
<EmotionThemeProvider theme={theme}>
|
|
1759
|
+
<CssBaseline />
|
|
1760
|
+
<App />
|
|
1761
|
+
</EmotionThemeProvider>
|
|
1762
|
+
</ThemeProvider>
|
|
1763
|
+
</StyledEngineProvider>
|
|
1764
|
+
);
|
|
1765
|
+
};
|
|
1766
|
+
|
|
1767
|
+
root.render(
|
|
1768
|
+
<React.StrictMode>
|
|
1769
|
+
<RootApp />
|
|
1770
|
+
</React.StrictMode>
|
|
1771
|
+
);
|
|
1772
|
+
|
|
1773
|
+
```
|
|
1774
|
+
|
|
1775
|
+
**/public/index.html**
|
|
1776
|
+
|
|
1777
|
+
```html
|
|
1778
|
+
<!DOCTYPE html>
|
|
1779
|
+
<html lang="en">
|
|
1780
|
+
<head>
|
|
1781
|
+
<meta charset="UTF-8" />
|
|
1782
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1783
|
+
<title>Pagination</title>
|
|
1784
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
1785
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
1786
|
+
<link
|
|
1787
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
1788
|
+
rel="stylesheet"
|
|
1789
|
+
/>
|
|
1790
|
+
<link
|
|
1791
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
1792
|
+
rel="stylesheet"
|
|
1793
|
+
/>
|
|
1794
|
+
</head>
|
|
1795
|
+
<body>
|
|
1796
|
+
<div id="root"></div>
|
|
1797
|
+
</body>
|
|
1798
|
+
</html>
|
|
1799
|
+
|
|
1800
|
+
```
|
|
1801
|
+
|
|
1802
|
+
**/package.json**
|
|
1803
|
+
|
|
1804
|
+
```json
|
|
1805
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
1806
|
+
```
|
|
1807
|
+
|
|
1808
|
+
---
|
|
1809
|
+
|
|
1810
|
+
---
|
|
1811
|
+
|
|
1812
|
+
## Table
|
|
1813
|
+
|
|
1814
|
+
### SDS Source Code
|
|
1815
|
+
|
|
1816
|
+
The component's source code in the SDS codebase can be found here.
|
|
1817
|
+
|
|
1818
|
+
### Code examples
|
|
1819
|
+
|
|
1820
|
+
#### **Default Table**
|
|
1821
|
+
|
|
1822
|
+
This example demonstrates a `Table` that utilizes all SDS Table-related components, providing a comprehensive showcase of their functionalities and features.
|
|
1823
|
+
|
|
1824
|
+
**/styles.css**
|
|
1825
|
+
|
|
1826
|
+
```css
|
|
1827
|
+
body {
|
|
1828
|
+
font-size: 13px;
|
|
1829
|
+
font-weight: 400;
|
|
1830
|
+
letter-spacing: 0.08px;
|
|
1831
|
+
line-height: 20px;
|
|
1832
|
+
text-transform: none;
|
|
1833
|
+
font-family: "Inter", sans-serif;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
h1 {
|
|
1837
|
+
font-size: 1.5rem;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
.app {
|
|
1841
|
+
padding: 20px;
|
|
1842
|
+
}
|
|
1843
|
+
```
|
|
1844
|
+
|
|
1845
|
+
**tsconfig.json**
|
|
1846
|
+
|
|
1847
|
+
```json
|
|
1848
|
+
{
|
|
1849
|
+
"include": [
|
|
1850
|
+
"./**/*"
|
|
1851
|
+
],
|
|
1852
|
+
"compilerOptions": {
|
|
1853
|
+
"strict": true,
|
|
1854
|
+
"esModuleInterop": true,
|
|
1855
|
+
"lib": [ "dom", "es2015" ],
|
|
1856
|
+
"jsx": "react-jsx"
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
```
|
|
1860
|
+
|
|
1861
|
+
**/App.tsx**
|
|
1862
|
+
|
|
1863
|
+
```tsx
|
|
1864
|
+
import {
|
|
1865
|
+
CellBasic,
|
|
1866
|
+
CellComponent,
|
|
1867
|
+
CellHeader,
|
|
1868
|
+
Icon,
|
|
1869
|
+
InputRadio,
|
|
1870
|
+
Tag,
|
|
1871
|
+
Table,
|
|
1872
|
+
TableRow,
|
|
1873
|
+
TableHeader
|
|
1874
|
+
} from "@czi-sds/components";
|
|
1875
|
+
import { styled, RadioGroup, FormControlLabel } from "@mui/material";
|
|
1876
|
+
import "./styles.css";
|
|
1877
|
+
|
|
1878
|
+
function App() {
|
|
1879
|
+
|
|
1880
|
+
const StyledIconCell = styled("div")`
|
|
1881
|
+
align-items: center;
|
|
1882
|
+
display: flex;
|
|
1883
|
+
flex-direction: column;
|
|
1884
|
+
justify-content: center;
|
|
1885
|
+
`;
|
|
1886
|
+
|
|
1887
|
+
return (
|
|
1888
|
+
<div className="app">
|
|
1889
|
+
<Table>
|
|
1890
|
+
<TableHeader>
|
|
1891
|
+
<CellHeader horizontalAlign="center" hideSortIcon>
|
|
1892
|
+
Category
|
|
1893
|
+
</CellHeader>
|
|
1894
|
+
<CellHeader active>Active Header</CellHeader>
|
|
1895
|
+
<CellHeader>
|
|
1896
|
+
A very long table header title to test sort icon positioning
|
|
1897
|
+
</CellHeader>
|
|
1898
|
+
<CellHeader hideSortIcon>Component</CellHeader>
|
|
1899
|
+
<CellHeader horizontalAlign="right" hideSortIcon>
|
|
1900
|
+
Right Aligned and Not sortable
|
|
1901
|
+
</CellHeader>
|
|
1902
|
+
</TableHeader>
|
|
1903
|
+
<tbody>
|
|
1904
|
+
<TableRow>
|
|
1905
|
+
<CellComponent verticalAlign="center" horizontalAlign="center">
|
|
1906
|
+
<StyledIconCell>
|
|
1907
|
+
<Icon sdsSize="xl" sdsIcon="Flask" sdsType="static" />
|
|
1908
|
+
<Tag
|
|
1909
|
+
color="info"
|
|
1910
|
+
label="Chemistry"
|
|
1911
|
+
sdsStyle="rounded"
|
|
1912
|
+
sdsType="secondary"
|
|
1913
|
+
/>
|
|
1914
|
+
</StyledIconCell>
|
|
1915
|
+
</CellComponent>
|
|
1916
|
+
<CellBasic
|
|
1917
|
+
primaryText="Primary Text"
|
|
1918
|
+
secondaryText="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
|
1919
|
+
secondaryTextWrapLineCount={2}
|
|
1920
|
+
shouldTextWrap
|
|
1921
|
+
/>
|
|
1922
|
+
<CellBasic
|
|
1923
|
+
primaryText="Primary Text"
|
|
1924
|
+
secondaryText="Secondary Text"
|
|
1925
|
+
tertiaryText="Tertiary Text"
|
|
1926
|
+
shouldShowTooltipOnHover={false}
|
|
1927
|
+
/>
|
|
1928
|
+
<CellComponent>
|
|
1929
|
+
{["info", "notice", "beta", "negative", "positive"].map((item) => (
|
|
1930
|
+
<Tag
|
|
1931
|
+
key={item}
|
|
1932
|
+
color={item as SdsTagColorType}
|
|
1933
|
+
label={item as string}
|
|
1934
|
+
sdsStyle="rounded"
|
|
1935
|
+
sdsType="secondary"
|
|
1936
|
+
/>
|
|
1937
|
+
))}
|
|
1938
|
+
</CellComponent>
|
|
1939
|
+
<CellBasic
|
|
1940
|
+
primaryText="356"
|
|
1941
|
+
horizontalAlign="right"
|
|
1942
|
+
shouldShowTooltipOnHover={false}
|
|
1943
|
+
/>
|
|
1944
|
+
</TableRow>
|
|
1945
|
+
<TableRow>
|
|
1946
|
+
<CellComponent verticalAlign="center" horizontalAlign="center">
|
|
1947
|
+
<StyledIconCell>
|
|
1948
|
+
<Icon sdsSize="xl" sdsIcon="Download" sdsType="static" />
|
|
1949
|
+
<Tag
|
|
1950
|
+
color="info"
|
|
1951
|
+
label="Downloadable Content"
|
|
1952
|
+
sdsStyle="rounded"
|
|
1953
|
+
sdsType="secondary"
|
|
1954
|
+
/>
|
|
1955
|
+
</StyledIconCell>
|
|
1956
|
+
</CellComponent>
|
|
1957
|
+
<CellBasic
|
|
1958
|
+
primaryText="Primary Text"
|
|
1959
|
+
secondaryText="Secondary Text"
|
|
1960
|
+
tertiaryText="Tertiary Text"
|
|
1961
|
+
shouldShowTooltipOnHover={false}
|
|
1962
|
+
/>
|
|
1963
|
+
<CellBasic
|
|
1964
|
+
primaryText="Primary Text"
|
|
1965
|
+
secondaryText="Secondary Text"
|
|
1966
|
+
shouldShowTooltipOnHover={false}
|
|
1967
|
+
/>
|
|
1968
|
+
<CellComponent>
|
|
1969
|
+
<RadioGroup
|
|
1970
|
+
aria-labelledby="demo-radio-buttons-group-label"
|
|
1971
|
+
name="radio-buttons-group"
|
|
1972
|
+
defaultValue="1"
|
|
1973
|
+
>
|
|
1974
|
+
<InputRadio label="Option I" caption="Caption I" value="1" />
|
|
1975
|
+
<InputRadio label="Option II" caption="Caption II" value="2" />
|
|
1976
|
+
</RadioGroup>
|
|
1977
|
+
</CellComponent>
|
|
1978
|
+
<CellBasic
|
|
1979
|
+
primaryText="1,234"
|
|
1980
|
+
secondaryText="2,344,000"
|
|
1981
|
+
tertiaryText="12.5%"
|
|
1982
|
+
horizontalAlign="right"
|
|
1983
|
+
shouldShowTooltipOnHover={false}
|
|
1984
|
+
/>
|
|
1985
|
+
</TableRow>
|
|
1986
|
+
<TableRow
|
|
1987
|
+
disabled
|
|
1988
|
+
shouldShowTooltipOnHover
|
|
1989
|
+
tooltipText="This row is DISABLED!"
|
|
1990
|
+
tooltipSubtitle="Tooltip subtitle"
|
|
1991
|
+
>
|
|
1992
|
+
<CellComponent verticalAlign="center" horizontalAlign="center">
|
|
1993
|
+
<StyledIconCell>
|
|
1994
|
+
<Icon sdsSize="xl" sdsIcon="Bacteria" sdsType="static" />
|
|
1995
|
+
<Tag
|
|
1996
|
+
color="negative"
|
|
1997
|
+
label="Disease"
|
|
1998
|
+
sdsStyle="rounded"
|
|
1999
|
+
sdsType="secondary"
|
|
2000
|
+
/>
|
|
2001
|
+
</StyledIconCell>
|
|
2002
|
+
</CellComponent>
|
|
2003
|
+
<CellBasic
|
|
2004
|
+
primaryText="Primary Text"
|
|
2005
|
+
secondaryText="Secondary Text"
|
|
2006
|
+
tertiaryText="Tertiary Text"
|
|
2007
|
+
shouldShowTooltipOnHover={false}
|
|
2008
|
+
verticalAlign="center"
|
|
2009
|
+
/>
|
|
2010
|
+
<CellBasic
|
|
2011
|
+
primaryText="Primary Text"
|
|
2012
|
+
shouldShowTooltipOnHover={false}
|
|
2013
|
+
verticalAlign="center"
|
|
2014
|
+
/>
|
|
2015
|
+
<CellComponent verticalAlign="center">
|
|
2016
|
+
<Icon sdsSize="l" sdsIcon="Virus" sdsType="static" />
|
|
2017
|
+
</CellComponent>
|
|
2018
|
+
<CellBasic
|
|
2019
|
+
primaryText="0.4"
|
|
2020
|
+
horizontalAlign="right"
|
|
2021
|
+
verticalAlign="center"
|
|
2022
|
+
shouldShowTooltipOnHover={false}
|
|
2023
|
+
/>
|
|
2024
|
+
</TableRow>
|
|
2025
|
+
</tbody>
|
|
2026
|
+
</Table>
|
|
2027
|
+
</div>
|
|
2028
|
+
);
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
export default App;
|
|
2032
|
+
```
|
|
2033
|
+
|
|
2034
|
+
**/index.tsx**
|
|
2035
|
+
|
|
2036
|
+
```tsx
|
|
2037
|
+
import React from "react";
|
|
2038
|
+
import { createRoot } from "react-dom/client";
|
|
2039
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
2040
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
2041
|
+
import { Theme } from "@czi-sds/components";
|
|
2042
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
2043
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
2044
|
+
|
|
2045
|
+
import App from "./App";
|
|
2046
|
+
|
|
2047
|
+
const container = document.getElementById("root");
|
|
2048
|
+
const root = createRoot(container!);
|
|
2049
|
+
|
|
2050
|
+
const RootApp = () => {
|
|
2051
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
2052
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
2053
|
+
|
|
2054
|
+
return (
|
|
2055
|
+
<StyledEngineProvider injectFirst>
|
|
2056
|
+
<ThemeProvider theme={theme}>
|
|
2057
|
+
<EmotionThemeProvider theme={theme}>
|
|
2058
|
+
<CssBaseline />
|
|
2059
|
+
<App />
|
|
2060
|
+
</EmotionThemeProvider>
|
|
2061
|
+
</ThemeProvider>
|
|
2062
|
+
</StyledEngineProvider>
|
|
2063
|
+
);
|
|
2064
|
+
};
|
|
2065
|
+
|
|
2066
|
+
root.render(
|
|
2067
|
+
<React.StrictMode>
|
|
2068
|
+
<RootApp />
|
|
2069
|
+
</React.StrictMode>
|
|
2070
|
+
);
|
|
2071
|
+
|
|
2072
|
+
```
|
|
2073
|
+
|
|
2074
|
+
**/public/index.html**
|
|
2075
|
+
|
|
2076
|
+
```html
|
|
2077
|
+
<!DOCTYPE html>
|
|
2078
|
+
<html lang="en">
|
|
2079
|
+
<head>
|
|
2080
|
+
<meta charset="UTF-8" />
|
|
2081
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
2082
|
+
<title>Table</title>
|
|
2083
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
2084
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
2085
|
+
<link
|
|
2086
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
2087
|
+
rel="stylesheet"
|
|
2088
|
+
/>
|
|
2089
|
+
<link
|
|
2090
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
2091
|
+
rel="stylesheet"
|
|
2092
|
+
/>
|
|
2093
|
+
</head>
|
|
2094
|
+
<body>
|
|
2095
|
+
<div id="root"></div>
|
|
2096
|
+
</body>
|
|
2097
|
+
</html>
|
|
2098
|
+
|
|
2099
|
+
```
|
|
2100
|
+
|
|
2101
|
+
**/package.json**
|
|
2102
|
+
|
|
2103
|
+
```json
|
|
2104
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
2105
|
+
```
|
|
2106
|
+
|
|
2107
|
+
---
|
|
2108
|
+
|
|
2109
|
+
## Component Recipes
|
|
2110
|
+
|
|
2111
|
+
## Table Recipes
|
|
2112
|
+
|
|
2113
|
+
| These component recipes show a few of the more complex approaches available for customizing Tables. | | **Jump to recipe:** Text-only Table Table with a custom sorting function Table with custom Tooltips Table with virtualized rows Editable Table Cells with Access to Internal Data Table with Front-End Pagination Table with Back-End Pagination |
|
|
2114
|
+
| --- | --- | --- |
|
|
2115
|
+
|
|
2116
|
+
#### Text-only Table
|
|
2117
|
+
|
|
2118
|
+
This shows a table that is made up entirely of `BasicCells`.
|
|
2119
|
+
|
|
2120
|
+
**/styles.css**
|
|
2121
|
+
|
|
2122
|
+
```css
|
|
2123
|
+
body {
|
|
2124
|
+
font-size: 13px;
|
|
2125
|
+
font-weight: 400;
|
|
2126
|
+
letter-spacing: 0.08px;
|
|
2127
|
+
line-height: 20px;
|
|
2128
|
+
text-transform: none;
|
|
2129
|
+
font-family: "Inter", sans-serif;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
h1 {
|
|
2133
|
+
font-size: 1.5rem;
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
.app {
|
|
2137
|
+
padding: 20px;
|
|
2138
|
+
width: 90%;
|
|
2139
|
+
margin: auto;
|
|
2140
|
+
}
|
|
2141
|
+
```
|
|
2142
|
+
|
|
2143
|
+
**tsconfig.json**
|
|
2144
|
+
|
|
2145
|
+
```json
|
|
2146
|
+
{
|
|
2147
|
+
"include": [
|
|
2148
|
+
"./**/*"
|
|
2149
|
+
],
|
|
2150
|
+
"compilerOptions": {
|
|
2151
|
+
"strict": true,
|
|
2152
|
+
"esModuleInterop": true,
|
|
2153
|
+
"lib": [ "dom", "es2015" ],
|
|
2154
|
+
"jsx": "react-jsx"
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
```
|
|
2158
|
+
|
|
2159
|
+
**/App.tsx**
|
|
2160
|
+
|
|
2161
|
+
```tsx
|
|
2162
|
+
import * as React from "react";
|
|
2163
|
+
import {
|
|
2164
|
+
CellBasic,
|
|
2165
|
+
CellHeader,
|
|
2166
|
+
TableHeader,
|
|
2167
|
+
TableRow,
|
|
2168
|
+
Table,
|
|
2169
|
+
} from "@czi-sds/components";
|
|
2170
|
+
import "./styles.css";
|
|
2171
|
+
|
|
2172
|
+
import {
|
|
2173
|
+
CellContext,
|
|
2174
|
+
HeaderContext,
|
|
2175
|
+
createColumnHelper,
|
|
2176
|
+
flexRender,
|
|
2177
|
+
getCoreRowModel,
|
|
2178
|
+
useReactTable,
|
|
2179
|
+
} from "@tanstack/react-table";
|
|
2180
|
+
|
|
2181
|
+
type Person = {
|
|
2182
|
+
firstName: string;
|
|
2183
|
+
lastName: string;
|
|
2184
|
+
age: number;
|
|
2185
|
+
birthdate: string;
|
|
2186
|
+
visits: number;
|
|
2187
|
+
status: string;
|
|
2188
|
+
progress: number;
|
|
2189
|
+
};
|
|
2190
|
+
|
|
2191
|
+
const defaultData: Person[] = [
|
|
2192
|
+
{
|
|
2193
|
+
firstName: "Tanner",
|
|
2194
|
+
lastName: "Linsley",
|
|
2195
|
+
age: 24,
|
|
2196
|
+
birthdate: "26 October 1997",
|
|
2197
|
+
visits: 100,
|
|
2198
|
+
status: "In Relationship",
|
|
2199
|
+
progress: 50,
|
|
2200
|
+
},
|
|
2201
|
+
{
|
|
2202
|
+
firstName: "Tandy",
|
|
2203
|
+
lastName: "Miller",
|
|
2204
|
+
age: 40,
|
|
2205
|
+
birthdate: "14 July 1982",
|
|
2206
|
+
visits: 40,
|
|
2207
|
+
status: "Single",
|
|
2208
|
+
progress: 80,
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
firstName: "Joe",
|
|
2212
|
+
lastName: "Dirte",
|
|
2213
|
+
age: 45,
|
|
2214
|
+
birthdate: "25 February 1977",
|
|
2215
|
+
visits: 20,
|
|
2216
|
+
status: "Complicated",
|
|
2217
|
+
progress: 10,
|
|
2218
|
+
},
|
|
2219
|
+
];
|
|
2220
|
+
|
|
2221
|
+
function createHeaderCell(
|
|
2222
|
+
header: HeaderContext<Person, unknown>,
|
|
2223
|
+
name: string
|
|
2224
|
+
) {
|
|
2225
|
+
return (
|
|
2226
|
+
<CellHeader
|
|
2227
|
+
hideSortIcon
|
|
2228
|
+
horizontalAlign={header.header.id === "progress" ? "right" : "left"}
|
|
2229
|
+
>
|
|
2230
|
+
{header.header.isPlaceholder ? "" : name}
|
|
2231
|
+
</CellHeader>
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
function createBodyCell(cell: CellContext<Person, unknown>) {
|
|
2236
|
+
return (
|
|
2237
|
+
<CellBasic
|
|
2238
|
+
primaryText={cell.getValue() as string}
|
|
2239
|
+
shouldShowTooltipOnHover={false}
|
|
2240
|
+
/>
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
const columnHelper = createColumnHelper<Person>();
|
|
2245
|
+
|
|
2246
|
+
const columns = [
|
|
2247
|
+
columnHelper.accessor("firstName", {
|
|
2248
|
+
cell: (cell) => createBodyCell(cell),
|
|
2249
|
+
header: (header) => createHeaderCell(header, "First Name"),
|
|
2250
|
+
}),
|
|
2251
|
+
columnHelper.accessor((row) => row.lastName, {
|
|
2252
|
+
id: "lastName",
|
|
2253
|
+
cell: (cell) => createBodyCell(cell),
|
|
2254
|
+
header: (header) => createHeaderCell(header, "Last Name"),
|
|
2255
|
+
}),
|
|
2256
|
+
columnHelper.accessor("age", {
|
|
2257
|
+
cell: (cell) => (
|
|
2258
|
+
<CellBasic
|
|
2259
|
+
key={cell.cell.id}
|
|
2260
|
+
primaryText={cell.getValue().toString()}
|
|
2261
|
+
secondaryText={cell.row.original.birthdate}
|
|
2262
|
+
shouldShowTooltipOnHover={false}
|
|
2263
|
+
/>
|
|
2264
|
+
),
|
|
2265
|
+
header: (header) => createHeaderCell(header, "Age"),
|
|
2266
|
+
}),
|
|
2267
|
+
columnHelper.accessor("visits", {
|
|
2268
|
+
cell: (cell) => createBodyCell(cell),
|
|
2269
|
+
header: (header) => createHeaderCell(header, "Visits"),
|
|
2270
|
+
}),
|
|
2271
|
+
columnHelper.accessor("status", {
|
|
2272
|
+
cell: (cell) => createBodyCell(cell),
|
|
2273
|
+
header: (header) => createHeaderCell(header, "Status"),
|
|
2274
|
+
}),
|
|
2275
|
+
columnHelper.accessor("progress", {
|
|
2276
|
+
cell: (cell) => (
|
|
2277
|
+
<CellBasic
|
|
2278
|
+
key={cell.cell.id}
|
|
2279
|
+
primaryText={cell.getValue().toString()}
|
|
2280
|
+
secondaryText={`${cell.getValue()} ± 5%`}
|
|
2281
|
+
shouldShowTooltipOnHover={false}
|
|
2282
|
+
horizontalAlign="right"
|
|
2283
|
+
/>
|
|
2284
|
+
),
|
|
2285
|
+
header: (header) => createHeaderCell(header, "Profile Progress"),
|
|
2286
|
+
}),
|
|
2287
|
+
];
|
|
2288
|
+
|
|
2289
|
+
function App() {
|
|
2290
|
+
const [data] = React.useState(() => [...defaultData]);
|
|
2291
|
+
|
|
2292
|
+
const table = useReactTable({
|
|
2293
|
+
data,
|
|
2294
|
+
columns,
|
|
2295
|
+
getCoreRowModel: getCoreRowModel(),
|
|
2296
|
+
});
|
|
2297
|
+
|
|
2298
|
+
return (
|
|
2299
|
+
<div className="app">
|
|
2300
|
+
<Table>
|
|
2301
|
+
<TableHeader>
|
|
2302
|
+
{table.getFlatHeaders().map((header) => (
|
|
2303
|
+
<React.Fragment key={header.id}>
|
|
2304
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
2305
|
+
</React.Fragment>
|
|
2306
|
+
))}
|
|
2307
|
+
</TableHeader>
|
|
2308
|
+
<tbody>
|
|
2309
|
+
{table.getRowModel().rows.map((row) => (
|
|
2310
|
+
<TableRow key={row.id}>
|
|
2311
|
+
{row.getVisibleCells().map((cell) => (
|
|
2312
|
+
<React.Fragment key={cell.id}>
|
|
2313
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
2314
|
+
</React.Fragment>
|
|
2315
|
+
))}
|
|
2316
|
+
</TableRow>
|
|
2317
|
+
))}
|
|
2318
|
+
</tbody>
|
|
2319
|
+
</Table>
|
|
2320
|
+
</div>
|
|
2321
|
+
);
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
export default App;
|
|
2325
|
+
|
|
2326
|
+
```
|
|
2327
|
+
|
|
2328
|
+
**/index.tsx**
|
|
2329
|
+
|
|
2330
|
+
```tsx
|
|
2331
|
+
import React from "react";
|
|
2332
|
+
import { createRoot } from "react-dom/client";
|
|
2333
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
2334
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
2335
|
+
import { Theme } from "@czi-sds/components";
|
|
2336
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
2337
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
2338
|
+
|
|
2339
|
+
import App from "./App";
|
|
2340
|
+
|
|
2341
|
+
const container = document.getElementById("root");
|
|
2342
|
+
const root = createRoot(container!);
|
|
2343
|
+
|
|
2344
|
+
const RootApp = () => {
|
|
2345
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
2346
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
2347
|
+
|
|
2348
|
+
return (
|
|
2349
|
+
<StyledEngineProvider injectFirst>
|
|
2350
|
+
<ThemeProvider theme={theme}>
|
|
2351
|
+
<EmotionThemeProvider theme={theme}>
|
|
2352
|
+
<CssBaseline />
|
|
2353
|
+
<App />
|
|
2354
|
+
</EmotionThemeProvider>
|
|
2355
|
+
</ThemeProvider>
|
|
2356
|
+
</StyledEngineProvider>
|
|
2357
|
+
);
|
|
2358
|
+
};
|
|
2359
|
+
|
|
2360
|
+
root.render(
|
|
2361
|
+
<React.StrictMode>
|
|
2362
|
+
<RootApp />
|
|
2363
|
+
</React.StrictMode>
|
|
2364
|
+
);
|
|
2365
|
+
|
|
2366
|
+
```
|
|
2367
|
+
|
|
2368
|
+
**/public/index.html**
|
|
2369
|
+
|
|
2370
|
+
```html
|
|
2371
|
+
<!DOCTYPE html>
|
|
2372
|
+
<html lang="en">
|
|
2373
|
+
<head>
|
|
2374
|
+
<meta charset="UTF-8" />
|
|
2375
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
2376
|
+
<title>Table</title>
|
|
2377
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
2378
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
2379
|
+
<link
|
|
2380
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
2381
|
+
rel="stylesheet"
|
|
2382
|
+
/>
|
|
2383
|
+
<link
|
|
2384
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
2385
|
+
rel="stylesheet"
|
|
2386
|
+
/>
|
|
2387
|
+
</head>
|
|
2388
|
+
<body>
|
|
2389
|
+
<div id="root"></div>
|
|
2390
|
+
</body>
|
|
2391
|
+
</html>
|
|
2392
|
+
|
|
2393
|
+
```
|
|
2394
|
+
|
|
2395
|
+
**/package.json**
|
|
2396
|
+
|
|
2397
|
+
```json
|
|
2398
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
2399
|
+
```
|
|
2400
|
+
|
|
2401
|
+
---
|
|
2402
|
+
|
|
2403
|
+
#### Table with a custom sorting function
|
|
2404
|
+
|
|
2405
|
+
In this example, a `Table` is presented with various columns, each employing default sorting mechanisms defined by react-table. However, the `LastName` column stands out as it utilizes a custom sorting function named `sortByLength`, which is specifically defined in the code. This customization allows for distinct sorting behavior in the "LastName" column compared to the rest of the `Table`.
|
|
2406
|
+
|
|
2407
|
+
**/styles.css**
|
|
2408
|
+
|
|
2409
|
+
```css
|
|
2410
|
+
body {
|
|
2411
|
+
font-size: 13px;
|
|
2412
|
+
font-weight: 400;
|
|
2413
|
+
letter-spacing: 0.08px;
|
|
2414
|
+
line-height: 20px;
|
|
2415
|
+
text-transform: none;
|
|
2416
|
+
font-family: "Inter", sans-serif;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
h1 {
|
|
2420
|
+
font-size: 1.5rem;
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
.app {
|
|
2424
|
+
padding: 20px;
|
|
2425
|
+
width: 90%;
|
|
2426
|
+
margin: auto;
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
.actions-list {
|
|
2430
|
+
display: flex;
|
|
2431
|
+
justify-content: end;
|
|
2432
|
+
list-style: none;
|
|
2433
|
+
|
|
2434
|
+
& li {
|
|
2435
|
+
margin-right: 8px;
|
|
2436
|
+
|
|
2437
|
+
& .MuiButtonBase-root {
|
|
2438
|
+
outline: 0;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
&:last-child {
|
|
2442
|
+
margin-right: 0;
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
```
|
|
2448
|
+
|
|
2449
|
+
**tsconfig.json**
|
|
2450
|
+
|
|
2451
|
+
```json
|
|
2452
|
+
{
|
|
2453
|
+
"include": [
|
|
2454
|
+
"./**/*"
|
|
2455
|
+
],
|
|
2456
|
+
"compilerOptions": {
|
|
2457
|
+
"strict": true,
|
|
2458
|
+
"esModuleInterop": true,
|
|
2459
|
+
"lib": [ "dom", "es2015" ],
|
|
2460
|
+
"jsx": "react-jsx"
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
```
|
|
2464
|
+
|
|
2465
|
+
**/App.tsx**
|
|
2466
|
+
|
|
2467
|
+
```tsx
|
|
2468
|
+
import React from "react";
|
|
2469
|
+
import {
|
|
2470
|
+
CellBasic,
|
|
2471
|
+
CellHeader,
|
|
2472
|
+
TableHeader,
|
|
2473
|
+
TableRow,
|
|
2474
|
+
Table,
|
|
2475
|
+
CellHeaderDirection,
|
|
2476
|
+
Icon,
|
|
2477
|
+
CellComponent,
|
|
2478
|
+
ButtonIcon,
|
|
2479
|
+
Button,
|
|
2480
|
+
Tag,
|
|
2481
|
+
} from "@czi-sds/components";
|
|
2482
|
+
import {
|
|
2483
|
+
CellContext,
|
|
2484
|
+
ColumnDef,
|
|
2485
|
+
flexRender,
|
|
2486
|
+
getCoreRowModel,
|
|
2487
|
+
getSortedRowModel,
|
|
2488
|
+
HeaderContext,
|
|
2489
|
+
Row,
|
|
2490
|
+
SortingFn,
|
|
2491
|
+
SortingState,
|
|
2492
|
+
useReactTable,
|
|
2493
|
+
} from "@tanstack/react-table";
|
|
2494
|
+
import { faker } from "@faker-js/faker";
|
|
2495
|
+
import "./styles.css";
|
|
2496
|
+
|
|
2497
|
+
type Person = {
|
|
2498
|
+
firstName: string;
|
|
2499
|
+
lastName: string;
|
|
2500
|
+
age: number;
|
|
2501
|
+
visits: number;
|
|
2502
|
+
status: "relationship" | "complicated" | "single";
|
|
2503
|
+
createdAt: Date;
|
|
2504
|
+
subRows?: Person[];
|
|
2505
|
+
};
|
|
2506
|
+
|
|
2507
|
+
const newPerson = (): Person => {
|
|
2508
|
+
return {
|
|
2509
|
+
firstName: faker.person.firstName(),
|
|
2510
|
+
lastName: faker.person.lastName(),
|
|
2511
|
+
age: faker.number.int(96),
|
|
2512
|
+
visits: faker.number.int(1000),
|
|
2513
|
+
createdAt: faker.date.between({
|
|
2514
|
+
from: new Date("2001-01-01"),
|
|
2515
|
+
to: new Date(),
|
|
2516
|
+
}),
|
|
2517
|
+
status: faker.helpers.shuffle<Person["status"]>([
|
|
2518
|
+
"relationship",
|
|
2519
|
+
"complicated",
|
|
2520
|
+
"single",
|
|
2521
|
+
])[0]!,
|
|
2522
|
+
};
|
|
2523
|
+
};
|
|
2524
|
+
|
|
2525
|
+
function makeData(...lens: number[]) {
|
|
2526
|
+
const makeDataLevel = (depth = 0): Person[] => {
|
|
2527
|
+
const len = lens[depth]!;
|
|
2528
|
+
return [...Array(len)].map((): Person => {
|
|
2529
|
+
return {
|
|
2530
|
+
...newPerson(),
|
|
2531
|
+
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
|
|
2532
|
+
};
|
|
2533
|
+
});
|
|
2534
|
+
};
|
|
2535
|
+
|
|
2536
|
+
return makeDataLevel();
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
declare module "@tanstack/table-core" {
|
|
2540
|
+
interface SortingFns {
|
|
2541
|
+
sortByLength: SortingFn<unknown>;
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
function createHeaderCell(
|
|
2546
|
+
header: HeaderContext<Person, unknown>,
|
|
2547
|
+
name: string
|
|
2548
|
+
) {
|
|
2549
|
+
return (
|
|
2550
|
+
<CellHeader
|
|
2551
|
+
colSpan={header.header.colSpan}
|
|
2552
|
+
direction={
|
|
2553
|
+
header.column.getIsSorted()
|
|
2554
|
+
? (header.column.getIsSorted() as CellHeaderDirection)
|
|
2555
|
+
: undefined
|
|
2556
|
+
}
|
|
2557
|
+
active={!!header.column.getIsSorted()}
|
|
2558
|
+
onClick={header.column.getToggleSortingHandler()}
|
|
2559
|
+
hideSortIcon={header.header.id === "actions" ? true : false}
|
|
2560
|
+
horizontalAlign={header.header.id === "actions" ? "right" : "left"}
|
|
2561
|
+
>
|
|
2562
|
+
{name}
|
|
2563
|
+
</CellHeader>
|
|
2564
|
+
);
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
function createBodyCell(cell: CellContext<Person, unknown>) {
|
|
2568
|
+
return (
|
|
2569
|
+
<CellBasic
|
|
2570
|
+
primaryText={cell.getValue() as string}
|
|
2571
|
+
horizontalAlign={cell.column.id === "progress" ? "center" : "left"}
|
|
2572
|
+
verticalAlign="center"
|
|
2573
|
+
shouldShowTooltipOnHover={false}
|
|
2574
|
+
/>
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
function App() {
|
|
2579
|
+
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
2580
|
+
const data = React.useState(() => makeData(20))[0];
|
|
2581
|
+
|
|
2582
|
+
const columns = React.useMemo<ColumnDef<Person>[]>(
|
|
2583
|
+
() => [
|
|
2584
|
+
{
|
|
2585
|
+
accessorKey: "firstName",
|
|
2586
|
+
id: "firstName",
|
|
2587
|
+
cell: (cell) => (
|
|
2588
|
+
<CellBasic
|
|
2589
|
+
primaryText={cell.getValue() as string}
|
|
2590
|
+
icon={<Icon sdsSize="s" sdsIcon="person" sdsType="static" />}
|
|
2591
|
+
verticalAlign="center"
|
|
2592
|
+
iconVerticalAlign="center"
|
|
2593
|
+
shouldShowTooltipOnHover={false}
|
|
2594
|
+
/>
|
|
2595
|
+
),
|
|
2596
|
+
header: (header) => createHeaderCell(header, "First Name"),
|
|
2597
|
+
},
|
|
2598
|
+
{
|
|
2599
|
+
accessorFn: (row) => row.lastName,
|
|
2600
|
+
id: "lastName",
|
|
2601
|
+
cell: (cell) => createBodyCell(cell),
|
|
2602
|
+
header: (header) => (
|
|
2603
|
+
<CellHeader
|
|
2604
|
+
colSpan={header.header.colSpan}
|
|
2605
|
+
direction={
|
|
2606
|
+
header.column.getIsSorted()
|
|
2607
|
+
? (header.column.getIsSorted() as CellHeaderDirection)
|
|
2608
|
+
: undefined
|
|
2609
|
+
}
|
|
2610
|
+
active={!!header.column.getIsSorted()}
|
|
2611
|
+
onClick={header.column.getToggleSortingHandler()}
|
|
2612
|
+
shouldShowTooltipOnHover
|
|
2613
|
+
tooltipText="This column uses a custom sorting function."
|
|
2614
|
+
tooltipSubtitle="Sorts items based on their length."
|
|
2615
|
+
>
|
|
2616
|
+
Last Name
|
|
2617
|
+
</CellHeader>
|
|
2618
|
+
),
|
|
2619
|
+
sortingFn: "sortByLength",
|
|
2620
|
+
},
|
|
2621
|
+
{
|
|
2622
|
+
accessorKey: "age",
|
|
2623
|
+
cell: (cell) => createBodyCell(cell),
|
|
2624
|
+
header: (header) => createHeaderCell(header, "Age"),
|
|
2625
|
+
},
|
|
2626
|
+
{
|
|
2627
|
+
accessorKey: "status",
|
|
2628
|
+
id: "status",
|
|
2629
|
+
cell: (cell) => (
|
|
2630
|
+
<CellComponent verticalAlign="center">
|
|
2631
|
+
<Tag
|
|
2632
|
+
label={cell.cell.getValue() as string}
|
|
2633
|
+
color={
|
|
2634
|
+
cell.cell.getValue() === "single"
|
|
2635
|
+
? "notice"
|
|
2636
|
+
: cell.cell.getValue() === "relationship"
|
|
2637
|
+
? "positive"
|
|
2638
|
+
: "negative"
|
|
2639
|
+
}
|
|
2640
|
+
sdsStyle="rounded"
|
|
2641
|
+
sdsType="secondary"
|
|
2642
|
+
hover={false}
|
|
2643
|
+
/>
|
|
2644
|
+
</CellComponent>
|
|
2645
|
+
),
|
|
2646
|
+
header: (header) => createHeaderCell(header, "Status"),
|
|
2647
|
+
},
|
|
2648
|
+
{
|
|
2649
|
+
accessorKey: "createdAt",
|
|
2650
|
+
cell: (cell) => (
|
|
2651
|
+
<CellBasic
|
|
2652
|
+
primaryText={(cell.getValue() as Date).getFullYear().toString()}
|
|
2653
|
+
secondaryText={(cell.getValue() as Date).toLocaleString("en-us", {
|
|
2654
|
+
month: "long",
|
|
2655
|
+
day: "numeric",
|
|
2656
|
+
})}
|
|
2657
|
+
tooltipProps={{
|
|
2658
|
+
title: (cell.getValue() as Date).toLocaleString("en-us"),
|
|
2659
|
+
sdsStyle: "dark",
|
|
2660
|
+
leaveDelay: 0,
|
|
2661
|
+
leaveTouchDelay: 0,
|
|
2662
|
+
}}
|
|
2663
|
+
verticalAlign="center"
|
|
2664
|
+
horizontalAlign="left"
|
|
2665
|
+
/>
|
|
2666
|
+
),
|
|
2667
|
+
header: (header) => createHeaderCell(header, "Created At"),
|
|
2668
|
+
},
|
|
2669
|
+
{
|
|
2670
|
+
id: "actions",
|
|
2671
|
+
accessorKey: "actions",
|
|
2672
|
+
cell: () => (
|
|
2673
|
+
<CellComponent horizontalAlign="right" verticalAlign="center">
|
|
2674
|
+
<ul className="actions-list">
|
|
2675
|
+
<li>
|
|
2676
|
+
<Button
|
|
2677
|
+
sdsStyle="icon"
|
|
2678
|
+
icon="TreeHorizontal"
|
|
2679
|
+
sdsSize="small"
|
|
2680
|
+
sdsType="secondary"
|
|
2681
|
+
/>
|
|
2682
|
+
</li>
|
|
2683
|
+
<li>
|
|
2684
|
+
<Button
|
|
2685
|
+
sdsStyle="icon"
|
|
2686
|
+
icon="BarChartVertical3"
|
|
2687
|
+
sdsSize="small"
|
|
2688
|
+
sdsType="secondary"
|
|
2689
|
+
/>
|
|
2690
|
+
</li>
|
|
2691
|
+
<li>
|
|
2692
|
+
<Button
|
|
2693
|
+
sdsStyle="icon"
|
|
2694
|
+
icon="Download"
|
|
2695
|
+
sdsSize="small"
|
|
2696
|
+
on
|
|
2697
|
+
sdsType="secondary"
|
|
2698
|
+
/>
|
|
2699
|
+
</li>
|
|
2700
|
+
<li>
|
|
2701
|
+
<Button
|
|
2702
|
+
sdsStyle="icon"
|
|
2703
|
+
icon="DotsHorizontal"
|
|
2704
|
+
sdsSize="small"
|
|
2705
|
+
sdsType="secondary"
|
|
2706
|
+
/>
|
|
2707
|
+
</li>
|
|
2708
|
+
</ul>
|
|
2709
|
+
</CellComponent>
|
|
2710
|
+
),
|
|
2711
|
+
header: (header) => createHeaderCell(header, "Actions"),
|
|
2712
|
+
enableSorting: false,
|
|
2713
|
+
},
|
|
2714
|
+
],
|
|
2715
|
+
[]
|
|
2716
|
+
);
|
|
2717
|
+
|
|
2718
|
+
const table = useReactTable({
|
|
2719
|
+
data,
|
|
2720
|
+
columns,
|
|
2721
|
+
state: {
|
|
2722
|
+
sorting,
|
|
2723
|
+
},
|
|
2724
|
+
onSortingChange: setSorting,
|
|
2725
|
+
getCoreRowModel: getCoreRowModel(),
|
|
2726
|
+
getSortedRowModel: getSortedRowModel(),
|
|
2727
|
+
debugTable: true,
|
|
2728
|
+
sortingFns: {
|
|
2729
|
+
sortByLength: (
|
|
2730
|
+
rowA: Row<Person>,
|
|
2731
|
+
rowB: Row<Person>,
|
|
2732
|
+
columnId: string
|
|
2733
|
+
): number => {
|
|
2734
|
+
const a: string = rowA.getValue(columnId);
|
|
2735
|
+
const b: string = rowB.getValue(columnId);
|
|
2736
|
+
|
|
2737
|
+
if (a.length > b.length) return 1;
|
|
2738
|
+
if (a.length < b.length) return -1;
|
|
2739
|
+
return 0;
|
|
2740
|
+
},
|
|
2741
|
+
},
|
|
2742
|
+
});
|
|
2743
|
+
return (
|
|
2744
|
+
<div className="app">
|
|
2745
|
+
<Table>
|
|
2746
|
+
<TableHeader>
|
|
2747
|
+
{table.getFlatHeaders().map((header) => (
|
|
2748
|
+
<React.Fragment key={header.id}>
|
|
2749
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
2750
|
+
</React.Fragment>
|
|
2751
|
+
))}
|
|
2752
|
+
</TableHeader>
|
|
2753
|
+
<tbody>
|
|
2754
|
+
{table
|
|
2755
|
+
.getRowModel()
|
|
2756
|
+
.rows.slice(0, 10)
|
|
2757
|
+
.map((row) => {
|
|
2758
|
+
return (
|
|
2759
|
+
<TableRow key={row.id}>
|
|
2760
|
+
{row.getVisibleCells().map((cell) => (
|
|
2761
|
+
<React.Fragment key={cell.id}>
|
|
2762
|
+
{flexRender(
|
|
2763
|
+
cell.column.columnDef.cell,
|
|
2764
|
+
cell.getContext()
|
|
2765
|
+
)}
|
|
2766
|
+
</React.Fragment>
|
|
2767
|
+
))}
|
|
2768
|
+
</TableRow>
|
|
2769
|
+
);
|
|
2770
|
+
})}
|
|
2771
|
+
</tbody>
|
|
2772
|
+
</Table>
|
|
2773
|
+
</div>
|
|
2774
|
+
);
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
export default App;
|
|
2778
|
+
|
|
2779
|
+
```
|
|
2780
|
+
|
|
2781
|
+
**/index.tsx**
|
|
2782
|
+
|
|
2783
|
+
```tsx
|
|
2784
|
+
import React from "react";
|
|
2785
|
+
import { createRoot } from "react-dom/client";
|
|
2786
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
2787
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
2788
|
+
import { Theme } from "@czi-sds/components";
|
|
2789
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
2790
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
2791
|
+
|
|
2792
|
+
import App from "./App";
|
|
2793
|
+
|
|
2794
|
+
const container = document.getElementById("root");
|
|
2795
|
+
const root = createRoot(container!);
|
|
2796
|
+
|
|
2797
|
+
const RootApp = () => {
|
|
2798
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
2799
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
2800
|
+
|
|
2801
|
+
return (
|
|
2802
|
+
<StyledEngineProvider injectFirst>
|
|
2803
|
+
<ThemeProvider theme={theme}>
|
|
2804
|
+
<EmotionThemeProvider theme={theme}>
|
|
2805
|
+
<CssBaseline />
|
|
2806
|
+
<App />
|
|
2807
|
+
</EmotionThemeProvider>
|
|
2808
|
+
</ThemeProvider>
|
|
2809
|
+
</StyledEngineProvider>
|
|
2810
|
+
);
|
|
2811
|
+
};
|
|
2812
|
+
|
|
2813
|
+
root.render(
|
|
2814
|
+
<React.StrictMode>
|
|
2815
|
+
<RootApp />
|
|
2816
|
+
</React.StrictMode>
|
|
2817
|
+
);
|
|
2818
|
+
|
|
2819
|
+
```
|
|
2820
|
+
|
|
2821
|
+
**/public/index.html**
|
|
2822
|
+
|
|
2823
|
+
```html
|
|
2824
|
+
<!DOCTYPE html>
|
|
2825
|
+
<html lang="en">
|
|
2826
|
+
<head>
|
|
2827
|
+
<meta charset="UTF-8" />
|
|
2828
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
2829
|
+
<title>Table</title>
|
|
2830
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
2831
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
2832
|
+
<link
|
|
2833
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
2834
|
+
rel="stylesheet"
|
|
2835
|
+
/>
|
|
2836
|
+
<link
|
|
2837
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
2838
|
+
rel="stylesheet"
|
|
2839
|
+
/>
|
|
2840
|
+
</head>
|
|
2841
|
+
<body>
|
|
2842
|
+
<div id="root"></div>
|
|
2843
|
+
</body>
|
|
2844
|
+
</html>
|
|
2845
|
+
|
|
2846
|
+
```
|
|
2847
|
+
|
|
2848
|
+
**/package.json**
|
|
2849
|
+
|
|
2850
|
+
```json
|
|
2851
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
2852
|
+
```
|
|
2853
|
+
|
|
2854
|
+
---
|
|
2855
|
+
|
|
2856
|
+
#### Table with custom Tooltips
|
|
2857
|
+
|
|
2858
|
+
This shows a `Table` with custom `Tooltips`.
|
|
2859
|
+
|
|
2860
|
+
**/styles.css**
|
|
2861
|
+
|
|
2862
|
+
```css
|
|
2863
|
+
body {
|
|
2864
|
+
font-size: 13px;
|
|
2865
|
+
font-weight: 400;
|
|
2866
|
+
letter-spacing: 0.08px;
|
|
2867
|
+
line-height: 20px;
|
|
2868
|
+
text-transform: none;
|
|
2869
|
+
font-family: "Inter", sans-serif;
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
h1 {
|
|
2873
|
+
font-size: 1.5rem;
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
.app {
|
|
2877
|
+
padding: 16px;
|
|
2878
|
+
}
|
|
2879
|
+
```
|
|
2880
|
+
|
|
2881
|
+
**tsconfig.json**
|
|
2882
|
+
|
|
2883
|
+
```json
|
|
2884
|
+
{
|
|
2885
|
+
"include": [
|
|
2886
|
+
"./**/*"
|
|
2887
|
+
],
|
|
2888
|
+
"compilerOptions": {
|
|
2889
|
+
"strict": true,
|
|
2890
|
+
"esModuleInterop": true,
|
|
2891
|
+
"lib": [ "dom", "es2015" ],
|
|
2892
|
+
"jsx": "react-jsx"
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
```
|
|
2896
|
+
|
|
2897
|
+
**/App.tsx**
|
|
2898
|
+
|
|
2899
|
+
```tsx
|
|
2900
|
+
import * as React from "react";
|
|
2901
|
+
import {
|
|
2902
|
+
CellBasic,
|
|
2903
|
+
CellHeader,
|
|
2904
|
+
TableHeader,
|
|
2905
|
+
TableRow,
|
|
2906
|
+
Table,
|
|
2907
|
+
CellComponent,
|
|
2908
|
+
Tag,
|
|
2909
|
+
} from "@czi-sds/components";
|
|
2910
|
+
import {
|
|
2911
|
+
CellContext,
|
|
2912
|
+
HeaderContext,
|
|
2913
|
+
createColumnHelper,
|
|
2914
|
+
flexRender,
|
|
2915
|
+
getCoreRowModel,
|
|
2916
|
+
useReactTable,
|
|
2917
|
+
} from "@tanstack/react-table";
|
|
2918
|
+
import "./styles.css";
|
|
2919
|
+
|
|
2920
|
+
type Person = {
|
|
2921
|
+
firstName: string;
|
|
2922
|
+
lastName: string;
|
|
2923
|
+
age: number;
|
|
2924
|
+
birthdate: string;
|
|
2925
|
+
visits: number;
|
|
2926
|
+
status: string;
|
|
2927
|
+
progress: number;
|
|
2928
|
+
};
|
|
2929
|
+
|
|
2930
|
+
const defaultData: Person[] = [
|
|
2931
|
+
{
|
|
2932
|
+
firstName: "Tanner",
|
|
2933
|
+
lastName: "Linsley",
|
|
2934
|
+
age: 24,
|
|
2935
|
+
birthdate: "26 October 1997",
|
|
2936
|
+
visits: 100,
|
|
2937
|
+
status: "In Relationship",
|
|
2938
|
+
progress: 50,
|
|
2939
|
+
},
|
|
2940
|
+
{
|
|
2941
|
+
firstName: "Tandy",
|
|
2942
|
+
lastName: "Miller",
|
|
2943
|
+
age: 40,
|
|
2944
|
+
birthdate: "14 July 1982",
|
|
2945
|
+
visits: 40,
|
|
2946
|
+
status: "Single",
|
|
2947
|
+
progress: 80,
|
|
2948
|
+
},
|
|
2949
|
+
{
|
|
2950
|
+
firstName: "Joe",
|
|
2951
|
+
lastName: "Dirte",
|
|
2952
|
+
age: 45,
|
|
2953
|
+
birthdate: "25 February 1977",
|
|
2954
|
+
visits: 20,
|
|
2955
|
+
status: "Complicated",
|
|
2956
|
+
progress: 10,
|
|
2957
|
+
},
|
|
2958
|
+
];
|
|
2959
|
+
|
|
2960
|
+
function createHeaderCell(
|
|
2961
|
+
header: HeaderContext<Person, unknown>,
|
|
2962
|
+
name: string
|
|
2963
|
+
) {
|
|
2964
|
+
return (
|
|
2965
|
+
<CellHeader
|
|
2966
|
+
hideSortIcon
|
|
2967
|
+
horizontalAlign={header.header.id === "progress" ? "right" : "left"}
|
|
2968
|
+
>
|
|
2969
|
+
{header.header.isPlaceholder ? "" : name}
|
|
2970
|
+
</CellHeader>
|
|
2971
|
+
);
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
function createBodyCell(info: CellContext<Person, unknown>) {
|
|
2975
|
+
return (
|
|
2976
|
+
<CellBasic
|
|
2977
|
+
primaryText={info.getValue() as string}
|
|
2978
|
+
shouldShowTooltipOnHover={false}
|
|
2979
|
+
/>
|
|
2980
|
+
);
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
const columnHelper = createColumnHelper<Person>();
|
|
2984
|
+
|
|
2985
|
+
const columns = [
|
|
2986
|
+
columnHelper.accessor("firstName", {
|
|
2987
|
+
cell: (info) => createBodyCell(info),
|
|
2988
|
+
header: (header) => createHeaderCell(header, "First Name"),
|
|
2989
|
+
}),
|
|
2990
|
+
columnHelper.accessor("lastName", {
|
|
2991
|
+
cell: (info) => createBodyCell(info),
|
|
2992
|
+
header: (header) => createHeaderCell(header, "Last Name"),
|
|
2993
|
+
}),
|
|
2994
|
+
columnHelper.accessor("age", {
|
|
2995
|
+
cell: (info) => (
|
|
2996
|
+
<CellBasic
|
|
2997
|
+
key={info.cell.id}
|
|
2998
|
+
primaryText={info.cell.getValue().toString()}
|
|
2999
|
+
secondaryText={info.cell.row.original.birthdate}
|
|
3000
|
+
tooltipProps={{
|
|
3001
|
+
sdsStyle: "dark",
|
|
3002
|
+
leaveTouchDelay: 0,
|
|
3003
|
+
leaveDelay: 0,
|
|
3004
|
+
subtitle: "",
|
|
3005
|
+
title: info.cell.row.original.birthdate,
|
|
3006
|
+
}}
|
|
3007
|
+
/>
|
|
3008
|
+
),
|
|
3009
|
+
header: (header) => createHeaderCell(header, "Age"),
|
|
3010
|
+
}),
|
|
3011
|
+
columnHelper.accessor("visits", {
|
|
3012
|
+
cell: (info) => createBodyCell(info),
|
|
3013
|
+
header: (header) => createHeaderCell(header, "Visits"),
|
|
3014
|
+
}),
|
|
3015
|
+
columnHelper.accessor("status", {
|
|
3016
|
+
cell: (info) => (
|
|
3017
|
+
<CellComponent verticalAlign="center" key={info.cell.id}>
|
|
3018
|
+
<Tag
|
|
3019
|
+
label={info.cell.getValue() as string}
|
|
3020
|
+
color={
|
|
3021
|
+
info.cell.getValue() === "Single"
|
|
3022
|
+
? "notice"
|
|
3023
|
+
: info.cell.getValue() === "In Relationship"
|
|
3024
|
+
? "positive"
|
|
3025
|
+
: "negative"
|
|
3026
|
+
}
|
|
3027
|
+
sdsStyle="rounded"
|
|
3028
|
+
sdsType="secondary"
|
|
3029
|
+
hover={false}
|
|
3030
|
+
/>
|
|
3031
|
+
</CellComponent>
|
|
3032
|
+
),
|
|
3033
|
+
header: (header) => createHeaderCell(header, "Status"),
|
|
3034
|
+
}),
|
|
3035
|
+
columnHelper.accessor("progress", {
|
|
3036
|
+
cell: (info) => (
|
|
3037
|
+
<CellBasic
|
|
3038
|
+
key={info.cell.id}
|
|
3039
|
+
primaryText={info.cell.getValue().toString()}
|
|
3040
|
+
secondaryText={`${info.cell.getValue()} ± 5%`}
|
|
3041
|
+
horizontalAlign="right"
|
|
3042
|
+
tooltipProps={{
|
|
3043
|
+
sdsStyle: "dark",
|
|
3044
|
+
leaveTouchDelay: 0,
|
|
3045
|
+
leaveDelay: 0,
|
|
3046
|
+
title: `Profile Progress`,
|
|
3047
|
+
}}
|
|
3048
|
+
/>
|
|
3049
|
+
),
|
|
3050
|
+
header: (header) => createHeaderCell(header, "Profile Progress"),
|
|
3051
|
+
}),
|
|
3052
|
+
];
|
|
3053
|
+
|
|
3054
|
+
function App() {
|
|
3055
|
+
const [data] = React.useState(() => [...defaultData]);
|
|
3056
|
+
|
|
3057
|
+
const table = useReactTable({
|
|
3058
|
+
data,
|
|
3059
|
+
columns,
|
|
3060
|
+
getCoreRowModel: getCoreRowModel(),
|
|
3061
|
+
});
|
|
3062
|
+
|
|
3063
|
+
return (
|
|
3064
|
+
<div className="app">
|
|
3065
|
+
<Table>
|
|
3066
|
+
<TableHeader>
|
|
3067
|
+
{table.getFlatHeaders().map((header) => (
|
|
3068
|
+
<React.Fragment key={header.id}>
|
|
3069
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
3070
|
+
</React.Fragment>
|
|
3071
|
+
))}
|
|
3072
|
+
</TableHeader>
|
|
3073
|
+
<tbody>
|
|
3074
|
+
{table.getRowModel().rows.map((row) => (
|
|
3075
|
+
<TableRow key={row.id}>
|
|
3076
|
+
{row.getVisibleCells().map((cell) => (
|
|
3077
|
+
<React.Fragment key={cell.id}>
|
|
3078
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
3079
|
+
</React.Fragment>
|
|
3080
|
+
))}
|
|
3081
|
+
</TableRow>
|
|
3082
|
+
))}
|
|
3083
|
+
</tbody>
|
|
3084
|
+
</Table>
|
|
3085
|
+
</div>
|
|
3086
|
+
);
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
export default App;
|
|
3090
|
+
|
|
3091
|
+
```
|
|
3092
|
+
|
|
3093
|
+
**/index.tsx**
|
|
3094
|
+
|
|
3095
|
+
```tsx
|
|
3096
|
+
import React from "react";
|
|
3097
|
+
import { createRoot } from "react-dom/client";
|
|
3098
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
3099
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
3100
|
+
import { Theme } from "@czi-sds/components";
|
|
3101
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
3102
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
3103
|
+
|
|
3104
|
+
import App from "./App";
|
|
3105
|
+
|
|
3106
|
+
const container = document.getElementById("root");
|
|
3107
|
+
const root = createRoot(container!);
|
|
3108
|
+
|
|
3109
|
+
const RootApp = () => {
|
|
3110
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
3111
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
3112
|
+
|
|
3113
|
+
return (
|
|
3114
|
+
<StyledEngineProvider injectFirst>
|
|
3115
|
+
<ThemeProvider theme={theme}>
|
|
3116
|
+
<EmotionThemeProvider theme={theme}>
|
|
3117
|
+
<CssBaseline />
|
|
3118
|
+
<App />
|
|
3119
|
+
</EmotionThemeProvider>
|
|
3120
|
+
</ThemeProvider>
|
|
3121
|
+
</StyledEngineProvider>
|
|
3122
|
+
);
|
|
3123
|
+
};
|
|
3124
|
+
|
|
3125
|
+
root.render(
|
|
3126
|
+
<React.StrictMode>
|
|
3127
|
+
<RootApp />
|
|
3128
|
+
</React.StrictMode>
|
|
3129
|
+
);
|
|
3130
|
+
|
|
3131
|
+
```
|
|
3132
|
+
|
|
3133
|
+
**/public/index.html**
|
|
3134
|
+
|
|
3135
|
+
```html
|
|
3136
|
+
<!DOCTYPE html>
|
|
3137
|
+
<html lang="en">
|
|
3138
|
+
<head>
|
|
3139
|
+
<meta charset="UTF-8" />
|
|
3140
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
3141
|
+
<title>Table</title>
|
|
3142
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
3143
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
3144
|
+
<link
|
|
3145
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
3146
|
+
rel="stylesheet"
|
|
3147
|
+
/>
|
|
3148
|
+
<link
|
|
3149
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
3150
|
+
rel="stylesheet"
|
|
3151
|
+
/>
|
|
3152
|
+
</head>
|
|
3153
|
+
<body>
|
|
3154
|
+
<div id="root"></div>
|
|
3155
|
+
</body>
|
|
3156
|
+
</html>
|
|
3157
|
+
|
|
3158
|
+
```
|
|
3159
|
+
|
|
3160
|
+
**/package.json**
|
|
3161
|
+
|
|
3162
|
+
```json
|
|
3163
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
3164
|
+
```
|
|
3165
|
+
|
|
3166
|
+
---
|
|
3167
|
+
|
|
3168
|
+
#### Table with virtualized rows
|
|
3169
|
+
|
|
3170
|
+
This example showcases a `Table` that implements virtualization for its rows, optimizing performance and rendering efficiency when dealing with a large amount of data.
|
|
3171
|
+
|
|
3172
|
+
**/styles.css**
|
|
3173
|
+
|
|
3174
|
+
```css
|
|
3175
|
+
body {
|
|
3176
|
+
font-size: 13px;
|
|
3177
|
+
font-weight: 400;
|
|
3178
|
+
letter-spacing: 0.08px;
|
|
3179
|
+
line-height: 20px;
|
|
3180
|
+
text-transform: none;
|
|
3181
|
+
font-family: "Inter", sans-serif;
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3184
|
+
h1 {
|
|
3185
|
+
font-size: 1.5rem;
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
.app {
|
|
3189
|
+
position: relative;
|
|
3190
|
+
padding: 10px;
|
|
3191
|
+
width: 95%;
|
|
3192
|
+
margin: auto;
|
|
3193
|
+
display: flex;
|
|
3194
|
+
align-items: center;
|
|
3195
|
+
justify-content: center;
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
.container {
|
|
3199
|
+
height: 350px;
|
|
3200
|
+
overflow: auto;
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
thead {
|
|
3204
|
+
background: white;
|
|
3205
|
+
box-shadow: 0 0 10px #ccc;
|
|
3206
|
+
margin: 0;
|
|
3207
|
+
position: sticky;
|
|
3208
|
+
top: 0;
|
|
3209
|
+
z-index: 10;
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3212
|
+
.fetched {
|
|
3213
|
+
font-size: 18px;
|
|
3214
|
+
padding: 25px 35px;
|
|
3215
|
+
position: fixed;
|
|
3216
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
3217
|
+
backdrop-filter: blur(10px);
|
|
3218
|
+
border-radius: 5px;
|
|
3219
|
+
box-shadow: 0 15px 20px rgba(0, 0, 0, 0.3);
|
|
3220
|
+
}
|
|
3221
|
+
```
|
|
3222
|
+
|
|
3223
|
+
**tsconfig.json**
|
|
3224
|
+
|
|
3225
|
+
```json
|
|
3226
|
+
{
|
|
3227
|
+
"include": [
|
|
3228
|
+
"./**/*"
|
|
3229
|
+
],
|
|
3230
|
+
"compilerOptions": {
|
|
3231
|
+
"strict": true,
|
|
3232
|
+
"esModuleInterop": true,
|
|
3233
|
+
"lib": [ "dom", "es2015" ],
|
|
3234
|
+
"jsx": "react-jsx"
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
```
|
|
3238
|
+
|
|
3239
|
+
**/App.tsx**
|
|
3240
|
+
|
|
3241
|
+
```tsx
|
|
3242
|
+
import React from "react";
|
|
3243
|
+
|
|
3244
|
+
import {
|
|
3245
|
+
ColumnDef,
|
|
3246
|
+
flexRender,
|
|
3247
|
+
getCoreRowModel,
|
|
3248
|
+
getSortedRowModel,
|
|
3249
|
+
Row,
|
|
3250
|
+
SortingState,
|
|
3251
|
+
ColumnSort,
|
|
3252
|
+
useReactTable,
|
|
3253
|
+
HeaderContext,
|
|
3254
|
+
CellContext,
|
|
3255
|
+
} from "@tanstack/react-table";
|
|
3256
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
3257
|
+
import { useVirtual } from "react-virtual";
|
|
3258
|
+
import {
|
|
3259
|
+
CellBasic,
|
|
3260
|
+
CellHeader,
|
|
3261
|
+
CellHeaderDirection,
|
|
3262
|
+
Table,
|
|
3263
|
+
TableHeader,
|
|
3264
|
+
TableRow,
|
|
3265
|
+
Tag,
|
|
3266
|
+
CellComponent,
|
|
3267
|
+
} from "@czi-sds/components";
|
|
3268
|
+
import { faker } from "@faker-js/faker";
|
|
3269
|
+
import "./styles.css";
|
|
3270
|
+
|
|
3271
|
+
const fetchSize = 20;
|
|
3272
|
+
|
|
3273
|
+
type Person = {
|
|
3274
|
+
id: number;
|
|
3275
|
+
firstName: string;
|
|
3276
|
+
lastName: string;
|
|
3277
|
+
age: number;
|
|
3278
|
+
status: "relationship" | "complicated" | "single";
|
|
3279
|
+
};
|
|
3280
|
+
|
|
3281
|
+
type PersonApiResponse = {
|
|
3282
|
+
data: Person[];
|
|
3283
|
+
meta: {
|
|
3284
|
+
totalRowCount: number;
|
|
3285
|
+
};
|
|
3286
|
+
};
|
|
3287
|
+
|
|
3288
|
+
const newPerson = (index: number): Person => {
|
|
3289
|
+
return {
|
|
3290
|
+
id: index + 1,
|
|
3291
|
+
firstName: faker.person.firstName(),
|
|
3292
|
+
lastName: faker.person.lastName(),
|
|
3293
|
+
age: faker.number.int(40),
|
|
3294
|
+
status: faker.helpers.shuffle<Person["status"]>([
|
|
3295
|
+
"relationship",
|
|
3296
|
+
"complicated",
|
|
3297
|
+
"single",
|
|
3298
|
+
])[0]!,
|
|
3299
|
+
};
|
|
3300
|
+
};
|
|
3301
|
+
|
|
3302
|
+
function makeData(...lens: number[]) {
|
|
3303
|
+
const makeDataLevel = (depth = 0): Person[] => {
|
|
3304
|
+
const len = lens[depth]!;
|
|
3305
|
+
return [...Array(len)].map((_, d): Person => {
|
|
3306
|
+
return {
|
|
3307
|
+
...newPerson(d),
|
|
3308
|
+
};
|
|
3309
|
+
});
|
|
3310
|
+
};
|
|
3311
|
+
|
|
3312
|
+
return makeDataLevel();
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
const data = makeData(1000);
|
|
3316
|
+
|
|
3317
|
+
//simulates a backend api
|
|
3318
|
+
const fetchData = (start: number, size: number, sorting: SortingState) => {
|
|
3319
|
+
const dbData = [...data];
|
|
3320
|
+
if (sorting.length) {
|
|
3321
|
+
const sort = sorting[0] as ColumnSort;
|
|
3322
|
+
const { id, desc } = sort as { id: keyof Person; desc: boolean };
|
|
3323
|
+
dbData.sort((a, b) => {
|
|
3324
|
+
if (desc) {
|
|
3325
|
+
return a[id] < b[id] ? 1 : -1;
|
|
3326
|
+
}
|
|
3327
|
+
return a[id] > b[id] ? 1 : -1;
|
|
3328
|
+
});
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
return {
|
|
3332
|
+
data: dbData.slice(start, start + size),
|
|
3333
|
+
meta: {
|
|
3334
|
+
totalRowCount: dbData.length,
|
|
3335
|
+
},
|
|
3336
|
+
};
|
|
3337
|
+
};
|
|
3338
|
+
|
|
3339
|
+
function createHeaderCell(cell: HeaderContext<Person, unknown>, name: string) {
|
|
3340
|
+
return (
|
|
3341
|
+
<CellHeader
|
|
3342
|
+
direction={
|
|
3343
|
+
cell.header.column.getIsSorted()
|
|
3344
|
+
? (cell.header.column.getIsSorted() as CellHeaderDirection)
|
|
3345
|
+
: undefined
|
|
3346
|
+
}
|
|
3347
|
+
active={!!cell.header.column.getIsSorted()}
|
|
3348
|
+
onClick={cell.header.column.getToggleSortingHandler()}
|
|
3349
|
+
>
|
|
3350
|
+
{name}
|
|
3351
|
+
</CellHeader>
|
|
3352
|
+
);
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
function createBodyCell(info: CellContext<Person, unknown>) {
|
|
3356
|
+
return (
|
|
3357
|
+
<CellBasic
|
|
3358
|
+
primaryText={info.getValue() as string}
|
|
3359
|
+
shouldShowTooltipOnHover={false}
|
|
3360
|
+
/>
|
|
3361
|
+
);
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
function App() {
|
|
3365
|
+
//we need a reference to the scrolling element for logic down below
|
|
3366
|
+
const tableContainerRef = React.useRef<HTMLDivElement>(null);
|
|
3367
|
+
|
|
3368
|
+
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
3369
|
+
|
|
3370
|
+
const columns = React.useMemo<ColumnDef<Person>[]>(
|
|
3371
|
+
() => [
|
|
3372
|
+
{
|
|
3373
|
+
accessorKey: "id",
|
|
3374
|
+
cell: (info) => createBodyCell(info),
|
|
3375
|
+
header: (info) => createHeaderCell(info, "ID"),
|
|
3376
|
+
},
|
|
3377
|
+
{
|
|
3378
|
+
accessorKey: "firstName",
|
|
3379
|
+
cell: (info) => createBodyCell(info),
|
|
3380
|
+
header: (info) => createHeaderCell(info, "First Name"),
|
|
3381
|
+
},
|
|
3382
|
+
{
|
|
3383
|
+
accessorFn: (row) => row.lastName,
|
|
3384
|
+
id: "lastName",
|
|
3385
|
+
cell: (info) => createBodyCell(info),
|
|
3386
|
+
header: (info) => createHeaderCell(info, "Last Name"),
|
|
3387
|
+
},
|
|
3388
|
+
{
|
|
3389
|
+
accessorKey: "age",
|
|
3390
|
+
cell: (info) => createBodyCell(info),
|
|
3391
|
+
header: (info) => createHeaderCell(info, "Age"),
|
|
3392
|
+
},
|
|
3393
|
+
{
|
|
3394
|
+
accessorKey: "status",
|
|
3395
|
+
header: (info) => createHeaderCell(info, "Status"),
|
|
3396
|
+
cell: (info) => {
|
|
3397
|
+
return (
|
|
3398
|
+
<CellComponent verticalAlign="center" key={info.cell.id}>
|
|
3399
|
+
<Tag
|
|
3400
|
+
label={info.cell.getValue() as string}
|
|
3401
|
+
color={
|
|
3402
|
+
info.cell.getValue() === "single"
|
|
3403
|
+
? "notice"
|
|
3404
|
+
: info.cell.getValue() === "relationship"
|
|
3405
|
+
? "positive"
|
|
3406
|
+
: "negative"
|
|
3407
|
+
}
|
|
3408
|
+
sdsStyle="rounded"
|
|
3409
|
+
sdsType="secondary"
|
|
3410
|
+
hover={false}
|
|
3411
|
+
/>
|
|
3412
|
+
</CellComponent>
|
|
3413
|
+
);
|
|
3414
|
+
},
|
|
3415
|
+
},
|
|
3416
|
+
],
|
|
3417
|
+
[]
|
|
3418
|
+
);
|
|
3419
|
+
|
|
3420
|
+
//react-query has an useInfiniteQuery hook just for this situation!
|
|
3421
|
+
const { data, fetchNextPage, isFetching, isLoading } =
|
|
3422
|
+
useInfiniteQuery<PersonApiResponse>(
|
|
3423
|
+
["table-data", sorting], //adding sorting state as key causes table to reset and fetch from new beginning upon sort
|
|
3424
|
+
async ({ pageParam = 0 }) => {
|
|
3425
|
+
const start = pageParam * fetchSize;
|
|
3426
|
+
const fetchedData = fetchData(start, fetchSize, sorting); //pretend api call
|
|
3427
|
+
return fetchedData;
|
|
3428
|
+
},
|
|
3429
|
+
{
|
|
3430
|
+
getNextPageParam: (_lastGroup, groups) => groups.length,
|
|
3431
|
+
keepPreviousData: true,
|
|
3432
|
+
refetchOnWindowFocus: false,
|
|
3433
|
+
}
|
|
3434
|
+
);
|
|
3435
|
+
|
|
3436
|
+
//we must flatten the array of arrays from the useInfiniteQuery hook
|
|
3437
|
+
const flatData = React.useMemo(
|
|
3438
|
+
() => data?.pages?.flatMap((page) => page.data) ?? [],
|
|
3439
|
+
[data]
|
|
3440
|
+
);
|
|
3441
|
+
const totalDBRowCount = data?.pages?.[0]?.meta?.totalRowCount ?? 0;
|
|
3442
|
+
const totalFetched = flatData.length;
|
|
3443
|
+
|
|
3444
|
+
//called on scroll and possibly on mount to fetch more data as the user scrolls and reaches bottom of table
|
|
3445
|
+
const fetchMoreOnBottomReached = React.useCallback(
|
|
3446
|
+
(containerRefElement?: HTMLDivElement | null) => {
|
|
3447
|
+
if (containerRefElement) {
|
|
3448
|
+
const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
|
|
3449
|
+
|
|
3450
|
+
//once the user has scrolled within 300px of the bottom of the table, fetch more data if there is any
|
|
3451
|
+
if (
|
|
3452
|
+
scrollHeight - scrollTop - clientHeight < 300 &&
|
|
3453
|
+
!isFetching &&
|
|
3454
|
+
totalFetched < totalDBRowCount
|
|
3455
|
+
) {
|
|
3456
|
+
fetchNextPage();
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
},
|
|
3460
|
+
[fetchNextPage, isFetching, totalFetched, totalDBRowCount]
|
|
3461
|
+
);
|
|
3462
|
+
|
|
3463
|
+
//a check on mount and after a fetch to see if the table is already scrolled to the bottom and immediately needs to fetch more data
|
|
3464
|
+
React.useEffect(() => {
|
|
3465
|
+
fetchMoreOnBottomReached(tableContainerRef.current);
|
|
3466
|
+
}, [fetchMoreOnBottomReached]);
|
|
3467
|
+
|
|
3468
|
+
const table = useReactTable({
|
|
3469
|
+
data: flatData,
|
|
3470
|
+
columns,
|
|
3471
|
+
state: {
|
|
3472
|
+
sorting,
|
|
3473
|
+
},
|
|
3474
|
+
onSortingChange: setSorting,
|
|
3475
|
+
getCoreRowModel: getCoreRowModel(),
|
|
3476
|
+
getSortedRowModel: getSortedRowModel(),
|
|
3477
|
+
});
|
|
3478
|
+
|
|
3479
|
+
const { rows } = table.getRowModel();
|
|
3480
|
+
|
|
3481
|
+
//Virtualizing is optional, but might be necessary if we are going to potentially have hundreds or thousands of rows
|
|
3482
|
+
const rowVirtualizer = useVirtual({
|
|
3483
|
+
parentRef: tableContainerRef,
|
|
3484
|
+
size: rows.length,
|
|
3485
|
+
overscan: 10,
|
|
3486
|
+
});
|
|
3487
|
+
const { virtualItems: virtualRows, totalSize } = rowVirtualizer;
|
|
3488
|
+
const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0;
|
|
3489
|
+
const paddingBottom =
|
|
3490
|
+
virtualRows.length > 0
|
|
3491
|
+
? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0)
|
|
3492
|
+
: 0;
|
|
3493
|
+
|
|
3494
|
+
if (isLoading) {
|
|
3495
|
+
return <>Loading...</>;
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
return (
|
|
3499
|
+
<div className="app">
|
|
3500
|
+
<div
|
|
3501
|
+
className="container"
|
|
3502
|
+
onScroll={(e) => fetchMoreOnBottomReached(e.target as HTMLDivElement)}
|
|
3503
|
+
ref={tableContainerRef}
|
|
3504
|
+
>
|
|
3505
|
+
<Table>
|
|
3506
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
3507
|
+
<TableHeader key={headerGroup.id}>
|
|
3508
|
+
{headerGroup.headers.map((header) => (
|
|
3509
|
+
<React.Fragment key={header.id}>
|
|
3510
|
+
{flexRender(
|
|
3511
|
+
header.column.columnDef.header,
|
|
3512
|
+
header.getContext()
|
|
3513
|
+
)}
|
|
3514
|
+
</React.Fragment>
|
|
3515
|
+
))}
|
|
3516
|
+
</TableHeader>
|
|
3517
|
+
))}
|
|
3518
|
+
<tbody>
|
|
3519
|
+
{paddingTop > 0 && (
|
|
3520
|
+
<tr>
|
|
3521
|
+
<td style={{ height: `${paddingTop}px` }} />
|
|
3522
|
+
</tr>
|
|
3523
|
+
)}
|
|
3524
|
+
{virtualRows.map((virtualRow) => {
|
|
3525
|
+
const row = rows[virtualRow.index] as Row<Person>;
|
|
3526
|
+
return (
|
|
3527
|
+
<TableRow key={row.id}>
|
|
3528
|
+
{row.getVisibleCells().map((cell) => (
|
|
3529
|
+
<React.Fragment key={cell.id}>
|
|
3530
|
+
{flexRender(
|
|
3531
|
+
cell.column.columnDef.cell,
|
|
3532
|
+
cell.getContext()
|
|
3533
|
+
)}
|
|
3534
|
+
</React.Fragment>
|
|
3535
|
+
))}
|
|
3536
|
+
</TableRow>
|
|
3537
|
+
);
|
|
3538
|
+
})}
|
|
3539
|
+
{paddingBottom > 0 && (
|
|
3540
|
+
<tr>
|
|
3541
|
+
<td style={{ height: `${paddingBottom}px` }} />
|
|
3542
|
+
</tr>
|
|
3543
|
+
)}
|
|
3544
|
+
</tbody>
|
|
3545
|
+
</Table>
|
|
3546
|
+
</div>
|
|
3547
|
+
<div className="fetched">
|
|
3548
|
+
Fetched {flatData.length} of {totalDBRowCount} Rows.
|
|
3549
|
+
</div>
|
|
3550
|
+
</div>
|
|
3551
|
+
);
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
export default App;
|
|
3555
|
+
|
|
3556
|
+
```
|
|
3557
|
+
|
|
3558
|
+
**/index.tsx**
|
|
3559
|
+
|
|
3560
|
+
```tsx
|
|
3561
|
+
import React from "react";
|
|
3562
|
+
import { createRoot } from "react-dom/client";
|
|
3563
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
3564
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
3565
|
+
import { Theme } from "@czi-sds/components";
|
|
3566
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
3567
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
3568
|
+
|
|
3569
|
+
import App from "./App";
|
|
3570
|
+
|
|
3571
|
+
const container = document.getElementById("root");
|
|
3572
|
+
const root = createRoot(container!);
|
|
3573
|
+
|
|
3574
|
+
const RootApp = () => {
|
|
3575
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
3576
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
3577
|
+
|
|
3578
|
+
return (
|
|
3579
|
+
<StyledEngineProvider injectFirst>
|
|
3580
|
+
<ThemeProvider theme={theme}>
|
|
3581
|
+
<EmotionThemeProvider theme={theme}>
|
|
3582
|
+
<CssBaseline />
|
|
3583
|
+
<App />
|
|
3584
|
+
</EmotionThemeProvider>
|
|
3585
|
+
</ThemeProvider>
|
|
3586
|
+
</StyledEngineProvider>
|
|
3587
|
+
);
|
|
3588
|
+
};
|
|
3589
|
+
|
|
3590
|
+
root.render(
|
|
3591
|
+
<React.StrictMode>
|
|
3592
|
+
<RootApp />
|
|
3593
|
+
</React.StrictMode>
|
|
3594
|
+
);
|
|
3595
|
+
|
|
3596
|
+
```
|
|
3597
|
+
|
|
3598
|
+
**/public/index.html**
|
|
3599
|
+
|
|
3600
|
+
```html
|
|
3601
|
+
<!DOCTYPE html>
|
|
3602
|
+
<html lang="en">
|
|
3603
|
+
<head>
|
|
3604
|
+
<meta charset="UTF-8" />
|
|
3605
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
3606
|
+
<title>Table</title>
|
|
3607
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
3608
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
3609
|
+
<link
|
|
3610
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
3611
|
+
rel="stylesheet"
|
|
3612
|
+
/>
|
|
3613
|
+
<link
|
|
3614
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
3615
|
+
rel="stylesheet"
|
|
3616
|
+
/>
|
|
3617
|
+
</head>
|
|
3618
|
+
<body>
|
|
3619
|
+
<div id="root"></div>
|
|
3620
|
+
</body>
|
|
3621
|
+
</html>
|
|
3622
|
+
|
|
3623
|
+
```
|
|
3624
|
+
|
|
3625
|
+
**/package.json**
|
|
3626
|
+
|
|
3627
|
+
```json
|
|
3628
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
3629
|
+
```
|
|
3630
|
+
|
|
3631
|
+
---
|
|
3632
|
+
|
|
3633
|
+
#### Editable Table Cells with Access to Internal Data
|
|
3634
|
+
|
|
3635
|
+
This example demonstrates how to access the internal data management state in a `Table`. Table cells are **Editable**, click on a cell to start editing (Changes will apply on blur!)
|
|
3636
|
+
|
|
3637
|
+
**/styles.css**
|
|
3638
|
+
|
|
3639
|
+
```css
|
|
3640
|
+
body {
|
|
3641
|
+
font-size: 13px;
|
|
3642
|
+
font-weight: 400;
|
|
3643
|
+
letter-spacing: 0.08px;
|
|
3644
|
+
line-height: 20px;
|
|
3645
|
+
text-transform: none;
|
|
3646
|
+
font-family: "Inter", sans-serif;
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
h1 {
|
|
3650
|
+
font-size: 1.5rem;
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
.app {
|
|
3654
|
+
padding: 20px;
|
|
3655
|
+
width: 90%;
|
|
3656
|
+
margin: auto;
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
.actions {
|
|
3660
|
+
margin: 20px 0;
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
.log {
|
|
3664
|
+
margin: 20px 0;
|
|
3665
|
+
padding: 0 15px;
|
|
3666
|
+
border: solid 1px #eee;
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3669
|
+
.log pre {
|
|
3670
|
+
font-family: "Courier New", Courier, monospace;
|
|
3671
|
+
font-size: 11px;
|
|
3672
|
+
word-break: break-word;
|
|
3673
|
+
}
|
|
3674
|
+
|
|
3675
|
+
.editable-input {
|
|
3676
|
+
background-color: transparent;
|
|
3677
|
+
border: none;
|
|
3678
|
+
outline: none;
|
|
3679
|
+
}
|
|
3680
|
+
|
|
3681
|
+
```
|
|
3682
|
+
|
|
3683
|
+
**tsconfig.json**
|
|
3684
|
+
|
|
3685
|
+
```json
|
|
3686
|
+
{
|
|
3687
|
+
"include": [
|
|
3688
|
+
"./**/*"
|
|
3689
|
+
],
|
|
3690
|
+
"compilerOptions": {
|
|
3691
|
+
"strict": true,
|
|
3692
|
+
"esModuleInterop": true,
|
|
3693
|
+
"lib": [ "dom", "es2015" ],
|
|
3694
|
+
"jsx": "react-jsx"
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
```
|
|
3698
|
+
|
|
3699
|
+
**/App.tsx**
|
|
3700
|
+
|
|
3701
|
+
```tsx
|
|
3702
|
+
import React from "react";
|
|
3703
|
+
import {
|
|
3704
|
+
CellBasic,
|
|
3705
|
+
CellHeader,
|
|
3706
|
+
TableHeader,
|
|
3707
|
+
TableRow,
|
|
3708
|
+
Table,
|
|
3709
|
+
Button,
|
|
3710
|
+
Icon,
|
|
3711
|
+
getSemanticColors,
|
|
3712
|
+
} from "@czi-sds/components";
|
|
3713
|
+
import {
|
|
3714
|
+
CellContext,
|
|
3715
|
+
ColumnDef,
|
|
3716
|
+
flexRender,
|
|
3717
|
+
getCoreRowModel,
|
|
3718
|
+
getPaginationRowModel,
|
|
3719
|
+
RowData,
|
|
3720
|
+
useReactTable,
|
|
3721
|
+
} from "@tanstack/react-table";
|
|
3722
|
+
import { useTheme } from "@mui/material";
|
|
3723
|
+
import { faker } from "@faker-js/faker";
|
|
3724
|
+
import "./styles.css";
|
|
3725
|
+
|
|
3726
|
+
type StatusTypes = "relationship" | "complicated" | "single";
|
|
3727
|
+
|
|
3728
|
+
type Person = {
|
|
3729
|
+
[key: string]: unknown;
|
|
3730
|
+
firstName: string;
|
|
3731
|
+
lastName: string;
|
|
3732
|
+
age: number;
|
|
3733
|
+
visits: number;
|
|
3734
|
+
progress: number;
|
|
3735
|
+
status: StatusTypes;
|
|
3736
|
+
subRows?: Person[];
|
|
3737
|
+
};
|
|
3738
|
+
|
|
3739
|
+
const newPerson = (): Person => {
|
|
3740
|
+
return {
|
|
3741
|
+
firstName: faker.person.firstName(),
|
|
3742
|
+
lastName: faker.person.lastName(),
|
|
3743
|
+
age: faker.number.int(40),
|
|
3744
|
+
visits: faker.number.int(1000),
|
|
3745
|
+
progress: faker.number.int(100),
|
|
3746
|
+
status: faker.helpers.shuffle<Person["status"]>([
|
|
3747
|
+
"relationship",
|
|
3748
|
+
"complicated",
|
|
3749
|
+
"single",
|
|
3750
|
+
])[0]!,
|
|
3751
|
+
};
|
|
3752
|
+
};
|
|
3753
|
+
|
|
3754
|
+
function makeData(...lens: number[]) {
|
|
3755
|
+
const makeDataLevel = (depth = 0): Person[] => {
|
|
3756
|
+
const len = lens[depth]!;
|
|
3757
|
+
return [...Array(len)].map((): Person => {
|
|
3758
|
+
return {
|
|
3759
|
+
...newPerson(),
|
|
3760
|
+
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
|
|
3761
|
+
};
|
|
3762
|
+
});
|
|
3763
|
+
};
|
|
3764
|
+
|
|
3765
|
+
return makeDataLevel();
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3768
|
+
declare module "@tanstack/react-table" {
|
|
3769
|
+
interface TableMeta<TData extends RowData> {
|
|
3770
|
+
updateData: (rowIndex: number, columnId: string, value: TData) => void;
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
const CellEditor = (cell: CellContext<Person, unknown>) => {
|
|
3775
|
+
const theme = useTheme();
|
|
3776
|
+
const semanticColors = getSemanticColors({theme});
|
|
3777
|
+
|
|
3778
|
+
const initialValue = cell.getValue();
|
|
3779
|
+
// We need to keep and update the state of the cell normally
|
|
3780
|
+
const [value, setValue] = React.useState(initialValue);
|
|
3781
|
+
|
|
3782
|
+
// When the input is blurred, we'll call our table meta's updateData function
|
|
3783
|
+
const onBlur = () => {
|
|
3784
|
+
cell.table.options.meta?.updateData(cell.row.index, cell.column.id, value as Person);
|
|
3785
|
+
};
|
|
3786
|
+
|
|
3787
|
+
// If the initialValue is changed external, sync it up with our state
|
|
3788
|
+
React.useEffect(() => {
|
|
3789
|
+
setValue(initialValue);
|
|
3790
|
+
}, [initialValue]);
|
|
3791
|
+
|
|
3792
|
+
return (
|
|
3793
|
+
<input
|
|
3794
|
+
className="editable-input"
|
|
3795
|
+
value={value as string}
|
|
3796
|
+
onChange={(e) => setValue(e.target.value)}
|
|
3797
|
+
onBlur={onBlur}
|
|
3798
|
+
style={{ color: semanticColors?.base?.textPrimary }}
|
|
3799
|
+
/>
|
|
3800
|
+
);
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
// Give our default column cell renderer editing superpowers!
|
|
3804
|
+
const defaultColumn: Partial<ColumnDef<Person>> = {
|
|
3805
|
+
cell: CellEditor,
|
|
3806
|
+
};
|
|
3807
|
+
|
|
3808
|
+
const defaultColumns: ColumnDef<Person>[] = [
|
|
3809
|
+
{
|
|
3810
|
+
accessorKey: "firstName",
|
|
3811
|
+
header: "First Name",
|
|
3812
|
+
},
|
|
3813
|
+
{
|
|
3814
|
+
accessorKey: "lastName",
|
|
3815
|
+
header: "Last Name",
|
|
3816
|
+
},
|
|
3817
|
+
{
|
|
3818
|
+
accessorKey: "age",
|
|
3819
|
+
header: "Age",
|
|
3820
|
+
},
|
|
3821
|
+
{
|
|
3822
|
+
accessorKey: "visits",
|
|
3823
|
+
header: "Visits",
|
|
3824
|
+
},
|
|
3825
|
+
{
|
|
3826
|
+
accessorKey: "status",
|
|
3827
|
+
header: "Status",
|
|
3828
|
+
},
|
|
3829
|
+
{
|
|
3830
|
+
accessorKey: "progress",
|
|
3831
|
+
header: "Profile Progress",
|
|
3832
|
+
},
|
|
3833
|
+
];
|
|
3834
|
+
|
|
3835
|
+
function App() {
|
|
3836
|
+
const [log, setLog] = React.useState("");
|
|
3837
|
+
const [data, setData] = React.useState(() => makeData(3));
|
|
3838
|
+
const [columns] = React.useState<typeof defaultColumns>(() => [
|
|
3839
|
+
...defaultColumns,
|
|
3840
|
+
]);
|
|
3841
|
+
|
|
3842
|
+
// Create the table and pass your options
|
|
3843
|
+
const table = useReactTable({
|
|
3844
|
+
data,
|
|
3845
|
+
columns,
|
|
3846
|
+
defaultColumn,
|
|
3847
|
+
getCoreRowModel: getCoreRowModel(),
|
|
3848
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
3849
|
+
// Provide our updateData function to our table meta
|
|
3850
|
+
meta: {
|
|
3851
|
+
updateData: (rowIndex, columnId, value) => {
|
|
3852
|
+
setData(
|
|
3853
|
+
(
|
|
3854
|
+
old: Person[] // Specify the type of 'old' as Person[]
|
|
3855
|
+
) =>
|
|
3856
|
+
old.map((row, index) => {
|
|
3857
|
+
if (index === rowIndex) {
|
|
3858
|
+
const oldValue = row[columnId]; // No need for type assertion
|
|
3859
|
+
setLog(
|
|
3860
|
+
`Row index: ${rowIndex}\nColumn Id: ${columnId}\nOld value: ${oldValue}\nNew value: ${value}`
|
|
3861
|
+
);
|
|
3862
|
+
return {
|
|
3863
|
+
...row,
|
|
3864
|
+
[columnId]: value,
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3867
|
+
return row;
|
|
3868
|
+
})
|
|
3869
|
+
);
|
|
3870
|
+
},
|
|
3871
|
+
},
|
|
3872
|
+
});
|
|
3873
|
+
|
|
3874
|
+
// Manage your own state
|
|
3875
|
+
const [state, setState] = React.useState(table.initialState);
|
|
3876
|
+
|
|
3877
|
+
// Change table data
|
|
3878
|
+
const changeTableData = () => {
|
|
3879
|
+
setData(() => makeData(5));
|
|
3880
|
+
setLog("");
|
|
3881
|
+
};
|
|
3882
|
+
|
|
3883
|
+
// Override the state managers for the table to your own
|
|
3884
|
+
table.setOptions((prev) => ({
|
|
3885
|
+
...prev,
|
|
3886
|
+
state,
|
|
3887
|
+
onStateChange: setState,
|
|
3888
|
+
// These are just table options, so if things
|
|
3889
|
+
// need to change based on your state, you can
|
|
3890
|
+
// derive them here
|
|
3891
|
+
}));
|
|
3892
|
+
|
|
3893
|
+
return (
|
|
3894
|
+
<div className="app">
|
|
3895
|
+
<Table>
|
|
3896
|
+
<TableHeader>
|
|
3897
|
+
{table.getHeaderGroups().map((headerGroup) =>
|
|
3898
|
+
headerGroup.headers.map((header) => (
|
|
3899
|
+
<CellHeader key={header.id} hideSortIcon>
|
|
3900
|
+
{flexRender(
|
|
3901
|
+
header.column.columnDef.header,
|
|
3902
|
+
header.getContext()
|
|
3903
|
+
)}
|
|
3904
|
+
</CellHeader>
|
|
3905
|
+
))
|
|
3906
|
+
)}
|
|
3907
|
+
</TableHeader>
|
|
3908
|
+
<tbody>
|
|
3909
|
+
{table.getRowModel().rows.map((row) => (
|
|
3910
|
+
<TableRow key={row.id}>
|
|
3911
|
+
{row.getVisibleCells().map((cell) => (
|
|
3912
|
+
<CellBasic
|
|
3913
|
+
key={cell.id}
|
|
3914
|
+
primaryText={
|
|
3915
|
+
flexRender(
|
|
3916
|
+
cell.column.columnDef.cell,
|
|
3917
|
+
cell.getContext()
|
|
3918
|
+
) as string
|
|
3919
|
+
}
|
|
3920
|
+
shouldShowTooltipOnHover={false}
|
|
3921
|
+
/>
|
|
3922
|
+
))}
|
|
3923
|
+
</TableRow>
|
|
3924
|
+
))}
|
|
3925
|
+
</tbody>
|
|
3926
|
+
</Table>
|
|
3927
|
+
|
|
3928
|
+
<div className="actions">
|
|
3929
|
+
<Button
|
|
3930
|
+
startIcon={<Icon sdsIcon="refresh" sdsSize="s" sdsType="button" />}
|
|
3931
|
+
sdsStyle="rounded"
|
|
3932
|
+
sdsType="primary"
|
|
3933
|
+
onClick={changeTableData}
|
|
3934
|
+
>
|
|
3935
|
+
Change table data
|
|
3936
|
+
</Button>
|
|
3937
|
+
</div>
|
|
3938
|
+
|
|
3939
|
+
{log && (
|
|
3940
|
+
<div className="log">
|
|
3941
|
+
<h4>Cell Value Update Log:</h4>
|
|
3942
|
+
<pre>{log}</pre>
|
|
3943
|
+
</div>
|
|
3944
|
+
)}
|
|
3945
|
+
|
|
3946
|
+
{data && (
|
|
3947
|
+
<div className="log">
|
|
3948
|
+
<h4>Table data:</h4>
|
|
3949
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
3950
|
+
</div>
|
|
3951
|
+
)}
|
|
3952
|
+
</div>
|
|
3953
|
+
);
|
|
3954
|
+
}
|
|
3955
|
+
|
|
3956
|
+
export default App;
|
|
3957
|
+
|
|
3958
|
+
```
|
|
3959
|
+
|
|
3960
|
+
**/index.tsx**
|
|
3961
|
+
|
|
3962
|
+
```tsx
|
|
3963
|
+
import React from "react";
|
|
3964
|
+
import { createRoot } from "react-dom/client";
|
|
3965
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
3966
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
3967
|
+
import { Theme } from "@czi-sds/components";
|
|
3968
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
3969
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
3970
|
+
|
|
3971
|
+
import App from "./App";
|
|
3972
|
+
|
|
3973
|
+
const container = document.getElementById("root");
|
|
3974
|
+
const root = createRoot(container!);
|
|
3975
|
+
|
|
3976
|
+
const RootApp = () => {
|
|
3977
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
3978
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
3979
|
+
|
|
3980
|
+
return (
|
|
3981
|
+
<StyledEngineProvider injectFirst>
|
|
3982
|
+
<ThemeProvider theme={theme}>
|
|
3983
|
+
<EmotionThemeProvider theme={theme}>
|
|
3984
|
+
<CssBaseline />
|
|
3985
|
+
<App />
|
|
3986
|
+
</EmotionThemeProvider>
|
|
3987
|
+
</ThemeProvider>
|
|
3988
|
+
</StyledEngineProvider>
|
|
3989
|
+
);
|
|
3990
|
+
};
|
|
3991
|
+
|
|
3992
|
+
root.render(
|
|
3993
|
+
<React.StrictMode>
|
|
3994
|
+
<RootApp />
|
|
3995
|
+
</React.StrictMode>
|
|
3996
|
+
);
|
|
3997
|
+
|
|
3998
|
+
```
|
|
3999
|
+
|
|
4000
|
+
**/public/index.html**
|
|
4001
|
+
|
|
4002
|
+
```html
|
|
4003
|
+
<!DOCTYPE html>
|
|
4004
|
+
<html lang="en">
|
|
4005
|
+
<head>
|
|
4006
|
+
<meta charset="UTF-8" />
|
|
4007
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
4008
|
+
<title>Table</title>
|
|
4009
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
4010
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
4011
|
+
<link
|
|
4012
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
4013
|
+
rel="stylesheet"
|
|
4014
|
+
/>
|
|
4015
|
+
<link
|
|
4016
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
4017
|
+
rel="stylesheet"
|
|
4018
|
+
/>
|
|
4019
|
+
</head>
|
|
4020
|
+
<body>
|
|
4021
|
+
<div id="root"></div>
|
|
4022
|
+
</body>
|
|
4023
|
+
</html>
|
|
4024
|
+
|
|
4025
|
+
```
|
|
4026
|
+
|
|
4027
|
+
**/package.json**
|
|
4028
|
+
|
|
4029
|
+
```json
|
|
4030
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
4031
|
+
```
|
|
4032
|
+
|
|
4033
|
+
---
|
|
4034
|
+
|
|
4035
|
+
#### Table with Front-End Pagination
|
|
4036
|
+
|
|
4037
|
+
This example uses front-end `Pagination` to show table data in multiple pages.
|
|
4038
|
+
|
|
4039
|
+
**/styles.css**
|
|
4040
|
+
|
|
4041
|
+
```css
|
|
4042
|
+
body {
|
|
4043
|
+
font-size: 13px;
|
|
4044
|
+
font-weight: 400;
|
|
4045
|
+
letter-spacing: 0.08px;
|
|
4046
|
+
line-height: 20px;
|
|
4047
|
+
text-transform: none;
|
|
4048
|
+
font-family: "Inter", sans-serif;
|
|
4049
|
+
}
|
|
4050
|
+
|
|
4051
|
+
h1 {
|
|
4052
|
+
font-size: 1.5rem;
|
|
4053
|
+
}
|
|
4054
|
+
|
|
4055
|
+
.app {
|
|
4056
|
+
padding: 16px;
|
|
4057
|
+
}
|
|
4058
|
+
|
|
4059
|
+
button {
|
|
4060
|
+
outline: none !important;
|
|
4061
|
+
}
|
|
4062
|
+
|
|
4063
|
+
#pagination-wrapper {
|
|
4064
|
+
display: flex;
|
|
4065
|
+
flex-direction: row;
|
|
4066
|
+
justify-content: end;
|
|
4067
|
+
padding: 15px 0;
|
|
4068
|
+
}
|
|
4069
|
+
```
|
|
4070
|
+
|
|
4071
|
+
**tsconfig.json**
|
|
4072
|
+
|
|
4073
|
+
```json
|
|
4074
|
+
{
|
|
4075
|
+
"include": [
|
|
4076
|
+
"./**/*"
|
|
4077
|
+
],
|
|
4078
|
+
"compilerOptions": {
|
|
4079
|
+
"strict": true,
|
|
4080
|
+
"esModuleInterop": true,
|
|
4081
|
+
"lib": [ "dom", "es2015" ],
|
|
4082
|
+
"jsx": "react-jsx"
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
```
|
|
4086
|
+
|
|
4087
|
+
**/App.tsx**
|
|
4088
|
+
|
|
4089
|
+
```tsx
|
|
4090
|
+
import React from "react";
|
|
4091
|
+
import {
|
|
4092
|
+
CellBasic,
|
|
4093
|
+
CellHeader,
|
|
4094
|
+
TableHeader,
|
|
4095
|
+
TableRow,
|
|
4096
|
+
Table,
|
|
4097
|
+
CellHeaderDirection,
|
|
4098
|
+
CellComponent,
|
|
4099
|
+
Tag,
|
|
4100
|
+
Pagination,
|
|
4101
|
+
} from "@czi-sds/components";
|
|
4102
|
+
import {
|
|
4103
|
+
ColumnDef,
|
|
4104
|
+
flexRender,
|
|
4105
|
+
getCoreRowModel,
|
|
4106
|
+
getSortedRowModel,
|
|
4107
|
+
getPaginationRowModel,
|
|
4108
|
+
SortingState,
|
|
4109
|
+
useReactTable,
|
|
4110
|
+
PaginationState,
|
|
4111
|
+
HeaderContext,
|
|
4112
|
+
CellContext,
|
|
4113
|
+
} from "@tanstack/react-table";
|
|
4114
|
+
import { faker } from "@faker-js/faker";
|
|
4115
|
+
import "./styles.css";
|
|
4116
|
+
|
|
4117
|
+
type Person = {
|
|
4118
|
+
firstName: string;
|
|
4119
|
+
lastName: string;
|
|
4120
|
+
age: number;
|
|
4121
|
+
visits: number;
|
|
4122
|
+
progress: number;
|
|
4123
|
+
status: "relationship" | "complicated" | "single";
|
|
4124
|
+
subRows?: Person[];
|
|
4125
|
+
};
|
|
4126
|
+
|
|
4127
|
+
const newPerson = (): Person => {
|
|
4128
|
+
return {
|
|
4129
|
+
firstName: faker.person.firstName(),
|
|
4130
|
+
lastName: faker.person.lastName(),
|
|
4131
|
+
age: faker.number.int(96),
|
|
4132
|
+
visits: faker.number.int(1000),
|
|
4133
|
+
progress: faker.number.int(100),
|
|
4134
|
+
status: faker.helpers.shuffle<Person["status"]>([
|
|
4135
|
+
"relationship",
|
|
4136
|
+
"complicated",
|
|
4137
|
+
"single",
|
|
4138
|
+
])[0]!,
|
|
4139
|
+
};
|
|
4140
|
+
};
|
|
4141
|
+
|
|
4142
|
+
function makeData(...lens: number[]) {
|
|
4143
|
+
const makeDataLevel = (depth = 0): Person[] => {
|
|
4144
|
+
const len = lens[depth]!;
|
|
4145
|
+
return [...Array(len)].map((): Person => {
|
|
4146
|
+
return {
|
|
4147
|
+
...newPerson(),
|
|
4148
|
+
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
|
|
4149
|
+
};
|
|
4150
|
+
});
|
|
4151
|
+
};
|
|
4152
|
+
|
|
4153
|
+
return makeDataLevel();
|
|
4154
|
+
}
|
|
4155
|
+
|
|
4156
|
+
function App() {
|
|
4157
|
+
const DATA_COUNT = 99;
|
|
4158
|
+
const PAGE_SIZE = 3;
|
|
4159
|
+
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
4160
|
+
const data = React.useState(() => makeData(DATA_COUNT))[0];
|
|
4161
|
+
|
|
4162
|
+
const [{ pageIndex, pageSize }, setPagination] =
|
|
4163
|
+
React.useState<PaginationState>({
|
|
4164
|
+
pageIndex: 0,
|
|
4165
|
+
pageSize: PAGE_SIZE,
|
|
4166
|
+
});
|
|
4167
|
+
|
|
4168
|
+
function createHeaderCell(
|
|
4169
|
+
header: HeaderContext<Person, unknown>,
|
|
4170
|
+
name: string
|
|
4171
|
+
) {
|
|
4172
|
+
return (
|
|
4173
|
+
<CellHeader
|
|
4174
|
+
key={header.header.id}
|
|
4175
|
+
colSpan={header.header.colSpan}
|
|
4176
|
+
direction={
|
|
4177
|
+
header.column.getIsSorted()
|
|
4178
|
+
? (header.column.getIsSorted() as CellHeaderDirection)
|
|
4179
|
+
: undefined
|
|
4180
|
+
}
|
|
4181
|
+
active={!!header.column.getIsSorted()}
|
|
4182
|
+
onClick={header.column.getToggleSortingHandler()}
|
|
4183
|
+
horizontalAlign={header.header.id === "createdAt" ? "right" : "left"}
|
|
4184
|
+
shouldShowTooltipOnHover={true}
|
|
4185
|
+
tooltipText={name}
|
|
4186
|
+
tooltipProps={{
|
|
4187
|
+
enterDelay: 2000,
|
|
4188
|
+
enterNextDelay: 2000,
|
|
4189
|
+
}}
|
|
4190
|
+
>
|
|
4191
|
+
{name}
|
|
4192
|
+
</CellHeader>
|
|
4193
|
+
);
|
|
4194
|
+
}
|
|
4195
|
+
|
|
4196
|
+
function createBodyCell(cell: CellContext<Person, unknown>) {
|
|
4197
|
+
return (
|
|
4198
|
+
<CellBasic
|
|
4199
|
+
key={cell.cell.id}
|
|
4200
|
+
primaryText={cell.getValue() as string}
|
|
4201
|
+
horizontalAlign={cell.column.id === "progress" ? "center" : "left"}
|
|
4202
|
+
verticalAlign="center"
|
|
4203
|
+
shouldShowTooltipOnHover={true}
|
|
4204
|
+
/>
|
|
4205
|
+
);
|
|
4206
|
+
}
|
|
4207
|
+
|
|
4208
|
+
const columns = React.useMemo<ColumnDef<Person>[]>(
|
|
4209
|
+
() => [
|
|
4210
|
+
{
|
|
4211
|
+
accessorKey: "firstName",
|
|
4212
|
+
cell: (cell) => createBodyCell(cell),
|
|
4213
|
+
header: (header) => createHeaderCell(header, "First Name"),
|
|
4214
|
+
},
|
|
4215
|
+
{
|
|
4216
|
+
accessorKey: "lastName",
|
|
4217
|
+
cell: (cell) => createBodyCell(cell),
|
|
4218
|
+
header: (header) => createHeaderCell(header, "Last Name"),
|
|
4219
|
+
},
|
|
4220
|
+
{
|
|
4221
|
+
accessorKey: "age",
|
|
4222
|
+
cell: (cell) => createBodyCell(cell),
|
|
4223
|
+
header: (header) => createHeaderCell(header, "Age"),
|
|
4224
|
+
},
|
|
4225
|
+
{
|
|
4226
|
+
accessorKey: "visits",
|
|
4227
|
+
cell: (cell) => createBodyCell(cell),
|
|
4228
|
+
header: (header) => createHeaderCell(header, "Visits"),
|
|
4229
|
+
},
|
|
4230
|
+
{
|
|
4231
|
+
accessorKey: "status",
|
|
4232
|
+
cell: (cell) => (
|
|
4233
|
+
<CellComponent key={cell.cell.id} verticalAlign="center">
|
|
4234
|
+
<Tag
|
|
4235
|
+
label={cell.getValue() as string}
|
|
4236
|
+
color={
|
|
4237
|
+
cell.cell.getValue() === "single"
|
|
4238
|
+
? "notice"
|
|
4239
|
+
: cell.cell.getValue() === "relationship"
|
|
4240
|
+
? "positive"
|
|
4241
|
+
: "negative"
|
|
4242
|
+
}
|
|
4243
|
+
sdsStyle="rounded"
|
|
4244
|
+
sdsType="secondary"
|
|
4245
|
+
/>
|
|
4246
|
+
</CellComponent>
|
|
4247
|
+
),
|
|
4248
|
+
header: (header) => createHeaderCell(header, "Status"),
|
|
4249
|
+
},
|
|
4250
|
+
{
|
|
4251
|
+
accessorKey: "progress",
|
|
4252
|
+
cell: (cell) => createBodyCell(cell),
|
|
4253
|
+
header: (header) => createHeaderCell(header, "Profile Progress"),
|
|
4254
|
+
},
|
|
4255
|
+
],
|
|
4256
|
+
[]
|
|
4257
|
+
);
|
|
4258
|
+
|
|
4259
|
+
const pagination = React.useMemo(
|
|
4260
|
+
() => ({
|
|
4261
|
+
pageIndex,
|
|
4262
|
+
pageSize,
|
|
4263
|
+
}),
|
|
4264
|
+
[pageIndex, pageSize]
|
|
4265
|
+
);
|
|
4266
|
+
|
|
4267
|
+
const table = useReactTable({
|
|
4268
|
+
data,
|
|
4269
|
+
columns,
|
|
4270
|
+
state: {
|
|
4271
|
+
sorting,
|
|
4272
|
+
pagination,
|
|
4273
|
+
},
|
|
4274
|
+
onPaginationChange: setPagination,
|
|
4275
|
+
onSortingChange: setSorting,
|
|
4276
|
+
getCoreRowModel: getCoreRowModel(),
|
|
4277
|
+
getSortedRowModel: getSortedRowModel(),
|
|
4278
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
4279
|
+
debugTable: true,
|
|
4280
|
+
});
|
|
4281
|
+
|
|
4282
|
+
return (
|
|
4283
|
+
<div className="app">
|
|
4284
|
+
<Table>
|
|
4285
|
+
<TableHeader>
|
|
4286
|
+
{table.getFlatHeaders().map((header) => (
|
|
4287
|
+
<React.Fragment key={header.id}>
|
|
4288
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
4289
|
+
</React.Fragment>
|
|
4290
|
+
))}
|
|
4291
|
+
</TableHeader>
|
|
4292
|
+
<tbody>
|
|
4293
|
+
{table.getRowModel().rows.map((row) => {
|
|
4294
|
+
return (
|
|
4295
|
+
<TableRow key={row.id}>
|
|
4296
|
+
{row.getVisibleCells().map((cell) => (
|
|
4297
|
+
<React.Fragment key={cell.id}>
|
|
4298
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
4299
|
+
</React.Fragment>
|
|
4300
|
+
))}
|
|
4301
|
+
</TableRow>
|
|
4302
|
+
);
|
|
4303
|
+
})}
|
|
4304
|
+
</tbody>
|
|
4305
|
+
</Table>
|
|
4306
|
+
<div id="pagination-wrapper">
|
|
4307
|
+
<Pagination
|
|
4308
|
+
sdsStyle="round"
|
|
4309
|
+
pageSize={table.getState().pagination.pageSize}
|
|
4310
|
+
onPageChange={(page: number) => {
|
|
4311
|
+
table.setPageIndex(page - 1);
|
|
4312
|
+
}}
|
|
4313
|
+
onNextPage={() => table.nextPage()}
|
|
4314
|
+
onPreviousPage={() => table.previousPage()}
|
|
4315
|
+
totalCount={DATA_COUNT}
|
|
4316
|
+
siblingCount={1}
|
|
4317
|
+
currentPage={table.getState().pagination.pageIndex + 1}
|
|
4318
|
+
truncateDropdown
|
|
4319
|
+
/>
|
|
4320
|
+
</div>
|
|
4321
|
+
</div>
|
|
4322
|
+
);
|
|
4323
|
+
}
|
|
4324
|
+
|
|
4325
|
+
export default App;
|
|
4326
|
+
|
|
4327
|
+
```
|
|
4328
|
+
|
|
4329
|
+
**/index.tsx**
|
|
4330
|
+
|
|
4331
|
+
```tsx
|
|
4332
|
+
import React from "react";
|
|
4333
|
+
import { createRoot } from "react-dom/client";
|
|
4334
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
4335
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
4336
|
+
import { Theme } from "@czi-sds/components";
|
|
4337
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
4338
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
4339
|
+
|
|
4340
|
+
import App from "./App";
|
|
4341
|
+
|
|
4342
|
+
const container = document.getElementById("root");
|
|
4343
|
+
const root = createRoot(container!);
|
|
4344
|
+
|
|
4345
|
+
const RootApp = () => {
|
|
4346
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
4347
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
4348
|
+
|
|
4349
|
+
return (
|
|
4350
|
+
<StyledEngineProvider injectFirst>
|
|
4351
|
+
<ThemeProvider theme={theme}>
|
|
4352
|
+
<EmotionThemeProvider theme={theme}>
|
|
4353
|
+
<CssBaseline />
|
|
4354
|
+
<App />
|
|
4355
|
+
</EmotionThemeProvider>
|
|
4356
|
+
</ThemeProvider>
|
|
4357
|
+
</StyledEngineProvider>
|
|
4358
|
+
);
|
|
4359
|
+
};
|
|
4360
|
+
|
|
4361
|
+
root.render(
|
|
4362
|
+
<React.StrictMode>
|
|
4363
|
+
<RootApp />
|
|
4364
|
+
</React.StrictMode>
|
|
4365
|
+
);
|
|
4366
|
+
|
|
4367
|
+
```
|
|
4368
|
+
|
|
4369
|
+
**/public/index.html**
|
|
4370
|
+
|
|
4371
|
+
```html
|
|
4372
|
+
<!DOCTYPE html>
|
|
4373
|
+
<html lang="en">
|
|
4374
|
+
<head>
|
|
4375
|
+
<meta charset="UTF-8" />
|
|
4376
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
4377
|
+
<title>Table</title>
|
|
4378
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
4379
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
4380
|
+
<link
|
|
4381
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
4382
|
+
rel="stylesheet"
|
|
4383
|
+
/>
|
|
4384
|
+
<link
|
|
4385
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
4386
|
+
rel="stylesheet"
|
|
4387
|
+
/>
|
|
4388
|
+
</head>
|
|
4389
|
+
<body>
|
|
4390
|
+
<div id="root"></div>
|
|
4391
|
+
</body>
|
|
4392
|
+
</html>
|
|
4393
|
+
|
|
4394
|
+
```
|
|
4395
|
+
|
|
4396
|
+
**/package.json**
|
|
4397
|
+
|
|
4398
|
+
```json
|
|
4399
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
4400
|
+
```
|
|
4401
|
+
|
|
4402
|
+
---
|
|
4403
|
+
|
|
4404
|
+
#### Table with Back-End Pagination
|
|
4405
|
+
|
|
4406
|
+
This example illustrates a `Table` that implements back-end pagination by utilizing mocked data retrieved through a query function built with the `react-query` package.
|
|
4407
|
+
|
|
4408
|
+
**/styles.css**
|
|
4409
|
+
|
|
4410
|
+
```css
|
|
4411
|
+
body {
|
|
4412
|
+
font-size: 13px;
|
|
4413
|
+
font-weight: 400;
|
|
4414
|
+
letter-spacing: 0.08px;
|
|
4415
|
+
line-height: 20px;
|
|
4416
|
+
text-transform: none;
|
|
4417
|
+
font-family: "Inter", sans-serif;
|
|
4418
|
+
}
|
|
4419
|
+
|
|
4420
|
+
h1 {
|
|
4421
|
+
font-size: 1.5rem;
|
|
4422
|
+
}
|
|
4423
|
+
|
|
4424
|
+
.app {
|
|
4425
|
+
padding: 16px;
|
|
4426
|
+
}
|
|
4427
|
+
|
|
4428
|
+
button {
|
|
4429
|
+
outline: none !important;
|
|
4430
|
+
}
|
|
4431
|
+
|
|
4432
|
+
#pagination-wrapper {
|
|
4433
|
+
display: flex;
|
|
4434
|
+
flex-direction: row-reverse;
|
|
4435
|
+
justify-content: space-between;
|
|
4436
|
+
align-items: center;
|
|
4437
|
+
padding: 15px 0;
|
|
4438
|
+
}
|
|
4439
|
+
|
|
4440
|
+
.table-data-loading {
|
|
4441
|
+
display: flex;
|
|
4442
|
+
align-items: center;
|
|
4443
|
+
}
|
|
4444
|
+
|
|
4445
|
+
.table-data-loading small {
|
|
4446
|
+
font-size: 12px;
|
|
4447
|
+
padding-left: 5px;
|
|
4448
|
+
color: #888;
|
|
4449
|
+
}
|
|
4450
|
+
|
|
4451
|
+
```
|
|
4452
|
+
|
|
4453
|
+
**tsconfig.json**
|
|
4454
|
+
|
|
4455
|
+
```json
|
|
4456
|
+
{
|
|
4457
|
+
"include": [
|
|
4458
|
+
"./**/*"
|
|
4459
|
+
],
|
|
4460
|
+
"compilerOptions": {
|
|
4461
|
+
"strict": true,
|
|
4462
|
+
"esModuleInterop": true,
|
|
4463
|
+
"lib": [ "dom", "es2015" ],
|
|
4464
|
+
"jsx": "react-jsx"
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
```
|
|
4468
|
+
|
|
4469
|
+
**/App.tsx**
|
|
4470
|
+
|
|
4471
|
+
```tsx
|
|
4472
|
+
import React from "react";
|
|
4473
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4474
|
+
import {
|
|
4475
|
+
CellBasic,
|
|
4476
|
+
CellHeader,
|
|
4477
|
+
TableHeader,
|
|
4478
|
+
TableRow,
|
|
4479
|
+
Table,
|
|
4480
|
+
CellHeaderDirection,
|
|
4481
|
+
CellComponent,
|
|
4482
|
+
Tag,
|
|
4483
|
+
Pagination,
|
|
4484
|
+
Icon,
|
|
4485
|
+
} from "@czi-sds/components";
|
|
4486
|
+
import {
|
|
4487
|
+
ColumnDef,
|
|
4488
|
+
flexRender,
|
|
4489
|
+
getCoreRowModel,
|
|
4490
|
+
getSortedRowModel,
|
|
4491
|
+
getPaginationRowModel,
|
|
4492
|
+
SortingState,
|
|
4493
|
+
useReactTable,
|
|
4494
|
+
PaginationState,
|
|
4495
|
+
CellContext,
|
|
4496
|
+
HeaderContext,
|
|
4497
|
+
} from "@tanstack/react-table";
|
|
4498
|
+
import "./styles.css";
|
|
4499
|
+
import { styled } from "@mui/material";
|
|
4500
|
+
import { faker } from "@faker-js/faker";
|
|
4501
|
+
|
|
4502
|
+
export type Person = {
|
|
4503
|
+
firstName: string;
|
|
4504
|
+
lastName: string;
|
|
4505
|
+
age: number;
|
|
4506
|
+
visits: number;
|
|
4507
|
+
progress: number;
|
|
4508
|
+
status: "relationship" | "complicated" | "single";
|
|
4509
|
+
subRows?: Person[];
|
|
4510
|
+
};
|
|
4511
|
+
|
|
4512
|
+
const DATA_COUNT = 198;
|
|
4513
|
+
|
|
4514
|
+
const newPerson = (): Person => {
|
|
4515
|
+
return {
|
|
4516
|
+
firstName: faker.person.firstName(),
|
|
4517
|
+
lastName: faker.person.lastName(),
|
|
4518
|
+
age: faker.number.int(40),
|
|
4519
|
+
visits: faker.number.int(1000),
|
|
4520
|
+
progress: faker.number.int(100),
|
|
4521
|
+
status: faker.helpers.shuffle<Person["status"]>([
|
|
4522
|
+
"relationship",
|
|
4523
|
+
"complicated",
|
|
4524
|
+
"single",
|
|
4525
|
+
])[0]!,
|
|
4526
|
+
};
|
|
4527
|
+
};
|
|
4528
|
+
|
|
4529
|
+
function makeData(...lens: number[]) {
|
|
4530
|
+
const makeDataLevel = (depth = 0): Person[] => {
|
|
4531
|
+
const len = lens[depth]!;
|
|
4532
|
+
return [...Array(len)].map((): Person => {
|
|
4533
|
+
return {
|
|
4534
|
+
...newPerson(),
|
|
4535
|
+
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
|
|
4536
|
+
};
|
|
4537
|
+
});
|
|
4538
|
+
};
|
|
4539
|
+
|
|
4540
|
+
return makeDataLevel();
|
|
4541
|
+
}
|
|
4542
|
+
|
|
4543
|
+
const data = makeData(DATA_COUNT);
|
|
4544
|
+
|
|
4545
|
+
async function fetchData(options: { pageIndex: number; pageSize: number }) {
|
|
4546
|
+
// Simulate some network latency
|
|
4547
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
4548
|
+
|
|
4549
|
+
return {
|
|
4550
|
+
rows: data.slice(
|
|
4551
|
+
options.pageIndex * options.pageSize,
|
|
4552
|
+
(options.pageIndex + 1) * options.pageSize
|
|
4553
|
+
),
|
|
4554
|
+
pageCount: Math.ceil(data.length / options.pageSize),
|
|
4555
|
+
};
|
|
4556
|
+
}
|
|
4557
|
+
|
|
4558
|
+
function App() {
|
|
4559
|
+
const PAGE_SIZE = 3;
|
|
4560
|
+
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
4561
|
+
|
|
4562
|
+
const [{ pageIndex, pageSize }, setPagination] =
|
|
4563
|
+
React.useState<PaginationState>({
|
|
4564
|
+
pageIndex: 0,
|
|
4565
|
+
pageSize: PAGE_SIZE,
|
|
4566
|
+
});
|
|
4567
|
+
|
|
4568
|
+
const fetchDataOptions = {
|
|
4569
|
+
pageIndex,
|
|
4570
|
+
pageSize,
|
|
4571
|
+
};
|
|
4572
|
+
|
|
4573
|
+
const dataQuery = useQuery(
|
|
4574
|
+
["data", fetchDataOptions],
|
|
4575
|
+
() => fetchData(fetchDataOptions),
|
|
4576
|
+
{ keepPreviousData: true }
|
|
4577
|
+
);
|
|
4578
|
+
|
|
4579
|
+
const defaultData = React.useMemo(() => [], []);
|
|
4580
|
+
|
|
4581
|
+
function createHeaderCell(
|
|
4582
|
+
header: HeaderContext<Person, unknown>,
|
|
4583
|
+
name: string
|
|
4584
|
+
) {
|
|
4585
|
+
return (
|
|
4586
|
+
<CellHeader
|
|
4587
|
+
key={header.header.id}
|
|
4588
|
+
colSpan={header.header.colSpan}
|
|
4589
|
+
direction={
|
|
4590
|
+
header.column.getIsSorted()
|
|
4591
|
+
? (header.column.getIsSorted() as CellHeaderDirection)
|
|
4592
|
+
: undefined
|
|
4593
|
+
}
|
|
4594
|
+
active={!!header.column.getIsSorted()}
|
|
4595
|
+
onClick={header.column.getToggleSortingHandler()}
|
|
4596
|
+
horizontalAlign={header.header.id === "createdAt" ? "right" : "left"}
|
|
4597
|
+
shouldShowTooltipOnHover={true}
|
|
4598
|
+
tooltipText={name}
|
|
4599
|
+
tooltipProps={{
|
|
4600
|
+
enterDelay: 2000,
|
|
4601
|
+
enterNextDelay: 2000,
|
|
4602
|
+
}}
|
|
4603
|
+
>
|
|
4604
|
+
{name}
|
|
4605
|
+
</CellHeader>
|
|
4606
|
+
);
|
|
4607
|
+
}
|
|
4608
|
+
|
|
4609
|
+
function createBodyCell(cell: CellContext<Person, unknown>) {
|
|
4610
|
+
return (
|
|
4611
|
+
<CellBasic
|
|
4612
|
+
key={cell.cell.id}
|
|
4613
|
+
primaryText={cell.getValue() as string}
|
|
4614
|
+
horizontalAlign={cell.column.id === "progress" ? "center" : "left"}
|
|
4615
|
+
verticalAlign="center"
|
|
4616
|
+
shouldShowTooltipOnHover={true}
|
|
4617
|
+
/>
|
|
4618
|
+
);
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4621
|
+
const columns = React.useMemo<ColumnDef<Person>[]>(
|
|
4622
|
+
() => [
|
|
4623
|
+
{
|
|
4624
|
+
accessorKey: "firstName",
|
|
4625
|
+
cell: (cell) => createBodyCell(cell),
|
|
4626
|
+
header: (header) => createHeaderCell(header, "First Name"),
|
|
4627
|
+
},
|
|
4628
|
+
{
|
|
4629
|
+
accessorKey: "lastName",
|
|
4630
|
+
cell: (cell) => createBodyCell(cell),
|
|
4631
|
+
header: (header) => createHeaderCell(header, "Last Name"),
|
|
4632
|
+
},
|
|
4633
|
+
{
|
|
4634
|
+
accessorKey: "age",
|
|
4635
|
+
cell: (cell) => createBodyCell(cell),
|
|
4636
|
+
header: (header) => createHeaderCell(header, "Age"),
|
|
4637
|
+
},
|
|
4638
|
+
{
|
|
4639
|
+
accessorKey: "visits",
|
|
4640
|
+
cell: (cell) => createBodyCell(cell),
|
|
4641
|
+
header: (header) => createHeaderCell(header, "Visits"),
|
|
4642
|
+
},
|
|
4643
|
+
{
|
|
4644
|
+
accessorKey: "status",
|
|
4645
|
+
cell: (cell) => (
|
|
4646
|
+
<CellComponent key={cell.cell.id} verticalAlign="center">
|
|
4647
|
+
<Tag
|
|
4648
|
+
label={cell.getValue() as string}
|
|
4649
|
+
color={
|
|
4650
|
+
cell.cell.getValue() === "single"
|
|
4651
|
+
? "notice"
|
|
4652
|
+
: cell.cell.getValue() === "relationship"
|
|
4653
|
+
? "positive"
|
|
4654
|
+
: "negative"
|
|
4655
|
+
}
|
|
4656
|
+
sdsStyle="rounded"
|
|
4657
|
+
sdsType="secondary"
|
|
4658
|
+
/>
|
|
4659
|
+
</CellComponent>
|
|
4660
|
+
),
|
|
4661
|
+
header: (header) => createHeaderCell(header, "Status"),
|
|
4662
|
+
},
|
|
4663
|
+
{
|
|
4664
|
+
accessorKey: "progress",
|
|
4665
|
+
cell: (cell) => createBodyCell(cell),
|
|
4666
|
+
header: (header) => createHeaderCell(header, "Profile Progress"),
|
|
4667
|
+
},
|
|
4668
|
+
],
|
|
4669
|
+
[]
|
|
4670
|
+
);
|
|
4671
|
+
|
|
4672
|
+
const pagination = React.useMemo(
|
|
4673
|
+
() => ({
|
|
4674
|
+
pageIndex,
|
|
4675
|
+
pageSize,
|
|
4676
|
+
}),
|
|
4677
|
+
[pageIndex, pageSize]
|
|
4678
|
+
);
|
|
4679
|
+
|
|
4680
|
+
const table = useReactTable({
|
|
4681
|
+
data: dataQuery.data?.rows ?? defaultData,
|
|
4682
|
+
columns,
|
|
4683
|
+
state: {
|
|
4684
|
+
sorting,
|
|
4685
|
+
pagination,
|
|
4686
|
+
},
|
|
4687
|
+
onPaginationChange: setPagination,
|
|
4688
|
+
onSortingChange: setSorting,
|
|
4689
|
+
getCoreRowModel: getCoreRowModel(),
|
|
4690
|
+
getSortedRowModel: getSortedRowModel(),
|
|
4691
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
4692
|
+
manualPagination: true,
|
|
4693
|
+
autoResetAll: false,
|
|
4694
|
+
debugTable: true,
|
|
4695
|
+
});
|
|
4696
|
+
|
|
4697
|
+
const StyledTableBody = styled("tbody")`
|
|
4698
|
+
${dataQuery.isFetching ? `opacity: .3; filter: grayscale(1)` : null}
|
|
4699
|
+
`;
|
|
4700
|
+
|
|
4701
|
+
return (
|
|
4702
|
+
<div className="app">
|
|
4703
|
+
<Table>
|
|
4704
|
+
<TableHeader>
|
|
4705
|
+
{table.getFlatHeaders().map((header) => (
|
|
4706
|
+
<React.Fragment key={header.id}>
|
|
4707
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
4708
|
+
</React.Fragment>
|
|
4709
|
+
))}
|
|
4710
|
+
</TableHeader>
|
|
4711
|
+
<StyledTableBody>
|
|
4712
|
+
{table.getRowModel().rows.map((row) => {
|
|
4713
|
+
return (
|
|
4714
|
+
<TableRow key={row.id}>
|
|
4715
|
+
{row.getVisibleCells().map((cell) => (
|
|
4716
|
+
<React.Fragment key={cell.id}>
|
|
4717
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
4718
|
+
</React.Fragment>
|
|
4719
|
+
))}
|
|
4720
|
+
</TableRow>
|
|
4721
|
+
);
|
|
4722
|
+
})}
|
|
4723
|
+
</StyledTableBody>
|
|
4724
|
+
</Table>
|
|
4725
|
+
<div id="pagination-wrapper">
|
|
4726
|
+
<Pagination
|
|
4727
|
+
sdsStyle="round"
|
|
4728
|
+
pageSize={table.getState().pagination.pageSize}
|
|
4729
|
+
onPageChange={(page: number) => {
|
|
4730
|
+
table.setPageIndex(page - 1);
|
|
4731
|
+
}}
|
|
4732
|
+
onNextPage={() => table.nextPage()}
|
|
4733
|
+
onPreviousPage={() => table.previousPage()}
|
|
4734
|
+
totalCount={DATA_COUNT}
|
|
4735
|
+
siblingCount={1}
|
|
4736
|
+
currentPage={table.getState().pagination.pageIndex + 1}
|
|
4737
|
+
truncateDropdown
|
|
4738
|
+
/>
|
|
4739
|
+
{dataQuery.isFetching ? (
|
|
4740
|
+
<div className="table-data-loading">
|
|
4741
|
+
<Icon sdsIcon="Loading" sdsSize="l" sdsType="static" />
|
|
4742
|
+
<small> Fetching Table Data ...</small>
|
|
4743
|
+
</div>
|
|
4744
|
+
) : null}
|
|
4745
|
+
</div>
|
|
4746
|
+
</div>
|
|
4747
|
+
);
|
|
4748
|
+
}
|
|
4749
|
+
|
|
4750
|
+
export default App;
|
|
4751
|
+
|
|
4752
|
+
```
|
|
4753
|
+
|
|
4754
|
+
**/index.tsx**
|
|
4755
|
+
|
|
4756
|
+
```tsx
|
|
4757
|
+
import React from "react";
|
|
4758
|
+
import { createRoot } from "react-dom/client";
|
|
4759
|
+
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
|
|
4760
|
+
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
|
|
4761
|
+
import { Theme } from "@czi-sds/components";
|
|
4762
|
+
import CssBaseline from "@mui/material/CssBaseline";
|
|
4763
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
4764
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
4765
|
+
import App from "./App";
|
|
4766
|
+
|
|
4767
|
+
const container = document.getElementById("root");
|
|
4768
|
+
const root = createRoot(container!);
|
|
4769
|
+
|
|
4770
|
+
const queryClient = new QueryClient();
|
|
4771
|
+
|
|
4772
|
+
const RootApp = () => {
|
|
4773
|
+
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
|
4774
|
+
const theme = prefersDarkMode ? Theme("dark") : Theme("light");
|
|
4775
|
+
|
|
4776
|
+
return (
|
|
4777
|
+
<StyledEngineProvider injectFirst>
|
|
4778
|
+
<ThemeProvider theme={theme}>
|
|
4779
|
+
<EmotionThemeProvider theme={theme}>
|
|
4780
|
+
<QueryClientProvider client={queryClient}>
|
|
4781
|
+
<CssBaseline />
|
|
4782
|
+
<App />
|
|
4783
|
+
</QueryClientProvider>
|
|
4784
|
+
</EmotionThemeProvider>
|
|
4785
|
+
</ThemeProvider>
|
|
4786
|
+
</StyledEngineProvider>
|
|
4787
|
+
);
|
|
4788
|
+
};
|
|
4789
|
+
|
|
4790
|
+
root.render(
|
|
4791
|
+
<React.StrictMode>
|
|
4792
|
+
<RootApp />
|
|
4793
|
+
</React.StrictMode>
|
|
4794
|
+
);
|
|
4795
|
+
|
|
4796
|
+
```
|
|
4797
|
+
|
|
4798
|
+
**/public/index.html**
|
|
4799
|
+
|
|
4800
|
+
```html
|
|
4801
|
+
<!DOCTYPE html>
|
|
4802
|
+
<html lang="en">
|
|
4803
|
+
<head>
|
|
4804
|
+
<meta charset="UTF-8" />
|
|
4805
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
4806
|
+
<title>Table</title>
|
|
4807
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
4808
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
4809
|
+
<link
|
|
4810
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
|
|
4811
|
+
rel="stylesheet"
|
|
4812
|
+
/>
|
|
4813
|
+
<link
|
|
4814
|
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
|
|
4815
|
+
rel="stylesheet"
|
|
4816
|
+
/>
|
|
4817
|
+
</head>
|
|
4818
|
+
<body>
|
|
4819
|
+
<div id="root"></div>
|
|
4820
|
+
</body>
|
|
4821
|
+
</html>
|
|
4822
|
+
|
|
4823
|
+
```
|
|
4824
|
+
|
|
4825
|
+
**/package.json**
|
|
4826
|
+
|
|
4827
|
+
```json
|
|
4828
|
+
{"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
|
|
4829
|
+
```
|
|
4830
|
+
|
|
4831
|
+
---
|
|
4832
|
+
|