@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.
Files changed (50) hide show
  1. package/data/zeroheight-cache.json +5195 -0
  2. package/data/zeroheight-exports/Accordion.md +675 -0
  3. package/data/zeroheight-exports/Banner.md +576 -0
  4. package/data/zeroheight-exports/Borders.md +118 -0
  5. package/data/zeroheight-exports/Buttons.md +2260 -0
  6. package/data/zeroheight-exports/Callout.md +756 -0
  7. package/data/zeroheight-exports/Colors.md +633 -0
  8. package/data/zeroheight-exports/Content-Card.md +162 -0
  9. package/data/zeroheight-exports/Contributing-to-SDS.md +99 -0
  10. package/data/zeroheight-exports/Control-Inputs.md +2066 -0
  11. package/data/zeroheight-exports/Corners.md +112 -0
  12. package/data/zeroheight-exports/Cover-page.md +4 -0
  13. package/data/zeroheight-exports/Dialog.md +1133 -0
  14. package/data/zeroheight-exports/Downloads.md +121 -0
  15. package/data/zeroheight-exports/Drop-Shadows.md +80 -0
  16. package/data/zeroheight-exports/Dropdown-Input.md +1149 -0
  17. package/data/zeroheight-exports/Dropdown-Menu.md +3787 -0
  18. package/data/zeroheight-exports/Element-Status-Tracker.md +268 -0
  19. package/data/zeroheight-exports/Field-Inputs.md +965 -0
  20. package/data/zeroheight-exports/Filters.md +868 -0
  21. package/data/zeroheight-exports/Getting-Started.md +1296 -0
  22. package/data/zeroheight-exports/Hero.md +256 -0
  23. package/data/zeroheight-exports/Icons.md +432 -0
  24. package/data/zeroheight-exports/Intent.md +47 -0
  25. package/data/zeroheight-exports/Introduction-to-Bases.md +36 -0
  26. package/data/zeroheight-exports/Introduction-to-Cells.md +36 -0
  27. package/data/zeroheight-exports/Introduction-to-DNA.md +36 -0
  28. package/data/zeroheight-exports/Introduction-to-Genes.md +36 -0
  29. package/data/zeroheight-exports/Introduction.md +116 -0
  30. package/data/zeroheight-exports/Link.md +373 -0
  31. package/data/zeroheight-exports/Lists.md +617 -0
  32. package/data/zeroheight-exports/Loading-Indicator.md +38 -0
  33. package/data/zeroheight-exports/Navigation.md +853 -0
  34. package/data/zeroheight-exports/Notification.md +858 -0
  35. package/data/zeroheight-exports/Panel.md +862 -0
  36. package/data/zeroheight-exports/README.md +124 -0
  37. package/data/zeroheight-exports/Responsive-Design.md +275 -0
  38. package/data/zeroheight-exports/Search-Input.md +54 -0
  39. package/data/zeroheight-exports/Search.md +609 -0
  40. package/data/zeroheight-exports/Segmented-Control.md +561 -0
  41. package/data/zeroheight-exports/Spacing.md +298 -0
  42. package/data/zeroheight-exports/Table.md +4832 -0
  43. package/data/zeroheight-exports/Tabs.md +773 -0
  44. package/data/zeroheight-exports/Tags.md +1025 -0
  45. package/data/zeroheight-exports/Text-Link.md +185 -0
  46. package/data/zeroheight-exports/Theming.md +313 -0
  47. package/data/zeroheight-exports/Tooltips.md +1564 -0
  48. package/data/zeroheight-exports/Typography.md +769 -0
  49. package/dist/stdio.js +515 -12
  50. 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
+