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