@patternfly/react-docs 7.6.0-prerelease.8 → 7.6.0-prerelease.9

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 (163) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +21 -0
  3. package/package.json +12 -11
  4. package/patternfly-docs/generated/components/about-modal/react.js +0 -149
  5. package/patternfly-docs/generated/components/accordion/react.js +0 -262
  6. package/patternfly-docs/generated/components/action-list/react.js +0 -144
  7. package/patternfly-docs/generated/components/alert/react-demos.js +0 -56
  8. package/patternfly-docs/generated/components/alert/react.js +0 -1433
  9. package/patternfly-docs/generated/components/avatar/react.js +0 -166
  10. package/patternfly-docs/generated/components/back-to-top/react-demos.js +0 -60
  11. package/patternfly-docs/generated/components/back-to-top/react.js +0 -77
  12. package/patternfly-docs/generated/components/backdrop/react.js +0 -64
  13. package/patternfly-docs/generated/components/background-image/react.js +0 -62
  14. package/patternfly-docs/generated/components/badge/react.js +0 -97
  15. package/patternfly-docs/generated/components/banner/react-demos.js +0 -57
  16. package/patternfly-docs/generated/components/banner/react.js +0 -148
  17. package/patternfly-docs/generated/components/brand/react.js +0 -142
  18. package/patternfly-docs/generated/components/breadcrumb/react.js +0 -206
  19. package/patternfly-docs/generated/components/button/react-demos.js +0 -57
  20. package/patternfly-docs/generated/components/button/react.js +0 -826
  21. package/patternfly-docs/generated/components/card/react-demos.js +0 -201
  22. package/patternfly-docs/generated/components/card/react.js +0 -1015
  23. package/patternfly-docs/generated/components/charts/area-chart/-Victory.js +0 -1350
  24. package/patternfly-docs/generated/components/charts/bar-chart/-Victory.js +0 -1334
  25. package/patternfly-docs/generated/components/charts/box-plot-chart/-Victory.js +0 -1282
  26. package/patternfly-docs/generated/components/charts/bullet-chart/-Victory.js +0 -848
  27. package/patternfly-docs/generated/components/charts/colors-for-charts/-Victory.js +0 -192
  28. package/patternfly-docs/generated/components/charts/donut-chart/-Victory.js +0 -426
  29. package/patternfly-docs/generated/components/charts/donut-utilization-chart/-Victory.js +0 -804
  30. package/patternfly-docs/generated/components/charts/legends/-Victory.js +0 -3230
  31. package/patternfly-docs/generated/components/charts/line-chart/-Victory.js +0 -1178
  32. package/patternfly-docs/generated/components/charts/line-chart/ECharts.js +0 -525
  33. package/patternfly-docs/generated/components/charts/patterns/-Victory.js +0 -3382
  34. package/patternfly-docs/generated/components/charts/pie-chart/-Victory.js +0 -377
  35. package/patternfly-docs/generated/components/charts/resize-observer/-Victory.js +0 -2475
  36. package/patternfly-docs/generated/components/charts/sankey-chart/ECharts.js +0 -538
  37. package/patternfly-docs/generated/components/charts/scatter-chart/-Victory.js +0 -1551
  38. package/patternfly-docs/generated/components/charts/skeletons/-Victory.js +0 -4115
  39. package/patternfly-docs/generated/components/charts/sparkline-chart/-Victory.js +0 -955
  40. package/patternfly-docs/generated/components/charts/stack-chart/-Victory.js +0 -1173
  41. package/patternfly-docs/generated/components/charts/threshold-chart/-Victory.js +0 -1166
  42. package/patternfly-docs/generated/components/charts/tooltips/-Victory.js +0 -413
  43. package/patternfly-docs/generated/components/chip/react-deprecated.js +0 -323
  44. package/patternfly-docs/generated/components/clipboard-copy/react.js +0 -373
  45. package/patternfly-docs/generated/components/code-block/react.js +0 -148
  46. package/patternfly-docs/generated/components/code-editor/react.js +0 -659
  47. package/patternfly-docs/generated/components/compass/react-demos.js +0 -147
  48. package/patternfly-docs/generated/components/compass/react.js +0 -440
  49. package/patternfly-docs/generated/components/content/react.js +0 -248
  50. package/patternfly-docs/generated/components/data-list/react-demos.js +0 -90
  51. package/patternfly-docs/generated/components/data-list/react.js +0 -709
  52. package/patternfly-docs/generated/components/date-and-time/calendar-month/react.js +0 -283
  53. package/patternfly-docs/generated/components/date-and-time/date-and-time-picker/react-demos.js +0 -64
  54. package/patternfly-docs/generated/components/date-and-time/date-picker/react-demos.js +0 -83
  55. package/patternfly-docs/generated/components/date-and-time/date-picker/react.js +0 -395
  56. package/patternfly-docs/generated/components/date-and-time/time-picker/react.js +0 -241
  57. package/patternfly-docs/generated/components/description-list/react-demos.js +0 -58
  58. package/patternfly-docs/generated/components/description-list/react.js +0 -743
  59. package/patternfly-docs/generated/components/divider/react.js +0 -126
  60. package/patternfly-docs/generated/components/drag-and-drop/react-demos.js +0 -351
  61. package/patternfly-docs/generated/components/drag-and-drop/react-deprecated.js +0 -184
  62. package/patternfly-docs/generated/components/drag-and-drop/react.js +0 -137
  63. package/patternfly-docs/generated/components/drawer/react.js +0 -598
  64. package/patternfly-docs/generated/components/dual-list-selector/react-deprecated.js +0 -772
  65. package/patternfly-docs/generated/components/dual-list-selector/react.js +0 -594
  66. package/patternfly-docs/generated/components/empty-state/react.js +0 -199
  67. package/patternfly-docs/generated/components/expandable-section/react-demos.js +0 -65
  68. package/patternfly-docs/generated/components/expandable-section/react.js +0 -408
  69. package/patternfly-docs/generated/components/file-upload/multiple-file-upload/react-demos.js +0 -52
  70. package/patternfly-docs/generated/components/file-upload/multiple-file-upload/react.js +0 -398
  71. package/patternfly-docs/generated/components/file-upload/simple-file-upload/react.js +0 -749
  72. package/patternfly-docs/generated/components/forms/checkbox/react.js +0 -222
  73. package/patternfly-docs/generated/components/forms/form/react.js +0 -1106
  74. package/patternfly-docs/generated/components/forms/form-select/react.js +0 -208
  75. package/patternfly-docs/generated/components/forms/radio/react.js +0 -212
  76. package/patternfly-docs/generated/components/forms/text-area/react.js +0 -160
  77. package/patternfly-docs/generated/components/forms/text-input/react.js +0 -216
  78. package/patternfly-docs/generated/components/helper-text/react-demos.js +0 -180
  79. package/patternfly-docs/generated/components/helper-text/react.js +0 -164
  80. package/patternfly-docs/generated/components/hero/react.js +0 -88
  81. package/patternfly-docs/generated/components/hint/react.js +0 -169
  82. package/patternfly-docs/generated/components/icon/react.js +0 -215
  83. package/patternfly-docs/generated/components/input-group/react.js +0 -182
  84. package/patternfly-docs/generated/components/jump-links/react-demos.js +0 -154
  85. package/patternfly-docs/generated/components/jump-links/react.js +0 -212
  86. package/patternfly-docs/generated/components/label/react-demos.js +0 -57
  87. package/patternfly-docs/generated/components/label/react.js +0 -417
  88. package/patternfly-docs/generated/components/list/react.js +0 -175
  89. package/patternfly-docs/generated/components/login-page/react.js +0 -587
  90. package/patternfly-docs/generated/components/masthead/react-demos.js +0 -79
  91. package/patternfly-docs/generated/components/masthead/react.js +0 -291
  92. package/patternfly-docs/generated/components/menus/application-launcher/react-demos.js +0 -769
  93. package/patternfly-docs/generated/components/menus/context-selector/react-demos.js +0 -665
  94. package/patternfly-docs/generated/components/menus/custom-menus/react-demos.js +0 -187
  95. package/patternfly-docs/generated/components/menus/dropdown/react-templates.js +0 -163
  96. package/patternfly-docs/generated/components/menus/dropdown/react.js +0 -998
  97. package/patternfly-docs/generated/components/menus/menu/react.js +0 -1540
  98. package/patternfly-docs/generated/components/menus/menu-toggle/react.js +0 -747
  99. package/patternfly-docs/generated/components/menus/options-menu/react-demos.js +0 -508
  100. package/patternfly-docs/generated/components/menus/select/react-templates.js +0 -257
  101. package/patternfly-docs/generated/components/menus/select/react.js +0 -998
  102. package/patternfly-docs/generated/components/modal/react-deprecated.js +0 -554
  103. package/patternfly-docs/generated/components/modal/react.js +0 -597
  104. package/patternfly-docs/generated/components/navigation/react-demos.js +0 -356
  105. package/patternfly-docs/generated/components/navigation/react.js +0 -409
  106. package/patternfly-docs/generated/components/notification-badge/react.js +0 -196
  107. package/patternfly-docs/generated/components/notification-drawer/react-demos.js +0 -107
  108. package/patternfly-docs/generated/components/notification-drawer/react.js +0 -394
  109. package/patternfly-docs/generated/components/number-input/react.js +0 -210
  110. package/patternfly-docs/generated/components/overflow-menu/react.js +0 -274
  111. package/patternfly-docs/generated/components/page/react-demos.js +0 -149
  112. package/patternfly-docs/generated/components/page/react.js +0 -1352
  113. package/patternfly-docs/generated/components/pagination/react.js +0 -492
  114. package/patternfly-docs/generated/components/panel/react.js +0 -236
  115. package/patternfly-docs/generated/components/popover/react.js +0 -390
  116. package/patternfly-docs/generated/components/progress/react-demos.js +0 -59
  117. package/patternfly-docs/generated/components/progress/react.js +0 -283
  118. package/patternfly-docs/generated/components/progress-stepper/react-demos.js +0 -45
  119. package/patternfly-docs/generated/components/progress-stepper/react.js +0 -219
  120. package/patternfly-docs/generated/components/search-input/react-demos.js +0 -113
  121. package/patternfly-docs/generated/components/search-input/react.js +0 -263
  122. package/patternfly-docs/generated/components/sidebar/react.js +0 -236
  123. package/patternfly-docs/generated/components/simple-list/react.js +0 -200
  124. package/patternfly-docs/generated/components/skeleton/react-demos.js +0 -44
  125. package/patternfly-docs/generated/components/skeleton/react.js +0 -122
  126. package/patternfly-docs/generated/components/skip-to-content/react.js +0 -73
  127. package/patternfly-docs/generated/components/slider/react.js +0 -309
  128. package/patternfly-docs/generated/components/spinner/react.js +0 -111
  129. package/patternfly-docs/generated/components/switch/react.js +0 -163
  130. package/patternfly-docs/generated/components/table/react-demos.js +0 -355
  131. package/patternfly-docs/generated/components/table/react-deprecated.js +0 -1350
  132. package/patternfly-docs/generated/components/table/react.js +0 -3241
  133. package/patternfly-docs/generated/components/tabs/react-demos.js +0 -108
  134. package/patternfly-docs/generated/components/tabs/react.js +0 -1359
  135. package/patternfly-docs/generated/components/text-input-group/react-demos.js +0 -152
  136. package/patternfly-docs/generated/components/text-input-group/react.js +0 -278
  137. package/patternfly-docs/generated/components/tile/react-deprecated.js +0 -242
  138. package/patternfly-docs/generated/components/timestamp/react.js +0 -283
  139. package/patternfly-docs/generated/components/title/react.js +0 -94
  140. package/patternfly-docs/generated/components/toggle-group/react.js +0 -299
  141. package/patternfly-docs/generated/components/toolbar/react-demos.js +0 -66
  142. package/patternfly-docs/generated/components/toolbar/react.js +0 -932
  143. package/patternfly-docs/generated/components/tooltip/react.js +0 -241
  144. package/patternfly-docs/generated/components/tree-view/react.js +0 -429
  145. package/patternfly-docs/generated/components/truncate/react.js +0 -211
  146. package/patternfly-docs/generated/components/wizard/react-demos.js +0 -87
  147. package/patternfly-docs/generated/components/wizard/react-deprecated.js +0 -788
  148. package/patternfly-docs/generated/components/wizard/react.js +0 -986
  149. package/patternfly-docs/generated/developer-guides/open-ui-automation/react.js +0 -285
  150. package/patternfly-docs/generated/foundations-and-styles/layouts/bullseye/react.js +0 -70
  151. package/patternfly-docs/generated/foundations-and-styles/layouts/flex/react.js +0 -506
  152. package/patternfly-docs/generated/foundations-and-styles/layouts/gallery/react.js +0 -94
  153. package/patternfly-docs/generated/foundations-and-styles/layouts/grid/react.js +0 -272
  154. package/patternfly-docs/generated/foundations-and-styles/layouts/level/react.js +0 -87
  155. package/patternfly-docs/generated/foundations-and-styles/layouts/split/react.js +0 -124
  156. package/patternfly-docs/generated/foundations-and-styles/layouts/stack/react.js +0 -112
  157. package/patternfly-docs/generated/index.js +0 -1769
  158. package/patternfly-docs/generated/patterns/card-view/react-demos.js +0 -78
  159. package/patternfly-docs/generated/patterns/filters/react-demos.js +0 -141
  160. package/patternfly-docs/generated/patterns/password-generator/react-demos.js +0 -51
  161. package/patternfly-docs/generated/patterns/password-strength/react-demos.js +0 -61
  162. package/patternfly-docs/generated/patterns/primary-detail/react-demos.js +0 -124
  163. package/patternfly-docs/generated/patterns/right-to-left/react-demos.js +0 -81
@@ -1,3241 +0,0 @@
1
- import React from 'react';
2
- import { AutoLinkHeader, Example, Link as PatternflyThemeLink } from '@patternfly/documentation-framework/components';
3
- import { Fragment, isValidElement, useCallback, useEffect, useRef, useState, useLayoutEffect } from 'react';
4
- import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
5
- import RhUiCodeIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-code-icon';
6
- import RhUiBranchFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-branch-fill-icon';
7
- import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
8
- import LeafIcon from '@patternfly/react-icons/dist/esm/icons/leaf-icon';
9
- import FolderIcon from '@patternfly/react-icons/dist/esm/icons/folder-icon';
10
- import FolderOpenIcon from '@patternfly/react-icons/dist/esm/icons/folder-open-icon';
11
- import RhMicronsSortDownLargeToSmallIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-sort-down-large-to-small-icon';
12
- import RhUiBlueprintIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-blueprint-icon';
13
- import RhUiEditFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-edit-fill-icon';
14
- import RhUiEllipsisVerticalFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ellipsis-vertical-fill-icon';
15
- import CheckIcon from '@patternfly/react-icons/dist/esm/icons/check-icon';
16
- import RhMicronsCloseIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-close-icon';
17
- import { css } from '@patternfly/react-styles';
18
- import styles from '@patternfly/react-styles/css/components/Table/table';
19
- import spacing from '@patternfly/react-styles/css/utilities/Spacing/spacing';
20
- import textStyles from '@patternfly/react-styles/css/utilities/Text/text';
21
- import inlineEditStyles from '@patternfly/react-styles/css/components/InlineEdit/inline-edit';
22
- import t_global_background_color_secondary_default from '@patternfly/react-tokens/dist/esm/t_global_background_color_secondary_default';
23
- const pageData = {
24
- "id": "Table",
25
- "section": "components",
26
- "subsection": "",
27
- "deprecated": false,
28
- "template": false,
29
- "beta": false,
30
- "demo": false,
31
- "newImplementationLink": false,
32
- "source": "react",
33
- "tabName": null,
34
- "slug": "/components/table/react",
35
- "sourceLink": "https://github.com/patternfly/patternfly-react/blob/main/packages/react-table/src/components/Table/examples/Table.md",
36
- "relPath": "packages/react-table/src/components/Table/examples/Table.md",
37
- "propComponents": [
38
- {
39
- "name": "Table",
40
- "description": "",
41
- "props": [
42
- {
43
- "name": "aria-label",
44
- "type": "string",
45
- "description": "Adds an accessible name for the Table"
46
- },
47
- {
48
- "name": "borders",
49
- "type": "boolean",
50
- "description": "Render borders"
51
- },
52
- {
53
- "name": "children",
54
- "type": "React.ReactNode",
55
- "description": "Content rendered inside the Table"
56
- },
57
- {
58
- "name": "className",
59
- "type": "string",
60
- "description": "Additional classes added to the Table"
61
- },
62
- {
63
- "name": "gridBreakPoint",
64
- "type": "'' | 'grid' | 'grid-md' | 'grid-lg' | 'grid-xl' | 'grid-2xl'",
65
- "description": "Specifies the grid breakpoints"
66
- },
67
- {
68
- "name": "hasAnimations",
69
- "type": "boolean",
70
- "description": "Flag indicating whether expandable rows within the table have animations. Expandable rows cannot be dynamically rendered. This prop\nwill be removed in the next breaking change, with the default behavior becoming animations always being enabled.",
71
- "beta": true
72
- },
73
- {
74
- "name": "hasNoInset",
75
- "type": "boolean",
76
- "description": "Flag indicating this table's rows will not have the inset typically reserved for expanding/collapsing rows in a tree table. Intended for use on tree tables with no visible rows with children."
77
- },
78
- {
79
- "name": "isExpandable",
80
- "type": "boolean",
81
- "description": "Flag indicating this table contains expandable rows."
82
- },
83
- {
84
- "name": "isNested",
85
- "type": "boolean",
86
- "description": "Flag indicating this table is nested within another table"
87
- },
88
- {
89
- "name": "isNoPlainOnGlass",
90
- "type": "boolean",
91
- "description": "Flag indicating if the table should not have plain styling when in the glass theme",
92
- "beta": true
93
- },
94
- {
95
- "name": "isPlain",
96
- "type": "boolean",
97
- "description": "Flag indicating if the table should have plain styling with a transparent background",
98
- "beta": true
99
- },
100
- {
101
- "name": "isStickyHeader",
102
- "type": "boolean",
103
- "description": "If set to true, the table header sticks to the top of its container. This property applies both the sticky position and styling."
104
- },
105
- {
106
- "name": "isStickyHeaderBase",
107
- "type": "boolean",
108
- "description": "Flag indicating the table header should have sticky positioning to the top of the parent InnerScrollContainer.",
109
- "beta": true
110
- },
111
- {
112
- "name": "isStickyHeaderStuck",
113
- "type": "boolean",
114
- "description": "Flag indicating the table header should have stuck styling, when the header is not at the top of the scroll container.",
115
- "beta": true
116
- },
117
- {
118
- "name": "isStriped",
119
- "type": "boolean",
120
- "description": "Flag indicating this table should be striped. This property works best for a single <tbody> table. Striping may also be done manually by applying this property to Tbody and Tr components."
121
- },
122
- {
123
- "name": "isTreeTable",
124
- "type": "boolean",
125
- "description": "Flag indicating table is a tree table"
126
- },
127
- {
128
- "name": "nestedHeaderColumnSpans",
129
- "type": "number[]",
130
- "description": "Collection of column spans for nested headers. Deprecated: see https://github.com/patternfly/patternfly/issues/4584"
131
- },
132
- {
133
- "name": "ouiaId",
134
- "type": "number | string",
135
- "description": "Value to overwrite the randomly generated data-ouia-component-id."
136
- },
137
- {
138
- "name": "ouiaSafe",
139
- "type": "boolean",
140
- "description": "Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false."
141
- },
142
- {
143
- "name": "role",
144
- "type": "string",
145
- "description": "A valid WAI-ARIA role to be applied to the table element"
146
- },
147
- {
148
- "name": "selectableRowCaptionText",
149
- "type": "string",
150
- "description": "Visible text to add alongside the hidden a11y caption for tables with selectable rows."
151
- },
152
- {
153
- "name": "variant",
154
- "type": "TableVariant | 'compact'",
155
- "description": "Style variant for the Table\ncompact: Reduces spacing and makes the table more compact"
156
- }
157
- ]
158
- },
159
- {
160
- "name": "Thead",
161
- "description": "",
162
- "props": [
163
- {
164
- "name": "children",
165
- "type": "React.ReactNode",
166
- "description": "Content rendered inside the <thead> row group"
167
- },
168
- {
169
- "name": "className",
170
- "type": "string",
171
- "description": "Additional classes added to the <thead> element"
172
- },
173
- {
174
- "name": "hasNestedHeader",
175
- "type": "boolean",
176
- "description": "Indicates the <thead> contains a nested header"
177
- },
178
- {
179
- "name": "noWrap",
180
- "type": "boolean",
181
- "description": "Won't wrap the table head if true"
182
- }
183
- ]
184
- },
185
- {
186
- "name": "Tbody",
187
- "description": "",
188
- "props": [
189
- {
190
- "name": "children",
191
- "type": "React.ReactNode",
192
- "description": "Content rendered inside the <tbody> row group"
193
- },
194
- {
195
- "name": "className",
196
- "type": "string",
197
- "description": "Additional classes added to the <tbody> element"
198
- },
199
- {
200
- "name": "isEvenStriped",
201
- "type": "boolean",
202
- "description": "Flag indicating the <tbody> contains evenly striped rows."
203
- },
204
- {
205
- "name": "isExpanded",
206
- "type": "boolean",
207
- "description": "Modifies the body to allow for expandable rows"
208
- },
209
- {
210
- "name": "isOddStriped",
211
- "type": "boolean",
212
- "description": "Flag indicating the <tbody> contains oddly striped rows."
213
- }
214
- ]
215
- },
216
- {
217
- "name": "Tr",
218
- "description": "",
219
- "props": [
220
- {
221
- "name": "children",
222
- "type": "React.ReactNode",
223
- "description": "Content rendered inside the <tr> row"
224
- },
225
- {
226
- "name": "className",
227
- "type": "string",
228
- "description": "Additional classes added to the <tr> row"
229
- },
230
- {
231
- "name": "isBorderRow",
232
- "type": "boolean",
233
- "description": "Flag indicating the row will act as a border. This is typically used for a table with a nested and sticky header."
234
- },
235
- {
236
- "name": "isClickable",
237
- "type": "boolean",
238
- "description": "Flag which adds hover styles for the clickable table row"
239
- },
240
- {
241
- "name": "isContentExpanded",
242
- "type": "boolean",
243
- "description": "Flag indicating that the \"control row\" Tr has an expandable sibling Tr that is expanded or not. Only applicable to\na Tr within a Tbody, and should have the same value as an expandable Tr's isExpanded prop."
244
- },
245
- {
246
- "name": "isControlRow",
247
- "type": "boolean",
248
- "description": "Flag indicating the row is controlling the expansion of another row."
249
- },
250
- {
251
- "name": "isEditable",
252
- "type": "boolean",
253
- "description": "Only applicable to Tr within the Tbody: Whether the row is editable"
254
- },
255
- {
256
- "name": "isExpanded",
257
- "type": "boolean",
258
- "description": "Flag indicating whether an \"expandable\" Tr is expanded or not. Only applicable to a Tr within a Tbody.\nTo prevent column widths from responding automatically when expandable rows are toggled, the width prop must also be passed into either the th or td component"
259
- },
260
- {
261
- "name": "isHidden",
262
- "type": "boolean",
263
- "description": "Flag indicating the Tr is hidden"
264
- },
265
- {
266
- "name": "isRowSelected",
267
- "type": "boolean",
268
- "description": "Flag indicating the row is selected - adds selected styling"
269
- },
270
- {
271
- "name": "isSelectable",
272
- "type": "boolean",
273
- "description": "Flag indicating that the row is selectable"
274
- },
275
- {
276
- "name": "isStriped",
277
- "type": "boolean",
278
- "description": "Flag indicating the row is striped"
279
- },
280
- {
281
- "name": "onRowClick",
282
- "type": "(event?: React.KeyboardEvent | React.MouseEvent) => void",
283
- "description": "An event handler for the row"
284
- },
285
- {
286
- "name": "ouiaId",
287
- "type": "number | string",
288
- "description": "Value to overwrite the randomly generated data-ouia-component-id."
289
- },
290
- {
291
- "name": "ouiaSafe",
292
- "type": "boolean",
293
- "description": "Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false."
294
- },
295
- {
296
- "name": "resetOffset",
297
- "type": "boolean",
298
- "description": "Flag indicating the spacing offset of the first cell should be reset"
299
- }
300
- ]
301
- },
302
- {
303
- "name": "Th",
304
- "description": "",
305
- "props": [
306
- {
307
- "name": "aria-label",
308
- "type": "string",
309
- "description": "Provides an accessible name to the th. This should only be passed in when the th contains only non-text\ncontent, such as a \"select all\" checkbox or \"expand all\" toggle."
310
- },
311
- {
312
- "name": "children",
313
- "type": "React.ReactNode",
314
- "description": "Content rendered inside the cell"
315
- },
316
- {
317
- "name": "className",
318
- "type": "string",
319
- "description": "Additional classes added to the cell"
320
- },
321
- {
322
- "name": "component",
323
- "type": "React.ReactNode",
324
- "description": "Element to render"
325
- },
326
- {
327
- "name": "dataLabel",
328
- "type": "string",
329
- "description": "The column header the cell corresponds to. Applicable when this component is used as a direct child to <Tr>.\nThis attribute replaces table header in mobile viewport. It is rendered by ::before pseudo element."
330
- },
331
- {
332
- "name": "expand",
333
- "type": "ThExpandType",
334
- "description": "Renders a chevron so that all row chevrons can be expanded/collapsed"
335
- },
336
- {
337
- "name": "hasLeftBorder",
338
- "type": "boolean",
339
- "description": "Adds a border to the left side of the cell"
340
- },
341
- {
342
- "name": "hasRightBorder",
343
- "type": "boolean",
344
- "description": "Adds a border to the right side of the cell"
345
- },
346
- {
347
- "name": "info",
348
- "type": "ThInfoType",
349
- "description": "Adds tooltip/popover info button"
350
- },
351
- {
352
- "name": "isStickyColumn",
353
- "type": "boolean",
354
- "description": "Indicates the header column should be sticky"
355
- },
356
- {
357
- "name": "isSubheader",
358
- "type": "boolean",
359
- "description": "Indicates the <th> is part of a subheader of a nested header"
360
- },
361
- {
362
- "name": "modifier",
363
- "type": "'breakWord' | 'fitContent' | 'nowrap' | 'truncate' | 'wrap'",
364
- "description": "Style modifier to apply"
365
- },
366
- {
367
- "name": "onMouseEnter",
368
- "type": "(event: any) => void",
369
- "description": "Callback on mouse enter"
370
- },
371
- {
372
- "name": "scope",
373
- "type": "string",
374
- "description": "Adds scope to the column to associate header cells with data cells"
375
- },
376
- {
377
- "name": "screenReaderText",
378
- "type": "string",
379
- "description": "Visually hidden text accessible only via assistive technologies. This must be passed in if the\nth is intended to be visually empty, and must be conveyed as a column header text."
380
- },
381
- {
382
- "name": "select",
383
- "type": "ThSelectType",
384
- "description": "Renders a checkbox select so that all row checkboxes can be selected/deselected"
385
- },
386
- {
387
- "name": "sort",
388
- "type": "ThSortType",
389
- "description": "Formats the header so that its column will be sortable"
390
- },
391
- {
392
- "name": "stickyLeftOffset",
393
- "type": "string",
394
- "description": "Left offset of a sticky column. This will typically be equal to the combined value set by stickyMinWidth of any sticky columns that precede the current sticky column."
395
- },
396
- {
397
- "name": "stickyMinWidth",
398
- "type": "string",
399
- "description": "Minimum width for a sticky column"
400
- },
401
- {
402
- "name": "stickyRightOffset",
403
- "type": "string",
404
- "description": "Right offset of a sticky column. This will typically be equal to the combined value set by stickyMinWidth of any sticky columns that come after the current sticky column."
405
- },
406
- {
407
- "name": "textCenter",
408
- "type": "boolean",
409
- "description": "Modifies cell to center its contents."
410
- },
411
- {
412
- "name": "tooltip",
413
- "type": "React.ReactNode",
414
- "description": "Tooltip to show on the header cell.\nNote: If the header cell is truncated and has simple string content, it will already attempt to display the header text.\nIf you want to show a tooltip that differs from the header text, you can set it here.\nTo disable it completely you can set it to null."
415
- },
416
- {
417
- "name": "tooltipProps",
418
- "type": "Omit<TooltipProps, 'content'>",
419
- "description": "other props to pass to the tooltip"
420
- },
421
- {
422
- "name": "visibility",
423
- "type": "(keyof IVisibility)[]",
424
- "description": "Visibility breakpoint modifiers"
425
- },
426
- {
427
- "name": "width",
428
- "type": "10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 60 | 70 | 80 | 90 | 100",
429
- "description": "Width percentage modifier"
430
- }
431
- ]
432
- },
433
- {
434
- "name": "Td",
435
- "description": "",
436
- "props": [
437
- {
438
- "name": "actions",
439
- "type": "TdActionsType",
440
- "description": "Turns the cell into an actions cell. Recommended to use an ActionsColumn component as a child of the Td rather than this prop."
441
- },
442
- {
443
- "name": "children",
444
- "type": "React.ReactNode",
445
- "description": "Content rendered inside the cell"
446
- },
447
- {
448
- "name": "className",
449
- "type": "string",
450
- "description": "Additional classes added to the cell"
451
- },
452
- {
453
- "name": "component",
454
- "type": "React.ReactNode",
455
- "description": "Element to render"
456
- },
457
- {
458
- "name": "compoundExpand",
459
- "type": "TdCompoundExpandType",
460
- "description": "Turns the cell into a compound expansion toggle"
461
- },
462
- {
463
- "name": "dataLabel",
464
- "type": "string",
465
- "description": "The column header the cell corresponds to.\nThis attribute replaces table header in mobile viewport. It is rendered by ::before pseudo element."
466
- },
467
- {
468
- "name": "draggableRow",
469
- "type": "TdDraggableType",
470
- "description": "Turns the cell into the first cell in a draggable row"
471
- },
472
- {
473
- "name": "expand",
474
- "type": "TdExpandType",
475
- "description": "Turns the cell into an expansion toggle and determines if the corresponding expansion row is open"
476
- },
477
- {
478
- "name": "favorites",
479
- "type": "TdFavoritesType",
480
- "description": "Turns the cell into a favorites cell with a star button"
481
- },
482
- {
483
- "name": "hasAction",
484
- "type": "boolean",
485
- "description": "Indicates the cell contains an interactive element and prevents that element's padding from increasing row height. Recommended when other cells in the same row contains text."
486
- },
487
- {
488
- "name": "hasLeftBorder",
489
- "type": "boolean",
490
- "description": "Adds a border to the left side of the cell"
491
- },
492
- {
493
- "name": "hasRightBorder",
494
- "type": "boolean",
495
- "description": "Adds a border to the right side of the cell"
496
- },
497
- {
498
- "name": "isActionCell",
499
- "type": "boolean",
500
- "description": "Applies pf-v6-c-table__action to td"
501
- },
502
- {
503
- "name": "isStickyColumn",
504
- "type": "boolean",
505
- "description": "Indicates the column should be sticky"
506
- },
507
- {
508
- "name": "modifier",
509
- "type": "'breakWord' | 'fitContent' | 'nowrap' | 'truncate' | 'wrap'",
510
- "description": "Style modifier to apply"
511
- },
512
- {
513
- "name": "noPadding",
514
- "type": "boolean",
515
- "description": "True to remove padding"
516
- },
517
- {
518
- "name": "onMouseEnter",
519
- "type": "(event: any) => void",
520
- "description": "Callback on mouse enter"
521
- },
522
- {
523
- "name": "select",
524
- "type": "TdSelectType",
525
- "description": "Renders a checkbox or radio select"
526
- },
527
- {
528
- "name": "stickyLeftOffset",
529
- "type": "string",
530
- "description": "Left offset of a sticky column. This will typically be equal to the combined value set by stickyMinWidth of any sticky columns that precede the current sticky column."
531
- },
532
- {
533
- "name": "stickyMinWidth",
534
- "type": "string",
535
- "description": "Minimum width for a sticky column"
536
- },
537
- {
538
- "name": "stickyRightOffset",
539
- "type": "string",
540
- "description": "Right offset of a sticky column. This will typically be equal to the combined value set by stickyMinWidth of any sticky columns that come after the current sticky column."
541
- },
542
- {
543
- "name": "textCenter",
544
- "type": "boolean",
545
- "description": "Modifies cell to center its contents."
546
- },
547
- {
548
- "name": "tooltip",
549
- "type": "React.ReactNode",
550
- "description": "Tooltip to show on the body cell.\nNote: If the body cell is truncated and has simple string content, it will already attempt to display the cell text.\nIf you want to show a tooltip that differs from the cell text, you can set it here.\nTo disable it completely you can set it to null."
551
- },
552
- {
553
- "name": "treeRow",
554
- "type": "TdTreeRowType",
555
- "description": "Turns the cell into the first cell in a tree table row"
556
- },
557
- {
558
- "name": "visibility",
559
- "type": "(keyof IVisibility)[]",
560
- "description": "Visibility breakpoint modifiers"
561
- },
562
- {
563
- "name": "width",
564
- "type": "10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 60 | 70 | 80 | 90 | 100",
565
- "description": "Width percentage modifier"
566
- }
567
- ]
568
- },
569
- {
570
- "name": "Caption",
571
- "description": "",
572
- "props": [
573
- {
574
- "name": "children",
575
- "type": "React.ReactNode",
576
- "description": "Content rendered inside the caption"
577
- },
578
- {
579
- "name": "className",
580
- "type": "string",
581
- "description": "Additional classes added to the caption"
582
- }
583
- ]
584
- },
585
- {
586
- "name": "TableText",
587
- "description": "",
588
- "props": [
589
- {
590
- "name": "children",
591
- "type": "React.ReactNode",
592
- "description": "Content rendered within the table text",
593
- "defaultValue": "null"
594
- },
595
- {
596
- "name": "className",
597
- "type": "string",
598
- "description": "Additional classes added to the table text",
599
- "defaultValue": "''"
600
- },
601
- {
602
- "name": "focused",
603
- "type": "boolean",
604
- "description": "Determines if the TableText is focused by parent component",
605
- "defaultValue": "false"
606
- },
607
- {
608
- "name": "onMouseEnter",
609
- "type": "(event: any) => void",
610
- "description": "callback used to create the tooltip if text is truncated",
611
- "defaultValue": "() => {}"
612
- },
613
- {
614
- "name": "tooltip",
615
- "type": "React.ReactNode",
616
- "description": "text to display on the tooltip",
617
- "defaultValue": "''"
618
- },
619
- {
620
- "name": "tooltipHasDefaultBehavior",
621
- "type": "boolean",
622
- "description": "Determines if tooltip should have normal visibility behavior. If false, the tooltip will only be shown when children is not entirely visible",
623
- "defaultValue": "false"
624
- },
625
- {
626
- "name": "tooltipProps",
627
- "type": "Omit<TooltipProps, 'content'>",
628
- "description": "other props to pass to the tooltip",
629
- "defaultValue": "{}"
630
- },
631
- {
632
- "name": "variant",
633
- "type": "TableTextVariant | 'span' | 'div'",
634
- "description": "Determines which element to render as a table text",
635
- "defaultValue": "'span'"
636
- },
637
- {
638
- "name": "wrapModifier",
639
- "type": "WrapModifier | 'wrap' | 'nowrap' | 'truncate' | 'breakWord' | 'fitContent'",
640
- "description": "Determines which wrapping modifier to apply to the table text",
641
- "defaultValue": "null"
642
- }
643
- ]
644
- },
645
- {
646
- "name": "TdActionsType",
647
- "description": "",
648
- "props": [
649
- {
650
- "name": "actionsToggle",
651
- "type": "(props: CustomActionsToggleProps) => React.ReactNode",
652
- "description": "Custom toggle for the actions menu"
653
- },
654
- {
655
- "name": "dropdownDirection",
656
- "type": "'up' | 'down'",
657
- "description": "Actions dropdown direction"
658
- },
659
- {
660
- "name": "dropdownPosition",
661
- "type": "'right' | 'left'",
662
- "description": "Actions dropdown position"
663
- },
664
- {
665
- "name": "isDisabled",
666
- "type": "boolean",
667
- "description": "Whether the actions are disabled"
668
- },
669
- {
670
- "name": "items",
671
- "type": "IActions",
672
- "description": "Cell actions",
673
- "required": true
674
- },
675
- {
676
- "name": "menuAppendTo",
677
- "type": "HTMLElement | (() => HTMLElement) | 'inline' | 'parent'",
678
- "description": "The container to append the dropdown menu to. Defaults to 'inline'.\nIf your menu is being cut off you can append it to an element higher up the DOM tree.\nSome examples:\nmenuAppendTo=\"parent\"\nmenuAppendTo={() => document.body}\nmenuAppendTo={document.getElementById('target')}"
679
- },
680
- {
681
- "name": "rowIndex",
682
- "type": "number",
683
- "description": "The row index"
684
- }
685
- ]
686
- },
687
- {
688
- "name": "TdSelectType",
689
- "description": "",
690
- "props": [
691
- {
692
- "name": "isDisabled",
693
- "type": "boolean",
694
- "description": "Whether the selection is disabled"
695
- },
696
- {
697
- "name": "isSelected",
698
- "type": "boolean",
699
- "description": "Whether the cell is selected",
700
- "required": true
701
- },
702
- {
703
- "name": "onSelect",
704
- "type": "OnSelect",
705
- "description": "Callback on select"
706
- },
707
- {
708
- "name": "props",
709
- "type": "any",
710
- "description": "Additional props forwarded to select rowData"
711
- },
712
- {
713
- "name": "rowIndex",
714
- "type": "number",
715
- "description": "The row index",
716
- "required": true
717
- },
718
- {
719
- "name": "variant",
720
- "type": "'checkbox' | 'radio'",
721
- "description": "The selectable variant"
722
- }
723
- ]
724
- },
725
- {
726
- "name": "ThSelectType",
727
- "description": "",
728
- "props": [
729
- {
730
- "name": "isDisabled",
731
- "type": "boolean",
732
- "description": "Whether to disable the selection"
733
- },
734
- {
735
- "name": "isHeaderSelectDisabled",
736
- "type": "boolean",
737
- "description": "Flag indicating the select checkbox in the th is disabled"
738
- },
739
- {
740
- "name": "isIndeterminate",
741
- "type": "boolean",
742
- "description": "Whether the select checkbox should be in an indeterminate state (some items selected)"
743
- },
744
- {
745
- "name": "isSelected",
746
- "type": "boolean",
747
- "description": "Whether the cell is selected",
748
- "required": true
749
- },
750
- {
751
- "name": "onSelect",
752
- "type": "OnSelect",
753
- "description": "Callback on select"
754
- },
755
- {
756
- "name": "props",
757
- "type": "any",
758
- "description": "Additional props forwarded to select rowData"
759
- }
760
- ]
761
- },
762
- {
763
- "name": "TdTreeRowType",
764
- "description": "",
765
- "props": [
766
- {
767
- "name": "onCheckChange",
768
- "type": "OnCheckChange",
769
- "description": "(optional) Callback when user changes the checkbox on a row"
770
- },
771
- {
772
- "name": "onCollapse",
773
- "type": "OnTreeRowCollapse",
774
- "description": "Callback when user expands/collapses a row to reveal/hide the row's children",
775
- "required": true
776
- },
777
- {
778
- "name": "onToggleRowDetails",
779
- "type": "OnToggleRowDetails",
780
- "description": "(optional) Callback when user shows/hides the row details in responsive view."
781
- },
782
- {
783
- "name": "props",
784
- "type": "any",
785
- "description": "Additional props forwarded to the title cell of the tree row"
786
- },
787
- {
788
- "name": "rowIndex",
789
- "type": "number",
790
- "description": "The row index"
791
- }
792
- ]
793
- },
794
- {
795
- "name": "ActionsColumn",
796
- "description": "",
797
- "props": [
798
- {
799
- "name": "actionsToggle",
800
- "type": "(props: CustomActionsToggleProps) => React.ReactNode",
801
- "description": "Custom actions toggle for the actions dropdown"
802
- },
803
- {
804
- "name": "extraData",
805
- "type": "IExtraData",
806
- "description": "Extra data of a row"
807
- },
808
- {
809
- "name": "firstActionItemRef",
810
- "type": "React.Ref<HTMLButtonElement>",
811
- "description": "Ref to forward to the first item in the popup menu"
812
- },
813
- {
814
- "name": "isDisabled",
815
- "type": "boolean",
816
- "description": "Indicates whether the actions dropdown is disabled"
817
- },
818
- {
819
- "name": "isOnOpenChangeDisabled",
820
- "type": "boolean",
821
- "description": "Flag indicating that the dropdown's onOpenChange callback should not be called."
822
- },
823
- {
824
- "name": "items",
825
- "type": "IAction[]",
826
- "description": "Actions to be rendered within or without the action dropdown",
827
- "required": true
828
- },
829
- {
830
- "name": "popperProps",
831
- "type": "any",
832
- "description": "Additional properties for the actions dropdown popper"
833
- },
834
- {
835
- "name": "rowData",
836
- "type": "IRowData",
837
- "description": "Data of the row the action dropdown is located"
838
- }
839
- ]
840
- },
841
- {
842
- "name": "TdCompoundExpandType",
843
- "description": "",
844
- "props": [
845
- {
846
- "name": "columnIndex",
847
- "type": "number",
848
- "description": "The column index"
849
- },
850
- {
851
- "name": "expandId",
852
- "type": "string",
853
- "description": "Id prefix for expandable cells *"
854
- },
855
- {
856
- "name": "isExpanded",
857
- "type": "boolean",
858
- "description": "determines if the corresponding expansion row is open",
859
- "required": true
860
- },
861
- {
862
- "name": "onToggle",
863
- "type": "OnExpand",
864
- "description": "Callback on toggling of the expansion"
865
- },
866
- {
867
- "name": "rowIndex",
868
- "type": "number",
869
- "description": "The row index"
870
- }
871
- ]
872
- },
873
- {
874
- "name": "TdFavoritesType",
875
- "description": "",
876
- "props": [
877
- {
878
- "name": "isFavorited",
879
- "type": "boolean",
880
- "description": "Whether the corresponding row is favorited",
881
- "required": true
882
- },
883
- {
884
- "name": "onFavorite",
885
- "type": "OnFavorite",
886
- "description": "Callback on clicking the favorites button"
887
- },
888
- {
889
- "name": "props",
890
- "type": "any",
891
- "description": "Additional props forwarded to the FavoritesCell"
892
- },
893
- {
894
- "name": "rowIndex",
895
- "type": "number",
896
- "description": "The row index"
897
- }
898
- ]
899
- },
900
- {
901
- "name": "TdDraggableType",
902
- "description": "",
903
- "props": [
904
- {
905
- "name": "id",
906
- "type": "string",
907
- "description": "Id of the draggable row",
908
- "required": true
909
- }
910
- ]
911
- },
912
- {
913
- "name": "ThInfoType",
914
- "description": "",
915
- "props": [
916
- {
917
- "name": "ariaLabel",
918
- "type": "string",
919
- "description": null
920
- },
921
- {
922
- "name": "className",
923
- "type": "string",
924
- "description": null
925
- },
926
- {
927
- "name": "popover",
928
- "type": "React.ReactNode",
929
- "description": null
930
- },
931
- {
932
- "name": "popoverProps",
933
- "type": "Omit<PopoverProps, 'bodyContent'>",
934
- "description": null
935
- },
936
- {
937
- "name": "tooltip",
938
- "type": "React.ReactNode",
939
- "description": null
940
- },
941
- {
942
- "name": "tooltipProps",
943
- "type": "Omit<TooltipProps, 'content'>",
944
- "description": null
945
- }
946
- ]
947
- },
948
- {
949
- "name": "TdExpandType",
950
- "description": "",
951
- "props": [
952
- {
953
- "name": "columnIndex",
954
- "type": "number",
955
- "description": "The column index"
956
- },
957
- {
958
- "name": "expandId",
959
- "type": "string",
960
- "description": "Id prefix for expandable rows *"
961
- },
962
- {
963
- "name": "isExpanded",
964
- "type": "boolean",
965
- "description": "Flag indicating the child row associated with this cell is expanded",
966
- "required": true
967
- },
968
- {
969
- "name": "onToggle",
970
- "type": "OnCollapse",
971
- "description": "On toggling the expansion"
972
- },
973
- {
974
- "name": "rowIndex",
975
- "type": "number",
976
- "description": "The row index",
977
- "required": true
978
- }
979
- ]
980
- },
981
- {
982
- "name": "ThExpandType",
983
- "description": "",
984
- "props": [
985
- {
986
- "name": "areAllExpanded",
987
- "type": "boolean",
988
- "description": "Whether all are expanded",
989
- "required": true
990
- },
991
- {
992
- "name": "collapseAllAriaLabel",
993
- "type": "string",
994
- "description": "Alternative aria label",
995
- "required": true
996
- },
997
- {
998
- "name": "onToggle",
999
- "type": "OnCollapse",
1000
- "description": "On toggling the expansion"
1001
- }
1002
- ]
1003
- },
1004
- {
1005
- "name": "EditableSelectInputCell",
1006
- "description": "",
1007
- "props": [
1008
- {
1009
- "name": "cellIndex",
1010
- "type": "number",
1011
- "description": "Cell index of this select input cell",
1012
- "required": true
1013
- },
1014
- {
1015
- "name": "clearSelection",
1016
- "type": "(event: React.MouseEvent, rowIndex: number, cellIndex: number) => void",
1017
- "description": "Event handler which fires when the user clears the selections"
1018
- },
1019
- {
1020
- "name": "isDisabled",
1021
- "type": "boolean",
1022
- "description": "Flag indicating the select input is disabled"
1023
- },
1024
- {
1025
- "name": "isOpen",
1026
- "type": "boolean",
1027
- "description": "Flag indicating the select menu is open",
1028
- "defaultValue": "false"
1029
- },
1030
- {
1031
- "name": "isPlaceholder",
1032
- "type": "boolean",
1033
- "description": "Flag indicating the toggle gets placeholder styles *",
1034
- "defaultValue": "false"
1035
- },
1036
- {
1037
- "name": "onSelect",
1038
- "type": "(\n event: React.MouseEvent | React.ChangeEvent,\n newValue: any | any[],\n rowIndex: number,\n cellIndex: number,\n isPlaceholder?: boolean\n) => void",
1039
- "description": "Event handler which fires when user selects an option in this cell",
1040
- "defaultValue": "() => {}"
1041
- },
1042
- {
1043
- "name": "onToggle",
1044
- "type": "(event: React.MouseEvent | undefined) => void",
1045
- "description": "Event handler which fires when the select toggle is toggled",
1046
- "defaultValue": "() => {}"
1047
- },
1048
- {
1049
- "name": "options",
1050
- "type": "React.ReactElement<any>[]",
1051
- "description": "Options to display in the expandable select menu",
1052
- "defaultValue": "[]"
1053
- },
1054
- {
1055
- "name": "props",
1056
- "type": "EditableSelectInputProps",
1057
- "description": "Props to build the select component",
1058
- "required": true
1059
- },
1060
- {
1061
- "name": "rowIndex",
1062
- "type": "number",
1063
- "description": "Row index of this select input cell",
1064
- "required": true
1065
- },
1066
- {
1067
- "name": "selections",
1068
- "type": "any | any[]",
1069
- "description": "Current selected options to display as the read only value of the table cell",
1070
- "defaultValue": "['']"
1071
- }
1072
- ]
1073
- },
1074
- {
1075
- "name": "EditableTextCell",
1076
- "description": "",
1077
- "props": [
1078
- {
1079
- "name": "cellIndex",
1080
- "type": "number",
1081
- "description": "Cell index of this text cell",
1082
- "required": true
1083
- },
1084
- {
1085
- "name": "handleTextInputChange",
1086
- "type": "(\n newValue: string,\n event: React.FormEvent<HTMLInputElement>,\n rowIndex: number,\n cellIndex: number\n) => void",
1087
- "description": "Event handler which fires when user changes the text in this cell",
1088
- "required": true
1089
- },
1090
- {
1091
- "name": "inputAriaLabel",
1092
- "type": "string",
1093
- "description": "accessible label of the text input",
1094
- "required": true
1095
- },
1096
- {
1097
- "name": "isDisabled",
1098
- "type": "boolean",
1099
- "description": "flag indicating if the text input is disabled",
1100
- "defaultValue": "false"
1101
- },
1102
- {
1103
- "name": "props",
1104
- "type": "EditableTextCellProps",
1105
- "description": "Props to build the input",
1106
- "required": true
1107
- },
1108
- {
1109
- "name": "rowIndex",
1110
- "type": "number",
1111
- "description": "Row index of this text cell",
1112
- "required": true
1113
- },
1114
- {
1115
- "name": "value",
1116
- "type": "string",
1117
- "description": "The current value of the text input",
1118
- "required": true
1119
- }
1120
- ]
1121
- },
1122
- {
1123
- "name": "EditableSelectInputProps",
1124
- "description": "",
1125
- "props": [
1126
- {
1127
- "name": "[key: string]",
1128
- "type": "any",
1129
- "description": "arbitrary data to pass to the internal select component in the editable select input cell",
1130
- "required": true
1131
- },
1132
- {
1133
- "name": "editableSelectProps",
1134
- "type": "SelectProps",
1135
- "description": "Props to be passed down to the select component"
1136
- },
1137
- {
1138
- "name": "isSelectOpen",
1139
- "type": "boolean",
1140
- "description": "Flag controlling isOpen state of select",
1141
- "required": true
1142
- },
1143
- {
1144
- "name": "name",
1145
- "type": "string",
1146
- "description": "Name of the select input",
1147
- "required": true
1148
- },
1149
- {
1150
- "name": "options",
1151
- "type": "React.ReactElement<any>[]",
1152
- "description": "Array of react elements to display in the select menu",
1153
- "required": true
1154
- },
1155
- {
1156
- "name": "selected",
1157
- "type": "any | any[]",
1158
- "description": "Single select option value for single select menus, or array of select option values for multi select. You can also specify isSelected on the SelectOption",
1159
- "required": true
1160
- },
1161
- {
1162
- "name": "value",
1163
- "type": "string | string[]",
1164
- "description": "Value to display in the cell",
1165
- "required": true
1166
- }
1167
- ]
1168
- },
1169
- {
1170
- "name": "EditableTextCellProps",
1171
- "description": "",
1172
- "props": [
1173
- {
1174
- "name": "[key: string]",
1175
- "type": "any",
1176
- "description": "arbitrary data to pass to the internal text input in the editable text cell",
1177
- "required": true
1178
- },
1179
- {
1180
- "name": "name",
1181
- "type": "string",
1182
- "description": "Name of the input",
1183
- "required": true
1184
- },
1185
- {
1186
- "name": "value",
1187
- "type": "string",
1188
- "description": "Value to display in the cell",
1189
- "required": true
1190
- }
1191
- ]
1192
- },
1193
- {
1194
- "name": "ThSortType",
1195
- "description": "",
1196
- "props": [
1197
- {
1198
- "name": "columnIndex",
1199
- "type": "number",
1200
- "description": "The column index",
1201
- "required": true
1202
- },
1203
- {
1204
- "name": "favoriteButtonProps",
1205
- "type": "FavoriteButtonProps",
1206
- "description": "Props for the favorite button (only for favoritable cell)."
1207
- },
1208
- {
1209
- "name": "isFavorites",
1210
- "type": "boolean",
1211
- "description": "True to make this a favoritable sorting cell"
1212
- },
1213
- {
1214
- "name": "onSort",
1215
- "type": "OnSort",
1216
- "description": "Wraps the content in a button and adds a sort icon - Click callback on the sortable cell"
1217
- },
1218
- {
1219
- "name": "sortBy",
1220
- "type": "ISortBy",
1221
- "description": "Provide the currently active column's index and direction",
1222
- "required": true
1223
- },
1224
- {
1225
- "name": "Unknown",
1226
- "type": "string",
1227
- "description": "Adds accessible text to the sort button."
1228
- }
1229
- ]
1230
- },
1231
- {
1232
- "name": "ISortBy",
1233
- "description": "",
1234
- "props": [
1235
- {
1236
- "name": "defaultDirection",
1237
- "type": "'asc' | 'desc'",
1238
- "description": "Defaulting sorting direction. Defaults to \"asc\"."
1239
- },
1240
- {
1241
- "name": "direction",
1242
- "type": "'asc' | 'desc'",
1243
- "description": "Current sort direction"
1244
- },
1245
- {
1246
- "name": "index",
1247
- "type": "number",
1248
- "description": "Index of the current sorted column"
1249
- }
1250
- ]
1251
- },
1252
- {
1253
- "name": "InnerScrollContainer",
1254
- "description": "",
1255
- "props": [
1256
- {
1257
- "name": "children",
1258
- "type": "React.ReactNode",
1259
- "description": "Content rendered inside the inner scroll container"
1260
- },
1261
- {
1262
- "name": "className",
1263
- "type": "string",
1264
- "description": "Additional classes added to the container"
1265
- }
1266
- ]
1267
- },
1268
- {
1269
- "name": "OuterScrollContainer",
1270
- "description": "",
1271
- "props": [
1272
- {
1273
- "name": "children",
1274
- "type": "React.ReactNode",
1275
- "description": "Content rendered inside the outer scroll container"
1276
- },
1277
- {
1278
- "name": "className",
1279
- "type": "string",
1280
- "description": "Additional classes added to the container"
1281
- }
1282
- ]
1283
- }
1284
- ],
1285
- "cssPrefix": [
1286
- "pf-v6-c-table"
1287
- ],
1288
- "examples": [
1289
- "Basic",
1290
- "Plain",
1291
- "Custom row wrapper, header tooltips & popovers",
1292
- "Sortable & wrapping headers",
1293
- "Sortable - custom control",
1294
- "Selectable with checkbox",
1295
- "Selectable with indeterminate state",
1296
- "Selectable radio input",
1297
- "Row click handler, clickable rows",
1298
- "Editable rows",
1299
- "Actions",
1300
- "Actions Overflow",
1301
- "Expandable",
1302
- "Compound expandable",
1303
- "Cell width, breakpoint modifiers",
1304
- "Controlling text",
1305
- "Modifiers with table text",
1306
- "Empty state",
1307
- "Favoritable (implemented with sortable)",
1308
- "Tree table",
1309
- "Flat tree table with no inset",
1310
- "Draggable row table",
1311
- "Sticky column",
1312
- "Multiple left-aligned sticky columns",
1313
- "Multiple right-aligned sticky columns",
1314
- "Dynamic sticky header",
1315
- "Sticky columns and header",
1316
- "Nested column headers",
1317
- "Nested column headers and expandable rows",
1318
- "Expandable with nested table",
1319
- "Nested sticky header",
1320
- "Striped",
1321
- "Striped expandable",
1322
- "Striped multiple tobdy",
1323
- "Striped tr"
1324
- ]
1325
- };
1326
- pageData.liveContext = {
1327
- Fragment,
1328
- isValidElement,
1329
- useCallback,
1330
- useEffect,
1331
- useRef,
1332
- useState,
1333
- useLayoutEffect,
1334
- SearchIcon,
1335
- RhUiCodeIcon,
1336
- RhUiBranchFillIcon,
1337
- CubeIcon,
1338
- LeafIcon,
1339
- FolderIcon,
1340
- FolderOpenIcon,
1341
- RhMicronsSortDownLargeToSmallIcon,
1342
- RhUiBlueprintIcon,
1343
- RhUiEditFillIcon,
1344
- RhUiEllipsisVerticalFillIcon,
1345
- CheckIcon,
1346
- RhMicronsCloseIcon,
1347
- css,
1348
- styles,
1349
- spacing,
1350
- textStyles,
1351
- inlineEditStyles,
1352
- t_global_background_color_secondary_default
1353
- };
1354
- pageData.examples = {
1355
- 'Basic': props =>
1356
- <Example {...pageData} {...props} {...{"code":"import { Fragment, useState } from 'react';\nimport { ToggleGroup, ToggleGroupItem, ToggleGroupItemProps } from '@patternfly/react-core';\nimport { Table, Caption, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string | null;\n prs: string | null;\n workspaces: string;\n lastCommit: string;\n}\n\ntype ExampleType = 'compact' | 'compactBorderless';\n\nexport const TableBasic: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'three', workspaces: 'four', lastCommit: 'five' },\n { name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' },\n { name: 'one - 3', branches: 'two - 3', prs: 'three - 3', workspaces: 'four - 3', lastCommit: 'five - 3' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n // This state is just for the ToggleGroup in this example and isn't necessary for Table usage.\n const [exampleChoice, setExampleChoice] = useState<ExampleType | ''>('');\n const onExampleTypeChange: ToggleGroupItemProps['onChange'] = (event, _isSelected) => {\n const id = event.currentTarget.id;\n // Allow toggling off if clicking the already selected item\n setExampleChoice(exampleChoice === id ? '' : (id as ExampleType));\n };\n\n return (\n <Fragment>\n <ToggleGroup aria-label=\"Default with single selectable\">\n <ToggleGroupItem\n text=\"Compact\"\n buttonId=\"compact\"\n isSelected={exampleChoice === 'compact'}\n onChange={onExampleTypeChange}\n />\n <ToggleGroupItem\n text=\"Compact borderless\"\n buttonId=\"compactBorderless\"\n isSelected={exampleChoice === 'compactBorderless'}\n onChange={onExampleTypeChange}\n />\n </ToggleGroup>\n <Table\n aria-label=\"Simple table\"\n variant={exampleChoice === 'compact' || exampleChoice === 'compactBorderless' ? 'compact' : undefined}\n borders={exampleChoice !== 'compactBorderless'}\n >\n <Caption>Simple table using composable components</Caption>\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo) => (\n <Tr key={repo.name}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </Fragment>\n );\n};\n","title":"Basic","lang":"ts","className":""}}>
1357
-
1358
- </Example>,
1359
- 'Plain': props =>
1360
- <Example {...pageData} {...props} {...{"code":"import { Table, Caption, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string | null;\n prs: string | null;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TablePlain: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'three', workspaces: 'four', lastCommit: 'five' },\n { name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' },\n { name: 'one - 3', branches: 'two - 3', prs: 'three - 3', workspaces: 'four - 3', lastCommit: 'five - 3' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n return (\n <Table aria-label=\"Plain table\" isPlain={true}>\n <Caption>Simple table with plain styling using composable components</Caption>\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo) => (\n <Tr key={repo.name}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Plain","lang":"ts","className":""}}>
1361
-
1362
- </Example>,
1363
- 'Custom row wrapper, header tooltips & popovers': props =>
1364
- <Example {...pageData} {...props} {...{"code":"import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\nimport t_global_background_color_secondary_default from '@patternfly/react-tokens/dist/esm/t_global_background_color_secondary_default';\n\ninterface Repository {\n name: string;\n branches: string | null;\n prs: string | null;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TableMisc: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'three', workspaces: 'four', lastCommit: 'five' },\n { name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' },\n { name: 'one - 3', branches: 'two - 3', prs: 'three - 3', workspaces: 'four - 3', lastCommit: 'five - 3' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n return (\n <Table aria-label=\"Misc table\">\n <Thead noWrap>\n <Tr>\n <Th\n info={{\n tooltip: 'More information about repositories',\n className: 'repositories-info-tip',\n tooltipProps: {\n isContentLeftAligned: true\n }\n }}\n >\n {columnNames.name}\n </Th>\n <Th>{columnNames.branches}</Th>\n <Th\n info={{\n popover: (\n <div>\n More <strong>information</strong> on pull requests\n </div>\n ),\n ariaLabel: 'More information on pull requests',\n popoverProps: {\n headerContent: columnNames.prs,\n footerContent: <a href=\"#\">Click here for even more info</a>\n }\n }}\n >\n {columnNames.prs}\n </Th>\n <Th>{columnNames.workspaces}</Th>\n <Th textCenter>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo, rowIndex) => {\n const isOddRow = (rowIndex + 1) % 2;\n const customStyle = {\n backgroundColor: t_global_background_color_secondary_default.var\n };\n // Some arbitrary logic to demonstrate that cell styles can be based on anything\n const nameColSpan = repo.branches === null && repo.prs === null ? 3 : 1;\n const lastCommitTextCenter = rowIndex !== 2;\n return (\n <Tr\n key={repo.name}\n className={isOddRow ? 'odd-row-class' : 'even-row-class'}\n style={isOddRow ? customStyle : {}}\n >\n <Td dataLabel={columnNames.name} colSpan={nameColSpan}>\n {repo.name}\n </Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}></Td>\n <Td dataLabel={columnNames.workspaces}></Td>\n <Td dataLabel={columnNames.lastCommit} textCenter={lastCommitTextCenter}></Td>\n </Tr>\n );\n })}\n </Tbody>\n </Table>\n );\n};\n","title":"Custom row wrapper, header tooltips & popovers","lang":"ts","className":""}}>
1365
-
1366
- <ul {...{"className":"pf-v6-c-content--ul pf-m-editorial ws-ul "}}>
1367
-
1368
-
1369
-
1370
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1371
- {`If you add the `}
1372
-
1373
- <code {...{"className":"ws-code "}}>
1374
- {`noWrap`}
1375
- </code>
1376
- {` prop to `}
1377
-
1378
- <code {...{"className":"ws-code "}}>
1379
- {`Thead`}
1380
- </code>
1381
- {`, it won't wrap it if there is no space`}
1382
- </li>
1383
-
1384
-
1385
-
1386
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1387
- {`You can add the `}
1388
-
1389
- <code {...{"className":"ws-code "}}>
1390
- {`textCenter`}
1391
- </code>
1392
- {` prop to `}
1393
-
1394
- <code {...{"className":"ws-code "}}>
1395
- {`Th`}
1396
- </code>
1397
- {` or `}
1398
-
1399
- <code {...{"className":"ws-code "}}>
1400
- {`Td`}
1401
- </code>
1402
- {` to center the contents`}
1403
- </li>
1404
-
1405
-
1406
-
1407
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1408
- {`You can pass `}
1409
-
1410
- <code {...{"className":"ws-code "}}>
1411
- {`className`}
1412
- </code>
1413
- {`, `}
1414
-
1415
- <code {...{"className":"ws-code "}}>
1416
- {`style`}
1417
- </code>
1418
- {` and more to `}
1419
-
1420
- <code {...{"className":"ws-code "}}>
1421
- {`Tr`}
1422
- </code>
1423
- </li>
1424
-
1425
-
1426
- </ul>
1427
-
1428
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1429
- {`To add a header tooltip or popover to `}
1430
-
1431
- <code {...{"className":"ws-code "}}>
1432
- {`Th`}
1433
- </code>
1434
- {`, pass a `}
1435
-
1436
- <code {...{"className":"ws-code "}}>
1437
- {`ThInfoType`}
1438
- </code>
1439
- {` object via the `}
1440
-
1441
- <code {...{"className":"ws-code "}}>
1442
- {`info`}
1443
- </code>
1444
- {` prop.`}
1445
- </p>
1446
- </Example>,
1447
- 'Sortable & wrapping headers': props =>
1448
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, ThProps } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TableSortable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },\n { name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'p', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }\n ];\n\n const columnNames = {\n name: 'Repositories table header that goes on for a long time.',\n branches: 'Branches table header that goes on for a long time.',\n prs: 'Pull requests table header that goes on for a long time.',\n workspaces: 'Workspaces table header that goes on for a long time.',\n lastCommit: 'Last commit table header that goes on for a long time.'\n };\n\n // Index of the currently sorted column\n // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key\n // as the identifier of the sorted column. See the \"Compound expandable\" example.\n const [activeSortIndex, setActiveSortIndex] = useState<number | null>(null);\n\n // Sort direction of the currently sorted column\n const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null);\n\n // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.\n // This example is trivial since our data objects just contain strings, but if the data was more complex\n // this would be a place to return simplified string or number versions of each column to sort by.\n const getSortableRowValues = (repo: Repository): (string | number)[] => {\n const { name, branches, prs, workspaces, lastCommit } = repo;\n return [name, branches, prs, workspaces, lastCommit];\n };\n\n // Note that we perform the sort as part of the component's render logic and not in onSort.\n // We shouldn't store the list of data in state because we don't want to have to sync that with props.\n let sortedRepositories = repositories;\n if (activeSortIndex !== null) {\n sortedRepositories = repositories.sort((a, b) => {\n const aValue = getSortableRowValues(a)[activeSortIndex];\n const bValue = getSortableRowValues(b)[activeSortIndex];\n if (typeof aValue === 'number') {\n // Numeric sort\n if (activeSortDirection === 'asc') {\n return (aValue as number) - (bValue as number);\n }\n return (bValue as number) - (aValue as number);\n } else {\n // String sort\n if (activeSortDirection === 'asc') {\n return (aValue as string).localeCompare(bValue as string);\n }\n return (bValue as string).localeCompare(aValue as string);\n }\n });\n }\n\n const getSortParams = (columnIndex: number): ThProps['sort'] => ({\n sortBy: {\n index: activeSortIndex,\n direction: activeSortDirection,\n defaultDirection: 'asc' // starting sort direction when first sorting a column. Defaults to 'asc'\n },\n onSort: (_event, index, direction) => {\n setActiveSortIndex(index);\n setActiveSortDirection(direction);\n },\n columnIndex\n });\n\n // In this example, we wrap all but the 1st column and make the 1st and 3rd columns sortable just to demonstrate.\n return (\n <Table aria-label=\"Sortable table\" ouiaId=\"SortableTable\">\n <Thead>\n <Tr>\n <Th sort={getSortParams(0)}>{columnNames.name}</Th>\n <Th modifier=\"wrap\">{columnNames.branches}</Th>\n <Th modifier=\"wrap\" sort={getSortParams(2)} info={{ tooltip: 'More information ' }}>\n {columnNames.prs}\n </Th>\n <Th modifier=\"wrap\">{columnNames.workspaces}</Th>\n <Th modifier=\"wrap\">{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {sortedRepositories.map((repo, rowIndex) => (\n <Tr key={rowIndex}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Sortable & wrapping headers","lang":"ts","className":""}}>
1449
-
1450
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1451
- {`To make a column sortable, pass a `}
1452
-
1453
- <code {...{"className":"ws-code "}}>
1454
- {`ThSortType`}
1455
- </code>
1456
- {` object via the `}
1457
-
1458
- <code {...{"className":"ws-code "}}>
1459
- {`sort`}
1460
- </code>
1461
- {` prop on a column's `}
1462
-
1463
- <code {...{"className":"ws-code "}}>
1464
- {`Th`}
1465
- </code>
1466
- {`.`}
1467
- </p>
1468
-
1469
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1470
-
1471
- <code {...{"className":"ws-code "}}>
1472
- {`ThSortType`}
1473
- </code>
1474
- {` includes an `}
1475
-
1476
- <code {...{"className":"ws-code "}}>
1477
- {`OnSort`}
1478
- </code>
1479
- {` event handler which has the following signature:`}
1480
- </p>
1481
-
1482
- <Example {...{"code":"type OnSort = (\n event: React.MouseEvent,\n columnIndex: number,\n sortByDirection: SortByDirection,\n extraData: IExtraColumnData\n) => void;","className":""}}>
1483
-
1484
- </Example>
1485
-
1486
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1487
- {`The built in display for sorting is not fully responsive, as the column headers will be displayed per row when the screen size is small. To see a full page demo of a responsive sortable table, utilizing a toolbar item to control sorting for small screens, view the `}
1488
-
1489
- <code {...{"className":"ws-code "}}>
1490
- {`Sortable - responsive`}
1491
- </code>
1492
- {` demo in the `}
1493
-
1494
- <code {...{"className":"ws-code "}}>
1495
- {`React demos`}
1496
- </code>
1497
- {` tab.`}
1498
- </p>
1499
- </Example>,
1500
- 'Sortable - custom control': props =>
1501
- <Example {...pageData} {...props} {...{"code":"import { Fragment, useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, ThProps } from '@patternfly/react-table';\nimport {\n Toolbar,\n ToolbarContent,\n ToolbarItem,\n Select,\n SelectGroup,\n SelectList,\n SelectOption,\n MenuToggle,\n MenuToggleElement\n} from '@patternfly/react-core';\nimport RhMicronsSortDownLargeToSmallIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-sort-down-large-to-small-icon';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TableSortableCustom: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },\n { name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'p', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n const [isSortDropdownOpen, setIsSortDropdownOpen] = useState(false);\n\n // Index of the currently sorted column\n // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key\n // as the identifier of the sorted column. See the \"Compound expandable\" example.\n const [activeSortIndex, setActiveSortIndex] = useState<number | null>(null);\n\n // Sort direction of the currently sorted column\n const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null);\n\n // Sort dropdown expansion\n // const [isSortDropdownOpen, setIsSortDropdownOpen] = useState(false);\n\n // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.\n // This example is trivial since our data objects just contain strings, but if the data was more complex\n // this would be a place to return simplified string or number versions of each column to sort by.\n const getSortableRowValues = (repo: Repository): (string | number)[] => {\n const { name, branches, prs, workspaces, lastCommit } = repo;\n return [name, branches, prs, workspaces, lastCommit];\n };\n\n // Note that we perform the sort as part of the component's render logic and not in onSort.\n // We shouldn't store the list of data in state because we don't want to have to sync that with props.\n let sortedRepositories = repositories;\n if (activeSortIndex !== null) {\n sortedRepositories = repositories.sort((a, b) => {\n const aValue = getSortableRowValues(a)[activeSortIndex];\n const bValue = getSortableRowValues(b)[activeSortIndex];\n if (typeof aValue === 'number') {\n // Numeric sort\n if (activeSortDirection === 'asc') {\n return (aValue as number) - (bValue as number);\n }\n return (bValue as number) - (aValue as number);\n } else {\n // String sort\n if (activeSortDirection === 'asc') {\n return (aValue as string).localeCompare(bValue as string);\n }\n return (bValue as string).localeCompare(aValue as string);\n }\n });\n }\n\n const getSortParams = (columnIndex: number): ThProps['sort'] => ({\n sortBy: {\n index: activeSortIndex,\n direction: activeSortDirection\n },\n onSort: (_event, index, direction) => {\n setActiveSortIndex(index);\n setActiveSortDirection(direction as 'desc' | 'asc');\n },\n columnIndex\n });\n\n return (\n <Fragment>\n <Toolbar id=\"toolbar\">\n <ToolbarContent>\n <ToolbarItem>\n <Select\n isOpen={isSortDropdownOpen}\n selected={[activeSortDirection, activeSortIndex]}\n onOpenChange={(isOpen) => setIsSortDropdownOpen(isOpen)}\n onSelect={(event, value) => {\n if (value === 'asc' || value === 'desc') {\n setActiveSortDirection(value as 'desc' | 'asc');\n } else {\n setActiveSortIndex(value as number);\n setActiveSortDirection(activeSortDirection !== null ? activeSortDirection : 'asc');\n }\n }}\n toggle={(toggleRef: React.Ref<MenuToggleElement>) => (\n <MenuToggle\n ref={toggleRef}\n onClick={() => setIsSortDropdownOpen(!isSortDropdownOpen)}\n isExpanded={isSortDropdownOpen}\n variant=\"plain\"\n aria-label=\"Sort columns\"\n icon={<RhMicronsSortDownLargeToSmallIcon />}\n />\n )}\n >\n <SelectGroup label=\"Sort column\">\n <SelectList>\n {Object.values(columnNames).map((column, columnIndex) => (\n <SelectOption key={column} value={columnIndex} isSelected={activeSortIndex === columnIndex}>\n {column}\n </SelectOption>\n ))}\n </SelectList>\n </SelectGroup>\n <SelectGroup label=\"Sort direction\">\n <SelectList>\n <SelectOption isSelected={activeSortDirection === 'asc'} value=\"asc\" key=\"ascending\">\n Ascending\n </SelectOption>\n <SelectOption isSelected={activeSortDirection === 'desc'} value=\"desc\" key=\"descending\">\n Descending\n </SelectOption>\n </SelectList>\n </SelectGroup>\n </Select>\n </ToolbarItem>\n </ToolbarContent>\n </Toolbar>\n <Table aria-label=\"Sortable table custom toolbar\">\n <Thead>\n <Tr>\n <Th sort={getSortParams(0)}>{columnNames.name}</Th>\n <Th sort={getSortParams(1)}>{columnNames.branches}</Th>\n <Th sort={getSortParams(2)} info={{ tooltip: 'More information ' }}>\n {columnNames.prs}\n </Th>\n <Th sort={getSortParams(3)}>{columnNames.workspaces}</Th>\n <Th sort={getSortParams(4)}>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {sortedRepositories.map((repo, rowIndex) => (\n <Tr key={rowIndex}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </Fragment>\n );\n};\n","title":"Sortable - custom control","lang":"ts","className":""}}>
1502
-
1503
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1504
- {`Sorting a table may also be controlled manually with a toolbar control. To see a full page demo of a responsive table, view the `}
1505
-
1506
- <code {...{"className":"ws-code "}}>
1507
- {`Sortable - responsive`}
1508
- </code>
1509
- {` demo in the `}
1510
-
1511
- <code {...{"className":"ws-code "}}>
1512
- {`React demos`}
1513
- </code>
1514
- {` tab.`}
1515
- </p>
1516
- </Example>,
1517
- 'Selectable with checkbox': props =>
1518
- <Example {...pageData} {...props} {...{"code":"import { useEffect, useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TableSelectable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },\n { name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'b', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'c', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'd', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'e', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n const isRepoSelectable = (repo: Repository) => repo.name !== 'a'; // Arbitrary logic for this example\n const selectableRepos = repositories.filter(isRepoSelectable);\n\n // In this example, selected rows are tracked by the repo names from each row. This could be any unique identifier.\n // This is to prevent state from being based on row order index in case we later add sorting.\n const [selectedRepoNames, setSelectedRepoNames] = useState<string[]>([]);\n const setRepoSelected = (repo: Repository, isSelecting = true) =>\n setSelectedRepoNames((prevSelected) => {\n const otherSelectedRepoNames = prevSelected.filter((r) => r !== repo.name);\n return isSelecting && isRepoSelectable(repo) ? [...otherSelectedRepoNames, repo.name] : otherSelectedRepoNames;\n });\n const selectAllRepos = (isSelecting = true) =>\n setSelectedRepoNames(isSelecting ? selectableRepos.map((r) => r.name) : []);\n const areAllReposSelected = selectedRepoNames.length === selectableRepos.length;\n const isRepoSelected = (repo: Repository) => selectedRepoNames.includes(repo.name);\n\n // To allow shift+click to select/deselect multiple rows\n const [recentSelectedRowIndex, setRecentSelectedRowIndex] = useState<number | null>(null);\n const [shifting, setShifting] = useState(false);\n\n const onSelectRepo = (repo: Repository, rowIndex: number, isSelecting: boolean) => {\n // If the user is shift + selecting the checkboxes, then all intermediate checkboxes should be selected\n if (shifting && recentSelectedRowIndex !== null) {\n const numberSelected = rowIndex - recentSelectedRowIndex;\n const intermediateIndexes =\n numberSelected > 0\n ? Array.from(new Array(numberSelected + 1), (_x, i) => i + recentSelectedRowIndex)\n : Array.from(new Array(Math.abs(numberSelected) + 1), (_x, i) => i + rowIndex);\n intermediateIndexes.forEach((index) => setRepoSelected(repositories[index], isSelecting));\n } else {\n setRepoSelected(repo, isSelecting);\n }\n setRecentSelectedRowIndex(rowIndex);\n };\n\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Shift') {\n setShifting(true);\n }\n };\n const onKeyUp = (e: KeyboardEvent) => {\n if (e.key === 'Shift') {\n setShifting(false);\n }\n };\n\n document.addEventListener('keydown', onKeyDown);\n document.addEventListener('keyup', onKeyUp);\n\n return () => {\n document.removeEventListener('keydown', onKeyDown);\n document.removeEventListener('keyup', onKeyUp);\n };\n }, []);\n\n return (\n <Table aria-label=\"Selectable table\">\n <Thead>\n <Tr>\n <Th\n select={{\n onSelect: (_event, isSelecting) => selectAllRepos(isSelecting),\n isSelected: areAllReposSelected\n }}\n aria-label=\"Row select\"\n />\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo, rowIndex) => (\n <Tr key={repo.name}>\n <Td\n select={{\n rowIndex,\n onSelect: (_event, isSelecting) => onSelectRepo(repo, rowIndex, isSelecting),\n isSelected: isRepoSelected(repo),\n isDisabled: !isRepoSelectable(repo)\n }}\n />\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Selectable with checkbox","lang":"ts","className":""}}>
1519
-
1520
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1521
- {`To make a row selectable, the table needs a selection column.
1522
- The selection column is just another column, but with selection specific props added.
1523
- We add it as the first header cell and also as the first body cell for each row.`}
1524
- </p>
1525
-
1526
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1527
- {`To make a column sortable, pass a `}
1528
-
1529
- <code {...{"className":"ws-code "}}>
1530
- {`ThSelectType`}
1531
- </code>
1532
- {` object via the `}
1533
-
1534
- <code {...{"className":"ws-code "}}>
1535
- {`select`}
1536
- </code>
1537
- {` prop on a column's `}
1538
-
1539
- <code {...{"className":"ws-code "}}>
1540
- {`Th`}
1541
- </code>
1542
- {`.`}
1543
- </p>
1544
-
1545
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1546
- {`To make a row sortable, pass a `}
1547
-
1548
- <code {...{"className":"ws-code "}}>
1549
- {`TdSelectType`}
1550
- </code>
1551
- {` object via the `}
1552
-
1553
- <code {...{"className":"ws-code "}}>
1554
- {`select`}
1555
- </code>
1556
- {` prop on each rows's first `}
1557
-
1558
- <code {...{"className":"ws-code "}}>
1559
- {`Td`}
1560
- </code>
1561
- {`.`}
1562
- </p>
1563
-
1564
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1565
- {`Both the `}
1566
-
1567
- <code {...{"className":"ws-code "}}>
1568
- {`TdSelectType`}
1569
- </code>
1570
- {` and the `}
1571
-
1572
- <code {...{"className":"ws-code "}}>
1573
- {`ThSelectType`}
1574
- </code>
1575
- {` expect an `}
1576
-
1577
- <code {...{"className":"ws-code "}}>
1578
- {`OnSelect`}
1579
- </code>
1580
- {` event handler with the following signature:`}
1581
- </p>
1582
-
1583
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1584
-
1585
- <code {...{"className":"ws-code "}}>
1586
- {`OnSelect:`}
1587
- </code>
1588
- </p>
1589
-
1590
- <Example {...{"code":"type OnSelect = (\n event: React.FormEvent<HTMLInputElement>,\n isSelected: boolean,\n rowIndex: number,\n rowData: IRowData,\n extraData: IExtraData\n) => void;","className":""}}>
1591
-
1592
- </Example>
1593
-
1594
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1595
-
1596
- <strong {...{"className":""}}>
1597
- {`Note:`}
1598
- </strong>
1599
- {` This example has a `}
1600
-
1601
- <code {...{"className":"ws-code "}}>
1602
- {`shift + select`}
1603
- </code>
1604
- {` feature where holding shift while
1605
- checking checkboxes will check intermediate rows' checkboxes.`}
1606
- </p>
1607
- </Example>,
1608
- 'Selectable with indeterminate state': props =>
1609
- <Example {...pageData} {...props} {...{"code":"import { useEffect, useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TableSelectableIndeterminate: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },\n { name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'b', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'c', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'd', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'e', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n const isRepoSelectable = (repo: Repository) => repo.name !== 'a'; // Arbitrary logic for this example\n const selectableRepos = repositories.filter(isRepoSelectable);\n\n // In this example, selected rows are tracked by the repo names from each row. This could be any unique identifier.\n // This is to prevent state from being based on row order index in case we later add sorting.\n const [selectedRepoNames, setSelectedRepoNames] = useState<string[]>([]);\n const setRepoSelected = (repo: Repository, isSelecting = true) =>\n setSelectedRepoNames((prevSelected) => {\n const otherSelectedRepoNames = prevSelected.filter((r) => r !== repo.name);\n return isSelecting && isRepoSelectable(repo) ? [...otherSelectedRepoNames, repo.name] : otherSelectedRepoNames;\n });\n const selectAllRepos = (isSelecting = true) =>\n setSelectedRepoNames(isSelecting ? selectableRepos.map((r) => r.name) : []);\n const areAllReposSelected = selectedRepoNames.length === selectableRepos.length;\n const areSomeReposSelected = selectedRepoNames.length > 0 && selectedRepoNames.length < selectableRepos.length;\n const isRepoSelected = (repo: Repository) => selectedRepoNames.includes(repo.name);\n\n // To allow shift+click to select/deselect multiple rows\n const [recentSelectedRowIndex, setRecentSelectedRowIndex] = useState<number | null>(null);\n const [shifting, setShifting] = useState(false);\n\n const onSelectRepo = (repo: Repository, rowIndex: number, isSelecting: boolean) => {\n // If the user is shift + selecting the checkboxes, then all intermediate checkboxes should be selected\n if (shifting && recentSelectedRowIndex !== null) {\n const numberSelected = rowIndex - recentSelectedRowIndex;\n const intermediateIndexes =\n numberSelected > 0\n ? Array.from(new Array(numberSelected + 1), (_x, i) => i + recentSelectedRowIndex)\n : Array.from(new Array(Math.abs(numberSelected) + 1), (_x, i) => i + rowIndex);\n intermediateIndexes.forEach((index) => setRepoSelected(repositories[index], isSelecting));\n } else {\n setRepoSelected(repo, isSelecting);\n }\n setRecentSelectedRowIndex(rowIndex);\n };\n\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Shift') {\n setShifting(true);\n }\n };\n const onKeyUp = (e: KeyboardEvent) => {\n if (e.key === 'Shift') {\n setShifting(false);\n }\n };\n\n document.addEventListener('keydown', onKeyDown);\n document.addEventListener('keyup', onKeyUp);\n\n return () => {\n document.removeEventListener('keydown', onKeyDown);\n document.removeEventListener('keyup', onKeyUp);\n };\n }, []);\n\n return (\n <Table aria-label=\"Selectable table with indeterminate state\">\n <Thead>\n <Tr>\n <Th\n select={{\n onSelect: (_event, isSelecting) => selectAllRepos(isSelecting),\n isSelected: areAllReposSelected,\n isIndeterminate: areSomeReposSelected\n }}\n aria-label=\"Row select\"\n />\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo, rowIndex) => (\n <Tr key={repo.name}>\n <Td\n select={{\n rowIndex,\n onSelect: (_event, isSelecting) => onSelectRepo(repo, rowIndex, isSelecting),\n isSelected: isRepoSelected(repo),\n isDisabled: !isRepoSelectable(repo)\n }}\n />\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Selectable with indeterminate state","lang":"ts","className":""}}>
1610
-
1611
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1612
- {`To indicate partial selection, use `}
1613
-
1614
- <code {...{"className":"ws-code "}}>
1615
- {`isIndeterminate`}
1616
- </code>
1617
- {` on the header's `}
1618
-
1619
- <code {...{"className":"ws-code "}}>
1620
- {`select`}
1621
- </code>
1622
- {` prop. When some (but not all) selectable rows are selected, the header checkbox will convey this information to assistive technologies and also display a dash instead of a checkmark.`}
1623
- </p>
1624
- </Example>,
1625
- 'Selectable radio input': props =>
1626
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TableSelectableRadio: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },\n { name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'p', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n const isRepoSelectable = (repo: Repository) => repo.name !== 'a'; // Arbitrary logic for this example\n\n // In this example, selected rows are tracked by the repo names from each row. This could be any unique identifier.\n // This is to prevent state from being based on row order index in case we later add sorting.\n const [selectedRepoName, setSelectedRepoName] = useState<string | null>(null);\n\n return (\n <Table aria-label=\"Selectable table\">\n <Thead>\n <Tr>\n <Th screenReaderText=\"Row select\" />\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo, rowIndex) => (\n <Tr key={repo.name}>\n <Td\n select={{\n rowIndex,\n onSelect: () => setSelectedRepoName(repo.name),\n isSelected: selectedRepoName === repo.name,\n isDisabled: !isRepoSelectable(repo),\n variant: 'radio'\n }}\n />\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Selectable radio input","lang":"ts","className":""}}>
1627
-
1628
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1629
- {`Similarly to the selectable example above, the radio buttons use the first column. The first header cell is empty, and each body row's first cell has radio button props.`}
1630
- </p>
1631
- </Example>,
1632
- 'Row click handler, clickable rows': props =>
1633
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string | null;\n prs: string | null;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TableClickable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'three', workspaces: 'four', lastCommit: 'five' },\n { name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' },\n { name: 'one - 3', branches: 'two - 3', prs: 'three - 3', workspaces: 'four - 3', lastCommit: 'five - 3' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n // In this example, selected rows are tracked by the repo names from each row. This could be any unique identifier.\n // This is to prevent state from being based on row order index in case we later add sorting.\n const [selectedRepoName, setSelectedRepoName] = useState('');\n\n return (\n <Table aria-label=\"Clickable table\">\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo) => (\n <Tr\n key={repo.name}\n onRowClick={() => setSelectedRepoName(repo.name)}\n isSelectable\n isClickable\n isRowSelected={selectedRepoName === repo.name}\n >\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Row click handler, clickable rows","lang":"ts","className":""}}>
1634
-
1635
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1636
- {`This selectable rows feature is intended for use when a table is used to present a list of objects in a Primary-detail view.`}
1637
- </p>
1638
- </Example>,
1639
- 'Editable rows': props =>
1640
- <Example {...pageData} {...props} {...{"code":"import { isValidElement, useRef, useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\nimport { Button, Checkbox, Radio, TextInput, KeyTypes, getUniqueId } from '@patternfly/react-core';\nimport RhUiEditFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-edit-fill-icon';\nimport CheckIcon from '@patternfly/react-icons/dist/esm/icons/check-icon';\nimport RhMicronsCloseIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-close-icon';\nimport inlineEditStyles from '@patternfly/react-styles/css/components/InlineEdit/inline-edit';\nimport { css } from '@patternfly/react-styles';\n\ninterface EditButtonsCellProps {\n onClick: (type: 'save' | 'cancel' | 'edit') => void;\n elementToFocusOnEditRef?: React.MutableRefObject<HTMLElement>;\n rowAriaLabel: string;\n}\n\nconst EditButtonsCell: React.FunctionComponent<EditButtonsCellProps> = ({\n onClick,\n elementToFocusOnEditRef,\n rowAriaLabel = 'row'\n}) => {\n const editButtonRef = useRef<HTMLButtonElement>(undefined);\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, button: 'edit' | 'stopEditing') => {\n const focusRef = button === 'edit' ? elementToFocusOnEditRef : editButtonRef;\n\n if (event.key === KeyTypes.Enter || event.key === KeyTypes.Space) {\n // because space key triggers click event before keyDown, we have to prevent default behaviour and trigger click manually\n event.preventDefault();\n (event.target as HTMLButtonElement).click();\n setTimeout(() => {\n focusRef?.current?.focus();\n }, 0);\n }\n };\n\n return (\n <>\n <Td\n dataLabel=\"Save and cancel buttons\"\n className={css(\n inlineEditStyles.inlineEditGroup,\n inlineEditStyles.modifiers.iconGroup,\n inlineEditStyles.modifiers.actionGroup\n )}\n >\n <div className={css(inlineEditStyles.inlineEditAction, inlineEditStyles.modifiers.valid)}>\n <Button\n aria-label={`Save edits of ${rowAriaLabel}`}\n onClick={() => onClick('save')}\n onKeyDown={(event) => onKeyDown(event, 'stopEditing')}\n variant=\"plain\"\n >\n <CheckIcon />\n </Button>\n </div>\n <div className={css(inlineEditStyles.inlineEditAction)}>\n <Button\n aria-label={`Discard edits of ${rowAriaLabel}`}\n onClick={() => onClick('cancel')}\n onKeyDown={(event) => onKeyDown(event, 'stopEditing')}\n variant=\"plain\"\n >\n <RhMicronsCloseIcon />\n </Button>\n </div>\n </Td>\n <Td\n dataLabel=\"Edit button\"\n className={css(inlineEditStyles.inlineEditAction, inlineEditStyles.modifiers.enableEditable)}\n >\n <Button\n ref={editButtonRef}\n aria-label={`Edit ${rowAriaLabel}`}\n onClick={() => onClick('edit')}\n onKeyDown={(event) => onKeyDown(event, 'edit')}\n variant=\"plain\"\n >\n <RhUiEditFillIcon />\n </Button>\n </Td>\n </>\n );\n};\n\ninterface EditableCellProps {\n dataLabel: string;\n staticValue: React.ReactNode;\n editingValue: React.ReactNode;\n role?: string;\n ariaLabel?: string;\n}\n\nconst EditableCell: React.FunctionComponent<EditableCellProps> = ({\n dataLabel,\n staticValue,\n editingValue,\n role,\n ariaLabel\n}) => {\n const hasMultipleInputs = Array.isArray(editingValue) && editingValue.every((elem) => isValidElement(elem));\n\n return (\n <Td dataLabel={dataLabel}>\n <div className={css(inlineEditStyles.inlineEditValue)}>{staticValue}</div>\n {hasMultipleInputs ? (\n <div className={css(inlineEditStyles.inlineEditGroup, 'pf-m-column')} role={role} aria-label={ariaLabel}>\n {(editingValue as React.ReactElement<any>[]).map((elem, index) => (\n <div key={index} className={css(inlineEditStyles.inlineEditInput)}>\n {elem}\n </div>\n ))}\n </div>\n ) : (\n <div className={css(inlineEditStyles.inlineEditInput)}>{editingValue}</div>\n )}\n </Td>\n );\n};\n\ninterface EditableRow {\n data: CustomData;\n columnNames: ColumnNames<CustomData>;\n dataOptions?: CustomDataOptions;\n saveChanges: (editedData: CustomData) => void;\n ariaLabel: string;\n rowIndex?: number;\n}\n\nconst EditableRow: React.FunctionComponent<EditableRow> = ({\n data,\n columnNames,\n dataOptions,\n saveChanges,\n ariaLabel,\n rowIndex\n}) => {\n const [editable, setEditable] = useState(false);\n const [editedData, setEditedData] = useState(data);\n\n const inputRef = useRef(undefined);\n\n return (\n <Tr className={css(inlineEditStyles.inlineEdit, editable ? inlineEditStyles.modifiers.inlineEditable : '')}>\n <EditableCell\n dataLabel={columnNames.textInput}\n staticValue={data.textInput}\n editingValue={\n <TextInput\n aria-label={`${columnNames.textInput} ${ariaLabel}`}\n ref={inputRef}\n value={editedData.textInput}\n onChange={(e) => setEditedData((data) => ({ ...data, textInput: (e.target as HTMLInputElement).value }))}\n />\n }\n />\n <EditableCell\n dataLabel={columnNames.textInputDisabled}\n staticValue={data.textInputDisabled}\n editingValue={\n <TextInput\n aria-label={`${columnNames.textInputDisabled} ${ariaLabel}`}\n isDisabled={true}\n value={editedData.textInputDisabled ?? ''}\n />\n }\n />\n <EditableCell\n dataLabel={columnNames.checkboxes}\n staticValue={data.checkboxes.join(', ')}\n role=\"group\"\n ariaLabel={`Checkbox group row ${rowIndex}`}\n editingValue={dataOptions?.checkboxes.map((option) => {\n const id = getUniqueId('checkbox');\n return (\n <Checkbox\n key={id}\n name={id}\n id={id}\n label={option}\n isChecked={editedData.checkboxes.includes(option)}\n onChange={(_e, checked) =>\n setEditedData((data) => ({\n ...data,\n checkboxes: checked ? [...data.checkboxes, option] : data.checkboxes.filter((item) => item !== option)\n }))\n }\n />\n );\n })}\n />\n <EditableCell\n dataLabel={columnNames.radios}\n staticValue={data.radios}\n ariaLabel={`Radio button group row ${rowIndex}`}\n role=\"radiogroup\"\n editingValue={dataOptions?.radios.map((option) => {\n const id = getUniqueId('radio');\n return (\n <Radio\n key={id}\n name={id}\n id={id}\n label={option}\n isChecked={editedData.radios === option}\n onChange={() => setEditedData((data) => ({ ...data, radios: option }))}\n />\n );\n })}\n />\n <EditButtonsCell\n onClick={(type) => {\n type === 'edit' ? setEditable(true) : setEditable(false);\n type === 'save' && saveChanges(editedData);\n type === 'cancel' && setEditedData(data);\n }}\n rowAriaLabel={ariaLabel}\n elementToFocusOnEditRef={inputRef}\n />\n </Tr>\n );\n};\n\ninterface CustomData {\n textInput: string;\n textInputDisabled: string | null;\n checkboxes: string[];\n radios: string;\n}\n\ninterface CustomDataOptions {\n checkboxes: string[];\n radios: string[];\n}\n\ntype ColumnNames<T> = { [K in keyof T]: string };\n\nexport const TableEditable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const initialRows: CustomData[] = [\n {\n textInput: 'Editable text 1',\n textInputDisabled: 'Non-editable text 1',\n checkboxes: ['Option A'],\n radios: 'Option A'\n },\n {\n textInput: 'Editable text 2',\n textInputDisabled: null,\n checkboxes: [],\n radios: 'Option B'\n },\n {\n textInput: 'Editable text 3',\n textInputDisabled: 'Non-editable text 3',\n checkboxes: ['Option A', 'Option B'],\n radios: 'Option A'\n }\n ];\n\n // List of all selectable options for some cells of initialRows\n const initialRowsOptions: CustomDataOptions[] = [\n {\n checkboxes: ['Option A', 'Option B', 'Option C'],\n radios: ['Option A', 'Option B']\n },\n {\n checkboxes: ['Option A', 'Option B'],\n radios: ['Option A', 'Option B', 'Option C']\n },\n {\n checkboxes: ['Option A', 'Option B'],\n radios: ['Option A', 'Option B']\n }\n ];\n\n const [rows, setRows] = useState(initialRows);\n\n const columnNames: ColumnNames<CustomData> = {\n textInput: 'Text input',\n textInputDisabled: 'Disabled text input',\n checkboxes: 'Checkboxes',\n radios: 'Radios'\n };\n\n return (\n <Table aria-label=\"Editable table\">\n <Thead>\n <Tr>\n <Th>{columnNames.textInput}</Th>\n <Th>{columnNames.textInputDisabled}</Th>\n <Th>{columnNames.checkboxes}</Th>\n <Th>{columnNames.radios}</Th>\n <Th screenReaderText=\"Row edit actions\" />\n </Tr>\n </Thead>\n <Tbody>\n {rows.map((data, index) => (\n <EditableRow\n key={index}\n data={data}\n rowIndex={index}\n dataOptions={initialRowsOptions[index]}\n columnNames={columnNames}\n saveChanges={(editedRow) => {\n setRows((rows) => rows.map((row, i) => (i === index ? editedRow : row)));\n }}\n ariaLabel={`row ${index + 1}`}\n ></EditableRow>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Editable rows","lang":"ts","className":""}}>
1641
-
1642
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1643
- {`This example shows a table with editable rows. Cells in a row can be edited after clicking on the edit icon.`}
1644
- </p>
1645
- </Example>,
1646
- 'Actions': props =>
1647
- <Example {...pageData} {...props} {...{"code":"/* eslint-disable no-console */\nimport { Fragment, useState } from 'react';\nimport { Button, MenuToggle, ToggleGroup, ToggleGroupItem, ToggleGroupItemProps } from '@patternfly/react-core';\nimport {\n Table,\n TableText,\n Thead,\n Tr,\n Th,\n Tbody,\n Td,\n CustomActionsToggleProps,\n ActionsColumn,\n IAction\n} from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n singleAction: string;\n}\n\ntype ExampleType = 'customToggle';\n\nexport const TableActions: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five', singleAction: 'Start' },\n { name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five', singleAction: '' },\n { name: 'p', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five', singleAction: 'Start' },\n { name: '4', branches: '2', prs: 'b', workspaces: 'four', lastCommit: 'five', singleAction: 'Start' },\n { name: '5', branches: '2', prs: 'b', workspaces: 'four', lastCommit: 'five', singleAction: 'Start' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit',\n singleAction: 'Single action'\n };\n\n // This state is just for the ToggleGroup in this example and isn't necessary for Table usage.\n const [exampleChoice, setExampleChoice] = useState<ExampleType | ''>('');\n const onExampleTypeChange: ToggleGroupItemProps['onChange'] = (event, _isSelected) => {\n const id = event.currentTarget.id;\n // Allow toggling off if clicking the already selected item\n setExampleChoice(exampleChoice === id ? '' : (id as ExampleType));\n };\n\n const customActionsToggle = (props: CustomActionsToggleProps) => (\n <MenuToggle ref={props.toggleRef} onClick={props.onToggle} isDisabled={props.isDisabled}>\n Actions\n </MenuToggle>\n );\n\n const defaultActions = (repo: Repository): IAction[] => [\n {\n title: 'Some action',\n onClick: () => console.log(`clicked on Some action, on row ${repo.name}`)\n },\n {\n title: <a href=\"https://www.patternfly.org\">Link action</a>\n },\n {\n isSeparator: true\n },\n {\n title: 'Third action',\n onClick: () => console.log(`clicked on Third action, on row ${repo.name}`)\n }\n ];\n\n const lastRowActions = (repo: Repository): IAction[] => [\n {\n title: 'Some action',\n onClick: () => console.log(`clicked on Some action, on row ${repo.name}`)\n },\n {\n title: <div>Another action</div>,\n onClick: () => console.log(`clicked on Another action, on row ${repo.name}`)\n },\n {\n isSeparator: true\n },\n {\n title: 'Third action',\n onClick: () => console.log(`clicked on Third action, on row ${repo.name}`)\n }\n ];\n\n return (\n <Fragment>\n <ToggleGroup aria-label=\"Default uses kebab toggle\">\n <ToggleGroupItem\n text=\"Custom actions toggle\"\n buttonId=\"customToggle\"\n isSelected={exampleChoice === 'customToggle'}\n onChange={onExampleTypeChange}\n />\n </ToggleGroup>\n <Table aria-label=\"Actions table\">\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n <Th screenReaderText=\"Primary action\" />\n <Th screenReaderText=\"Secondary action\" />\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo) => {\n // Arbitrary logic to determine which rows get which actions in this example\n let rowActions: IAction[] | null = defaultActions(repo);\n if (repo.name === 'a') {\n rowActions = null;\n }\n if (repo.name === '5') {\n rowActions = lastRowActions(repo);\n }\n let singleActionButton: React.ReactNode = null;\n if (repo.singleAction !== '') {\n singleActionButton = (\n <TableText>\n <Button variant=\"secondary\">{repo.singleAction}</Button>\n </TableText>\n );\n }\n\n return (\n <Tr key={repo.name}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n <Td dataLabel={columnNames.singleAction} modifier=\"fitContent\" hasAction>\n {singleActionButton}\n </Td>\n <Td isActionCell>\n {rowActions ? (\n <ActionsColumn\n items={rowActions}\n isDisabled={repo.name === '4'} // Also arbitrary for the example\n actionsToggle={exampleChoice === 'customToggle' ? customActionsToggle : undefined}\n />\n ) : null}\n </Td>\n </Tr>\n );\n })}\n </Tbody>\n </Table>\n </Fragment>\n );\n};\n","title":"Actions","lang":"ts","className":""}}>
1648
-
1649
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1650
- {`This example demonstrates adding actions as the last column. The header's last cell is an empty cell, and each body row's last cell is an action cell.`}
1651
- </p>
1652
-
1653
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1654
- {`To make a cell an action cell, render an `}
1655
-
1656
- <code {...{"className":"ws-code "}}>
1657
- {`ActionsColumn`}
1658
- </code>
1659
- {` component inside a row's last `}
1660
-
1661
- <code {...{"className":"ws-code "}}>
1662
- {`Td`}
1663
- </code>
1664
- {` and pass an array of `}
1665
-
1666
- <code {...{"className":"ws-code "}}>
1667
- {`IAction`}
1668
- </code>
1669
- {` objects via the `}
1670
-
1671
- <code {...{"className":"ws-code "}}>
1672
- {`items`}
1673
- </code>
1674
- {` prop of `}
1675
-
1676
- <code {...{"className":"ws-code "}}>
1677
- {`ActionsColumn`}
1678
- </code>
1679
- {`.`}
1680
- </p>
1681
-
1682
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1683
- {`If actions menus are getting clipped by other items on the page, such as sticky columns or rows, the `}
1684
-
1685
- <code {...{"className":"ws-code "}}>
1686
- {`ActionsColumn`}
1687
- </code>
1688
- {` can be passed a `}
1689
-
1690
- <code {...{"className":"ws-code "}}>
1691
- {`menuAppendTo`}
1692
- </code>
1693
- {` prop to adjust where the actions menu is appended.`}
1694
- </p>
1695
-
1696
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1697
- {`When a table row contains mixed content of text and interactive elements, the `}
1698
-
1699
- <code {...{"className":"ws-code "}}>
1700
- {`hasAction`}
1701
- </code>
1702
- {` property may be passed to a `}
1703
-
1704
- <code {...{"className":"ws-code "}}>
1705
- {`Td`}
1706
- </code>
1707
- {` which contains interactive content like the below example's start `}
1708
-
1709
- <code {...{"className":"ws-code "}}>
1710
- {`Button`}
1711
- </code>
1712
- {`. This will align buttons and other elements with other cells' text. Note that `}
1713
-
1714
- <code {...{"className":"ws-code "}}>
1715
- {`hasAction`}
1716
- </code>
1717
- {` should not be used with `}
1718
-
1719
- <code {...{"className":"ws-code "}}>
1720
- {`Td`}
1721
- </code>
1722
- {`s in an `}
1723
-
1724
- <code {...{"className":"ws-code "}}>
1725
- {`ActionsColumn`}
1726
- </code>
1727
- {` because that comes with it's own spacing.`}
1728
- </p>
1729
- </Example>,
1730
- 'Actions Overflow': props =>
1731
- <Example {...pageData} {...props} {...{"code":"import { Fragment, useState } from 'react';\nimport {\n Button,\n OverflowMenu,\n OverflowMenuControl,\n OverflowMenuContent,\n OverflowMenuGroup,\n OverflowMenuItem,\n OverflowMenuDropdownItem,\n MenuToggle,\n Dropdown,\n DropdownList\n} from '@patternfly/react-core';\nimport { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\nimport RhUiEllipsisVerticalFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ellipsis-vertical-fill-icon';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n isMenuOpen: boolean;\n}\n\nexport const TableActions: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five', isMenuOpen: false },\n { name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five', isMenuOpen: false },\n { name: 'p', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five', isMenuOpen: false },\n { name: '4', branches: '2', prs: 'b', workspaces: 'four', lastCommit: 'five', isMenuOpen: false },\n { name: '5', branches: '2', prs: 'b', workspaces: 'four', lastCommit: 'five', isMenuOpen: false }\n ];\n\n const [repos, setRepos] = useState(repositories);\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n const dropdownItems = [\n <OverflowMenuDropdownItem itemId={0} key=\"item1\" isShared>\n Primary\n </OverflowMenuDropdownItem>,\n <OverflowMenuDropdownItem itemId={1} key=\"item2\" isShared>\n Secondary\n </OverflowMenuDropdownItem>,\n <OverflowMenuDropdownItem itemId={2} key=\"item3\" isShared>\n Tertiary\n </OverflowMenuDropdownItem>\n ];\n\n return (\n <Fragment>\n <Table aria-label=\"Actions table\">\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n <Th screenReaderText=\"Actions\" />\n </Tr>\n </Thead>\n <Tbody>\n {repos.map((repo) => (\n <Tr key={repo.name}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n <Td isActionCell>\n <OverflowMenu breakpoint=\"lg\">\n <OverflowMenuContent>\n <OverflowMenuGroup groupType=\"button\">\n <OverflowMenuItem>\n <Button variant=\"primary\">Primary</Button>\n </OverflowMenuItem>\n <OverflowMenuItem>\n <Button variant=\"secondary\">Secondary</Button>\n </OverflowMenuItem>\n <OverflowMenuItem>\n <Button variant=\"tertiary\">Tertiary</Button>\n </OverflowMenuItem>\n </OverflowMenuGroup>\n </OverflowMenuContent>\n <OverflowMenuControl>\n <Dropdown\n onSelect={() =>\n setRepos(repos.map((r) => (r.name !== repo.name ? r : { ...r, isMenuOpen: !r.isMenuOpen })))\n }\n toggle={(toggleRef) => (\n <MenuToggle\n ref={toggleRef}\n variant=\"plain\"\n aria-label=\"Table actions overflow menu\"\n onClick={() =>\n setRepos(repos.map((r) => (r.name !== repo.name ? r : { ...r, isMenuOpen: !r.isMenuOpen })))\n }\n isExpanded={repo.isMenuOpen}\n icon={<RhUiEllipsisVerticalFillIcon />}\n />\n )}\n isOpen={repo.isMenuOpen}\n onOpenChange={(isOpen) =>\n setRepos(repos.map((r) => (r.name !== repo.name ? r : { ...r, isMenuOpen: isOpen })))\n }\n >\n <DropdownList>{dropdownItems}</DropdownList>\n </Dropdown>\n </OverflowMenuControl>\n </OverflowMenu>\n </Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </Fragment>\n );\n};\n","title":"Actions Overflow","lang":"ts","className":""}}>
1732
-
1733
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1734
- {`Using an `}
1735
-
1736
- <code {...{"className":"ws-code "}}>
1737
- {`OverflowMenu`}
1738
- </code>
1739
- {` in the actions column, allowing the actions to condense into a dropdown if necessary for space.`}
1740
- </p>
1741
- </Example>,
1742
- 'Expandable': props =>
1743
- <Example {...pageData} {...props} {...{"code":"import { Fragment, useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, ExpandableRowContent } from '@patternfly/react-table';\nimport { Checkbox } from '@patternfly/react-core';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n details?: {\n detail1?: string;\n detail2?: string;\n detail3?: string;\n detailFormat: 0 | 1 | 2 | 3;\n };\n}\n\nexport const TableExpandable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },\n {\n name: 'parent 1',\n branches: 'two',\n prs: 'k',\n workspaces: 'four',\n lastCommit: 'five',\n // This `details` structure is just for this example. You can drive expanded content from any kind of data.\n details: { detailFormat: 0, detail1: 'single cell' }\n },\n {\n name: 'parent 2',\n branches: 'two',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: {\n detailFormat: 1,\n detail1:\n 'Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. Lorem ipsum sit dolor. '\n }\n },\n {\n name: 'parent 3',\n branches: '2',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: { detailFormat: 2, detail1: 'single cell - noPadding' }\n },\n {\n name: 'parent 4',\n branches: '2',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: { detailFormat: 3, detail1: 'single cell - fullWidth & noPadding' }\n },\n {\n name: 'parent 5',\n branches: '2',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: {\n detailFormat: 0,\n detail1: \"spans 'Repositories and 'Branches'\",\n detail2: \"spans 'Pull requests' and 'Workspaces', and 'Last commit'\"\n }\n },\n {\n name: 'parent 6',\n branches: '2',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: {\n detailFormat: 1,\n detail1: \"fullWidth, spans the collapsible column and 'Repositories'\",\n detail2: \"fullWidth, spans 'Branches' and 'Pull requests'\",\n detail3: \"fullWidth, spans 'Workspaces' and 'Last commit'\"\n }\n }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n // In this example, expanded rows are tracked by the repo names from each row. This could be any unique identifier.\n // This is to prevent state from being based on row order index in case we later add sorting.\n // Note that this behavior is very similar to selection state.\n const initialExpandedRepoNames = repositories.filter((repo) => !!repo.details).map((repo) => repo.name); // Default to all expanded\n const [expandedRepoNames, setExpandedRepoNames] = useState<string[]>(initialExpandedRepoNames);\n const setRepoExpanded = (repo: Repository, isExpanding = true) =>\n setExpandedRepoNames((prevExpanded) => {\n const otherExpandedRepoNames = prevExpanded.filter((r) => r !== repo.name);\n return isExpanding ? [...otherExpandedRepoNames, repo.name] : otherExpandedRepoNames;\n });\n const isRepoExpanded = (repo: Repository) => expandedRepoNames.includes(repo.name);\n\n const [isExampleCompact, setIsExampleCompact] = useState(true);\n\n return (\n <Fragment>\n <Checkbox\n label=\"Compact\"\n isChecked={isExampleCompact}\n onChange={(_event, checked) => setIsExampleCompact(checked)}\n aria-label=\"toggle compact variation\"\n id=\"toggle-compact\"\n name=\"toggle-compact\"\n />\n <Table\n isExpandable\n hasAnimations\n aria-label=\"Expandable table\"\n variant={isExampleCompact ? 'compact' : undefined}\n >\n <Thead>\n <Tr>\n <Th screenReaderText=\"Row expansion\" />\n <Th width={25}>{columnNames.name}</Th>\n <Th width={10}>{columnNames.branches}</Th>\n <Th width={15}>{columnNames.prs}</Th>\n <Th width={30}>{columnNames.workspaces}</Th>\n <Th width={10}>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n {repositories.map((repo, rowIndex) => {\n // Some arbitrary examples of how you could customize the child row based on your needs\n let childIsFullWidth = false;\n let childHasNoPadding = false;\n let detail1Colspan = 1;\n let detail2Colspan = 1;\n let detail3Colspan = 1;\n if (repo.details) {\n const { detail1, detail2, detail3, detailFormat } = repo.details;\n const numColumns = 5;\n childIsFullWidth = [1, 3].includes(detailFormat);\n childHasNoPadding = [2, 3].includes(detailFormat);\n if (detail1 && !detail2 && !detail3) {\n detail1Colspan = !childIsFullWidth ? numColumns : numColumns + 1; // Account for toggle column\n } else if (detail1 && detail2 && !detail3) {\n detail1Colspan = 2;\n detail2Colspan = !childIsFullWidth ? 3 : 4;\n } else if (detail1 && detail2 && detail3) {\n detail1Colspan = 2;\n detail2Colspan = 2;\n detail3Colspan = !childIsFullWidth ? 1 : 2;\n }\n }\n return (\n <Tbody key={repo.name} isExpanded={isRepoExpanded(repo)}>\n <Tr isContentExpanded={isRepoExpanded(repo)}>\n <Td\n expand={\n repo.details\n ? {\n rowIndex,\n isExpanded: isRepoExpanded(repo),\n onToggle: () => setRepoExpanded(repo, !isRepoExpanded(repo)),\n expandId: 'composable-expandable-example'\n }\n : undefined\n }\n />\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n {repo.details ? (\n <Tr isExpanded={isRepoExpanded(repo)}>\n {!childIsFullWidth ? <Td /> : null}\n {repo.details.detail1 ? (\n <Td dataLabel=\"Repo detail 1\" noPadding={childHasNoPadding} colSpan={detail1Colspan}>\n <ExpandableRowContent>{repo.details.detail1}</ExpandableRowContent>\n </Td>\n ) : null}\n {repo.details.detail2 ? (\n <Td dataLabel=\"Repo detail 2\" noPadding={childHasNoPadding} colSpan={detail2Colspan}>\n <ExpandableRowContent>{repo.details.detail2}</ExpandableRowContent>\n </Td>\n ) : null}\n {repo.details.detail3 ? (\n <Td dataLabel=\"Repo detail 3\" noPadding={childHasNoPadding} colSpan={detail3Colspan}>\n <ExpandableRowContent>{repo.details.detail3}</ExpandableRowContent>\n </Td>\n ) : null}\n </Tr>\n ) : null}\n </Tbody>\n );\n })}\n </Table>\n </Fragment>\n );\n};\n","title":"Expandable","lang":"ts","className":""}}>
1744
-
1745
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1746
- {`To make a parent/child row pair expandable:`}
1747
- </p>
1748
-
1749
- <ol {...{"className":"pf-v6-c-content--ol pf-m-editorial ws-ol "}}>
1750
-
1751
-
1752
-
1753
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1754
- {`Pass `}
1755
-
1756
- <code {...{"className":"ws-code "}}>
1757
- {`isExpandable`}
1758
- </code>
1759
- {` to `}
1760
-
1761
- <code {...{"className":"ws-code "}}>
1762
- {`Table`}
1763
- </code>
1764
- {`.`}
1765
- </li>
1766
-
1767
-
1768
-
1769
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1770
- {`Make the first cell in every row an expandable cell by passing `}
1771
-
1772
- <code {...{"className":"ws-code "}}>
1773
- {`TdExpandType`}
1774
- </code>
1775
- {` object to the `}
1776
-
1777
- <code {...{"className":"ws-code "}}>
1778
- {`expand`}
1779
- </code>
1780
- {` prop on the `}
1781
-
1782
- <code {...{"className":"ws-code "}}>
1783
- {`Td`}
1784
- </code>
1785
- {`.`}
1786
- </li>
1787
-
1788
-
1789
-
1790
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1791
- {`Wrap the content of each child row cell in `}
1792
-
1793
- <code {...{"className":"ws-code "}}>
1794
- {`ExpandableRowContent`}
1795
- </code>
1796
- {`.`}
1797
- </li>
1798
-
1799
-
1800
-
1801
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1802
- {`Pass `}
1803
-
1804
- <code {...{"className":"ws-code "}}>
1805
- {`isExpanded`}
1806
- </code>
1807
- {` to `}
1808
-
1809
- <code {...{"className":"ws-code "}}>
1810
- {`Tbody`}
1811
- </code>
1812
- {` and the `}
1813
-
1814
- <code {...{"className":"ws-code "}}>
1815
- {`Tr`}
1816
- </code>
1817
- {` containing expandable content, and pass `}
1818
-
1819
- <code {...{"className":"ws-code "}}>
1820
- {`isContentExpanded`}
1821
- </code>
1822
- {` to the `}
1823
-
1824
- <code {...{"className":"ws-code "}}>
1825
- {`Tr`}
1826
- </code>
1827
- {` that acts as the "control row".`}
1828
- </li>
1829
-
1830
-
1831
- </ol>
1832
-
1833
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1834
- {`The `}
1835
-
1836
- <code {...{"className":"ws-code "}}>
1837
- {`TdExpandType`}
1838
- </code>
1839
- {` expects an `}
1840
-
1841
- <code {...{"className":"ws-code "}}>
1842
- {`OnCollapse`}
1843
- </code>
1844
- {` event handler that has the following signature:`}
1845
- </p>
1846
-
1847
- <Example {...{"code":"type OnCollapse = (\n event: React.MouseEvent,\n rowIndex: number,\n isOpen: boolean,\n rowData: IRowData,\n extraData: IExtraData\n) => void;","className":""}}>
1848
-
1849
- </Example>
1850
-
1851
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1852
- {`Note: Table column widths will respond automatically when toggling expanded rows. To retain column widths between expanded and collapsed states, column header and/or data cell widths must be set.`}
1853
- </p>
1854
- </Example>,
1855
- 'Compound expandable': props =>
1856
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, TdProps, ExpandableRowContent } from '@patternfly/react-table';\n\nimport RhUiCodeIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-code-icon';\nimport RhUiBranchFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-branch-fill-icon';\nimport CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';\n\ninterface Repository {\n name: string;\n branches: number;\n prs: number;\n workspaces: number;\n lastCommit: string;\n}\n\nexport const TableCompoundExpandable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'siemur/test-space', branches: 10, prs: 4, workspaces: 4, lastCommit: '20 minutes' },\n { name: 'siemur/test-space-2', branches: 3, prs: 4, workspaces: 4, lastCommit: '20 minutes' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n type ColumnKey = keyof typeof columnNames;\n\n // In this example, expanded cells are tracked by the repo and property names from each row. This could be any pair of unique identifiers.\n // This is to prevent state from being based on row and column order index in case we later add sorting and rearranging columns.\n // Note that this behavior is very similar to selection state.\n const [expandedCells, setExpandedCells] = useState<Record<string, ColumnKey>>({\n 'siemur/test-space': 'branches' // Default to the first cell of the first row being expanded\n });\n const setCellExpanded = (repo: Repository, columnKey: ColumnKey, isExpanding = true) => {\n const newExpandedCells = { ...expandedCells };\n if (isExpanding) {\n newExpandedCells[repo.name] = columnKey;\n } else {\n delete newExpandedCells[repo.name];\n }\n setExpandedCells(newExpandedCells);\n };\n const compoundExpandParams = (\n repo: Repository,\n columnKey: ColumnKey,\n rowIndex: number,\n columnIndex: number\n ): TdProps['compoundExpand'] => ({\n isExpanded: expandedCells[repo.name] === columnKey,\n onToggle: () => setCellExpanded(repo, columnKey, expandedCells[repo.name] !== columnKey),\n expandId: 'compound-expandable-example',\n rowIndex,\n columnIndex\n });\n\n return (\n <Table aria-label=\"Compound expandable table\" isExpandable hasAnimations>\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n <Th screenReaderText=\"URL\" />\n </Tr>\n </Thead>\n {repositories.map((repo: Repository, rowIndex: number) => {\n const expandedCellKey = expandedCells[repo.name];\n const isRowExpanded = !!expandedCellKey;\n return (\n <Tbody key={repo.name} isExpanded={isRowExpanded}>\n <Tr isContentExpanded={isRowExpanded} isControlRow>\n <Td width={25} dataLabel={columnNames.name} component=\"th\">\n <a href=\"#\">{repo.name}</a>\n </Td>\n <Td\n width={10}\n dataLabel={columnNames.branches}\n compoundExpand={compoundExpandParams(repo, 'branches', rowIndex, 1)}\n >\n <RhUiBranchFillIcon key=\"icon\" /> {repo.branches}\n </Td>\n <Td\n width={10}\n dataLabel={columnNames.prs}\n compoundExpand={compoundExpandParams(repo, 'prs', rowIndex, 2)}\n >\n <RhUiCodeIcon key=\"icon\" /> {repo.prs}\n </Td>\n <Td\n width={10}\n dataLabel={columnNames.workspaces}\n compoundExpand={compoundExpandParams(repo, 'workspaces', rowIndex, 3)}\n >\n <CubeIcon key=\"icon\" /> {repo.workspaces}\n </Td>\n <Td width={15} dataLabel={columnNames.lastCommit}>\n {repo.lastCommit}\n </Td>\n <Td width={30}>\n <a href=\"#\">Open in GitHub</a>\n </Td>\n </Tr>\n <Tr isExpanded={columnNames[expandedCellKey] === columnNames.branches}>\n <Td dataLabel={columnNames[expandedCellKey]} colSpan={6}>\n <ExpandableRowContent>\n <div>Expanded content for {repo.name}: branches goes here!</div>\n </ExpandableRowContent>\n </Td>\n </Tr>\n <Tr isExpanded={columnNames[expandedCellKey] === columnNames.prs}>\n <Td dataLabel={columnNames[expandedCellKey]} colSpan={6}>\n <ExpandableRowContent>\n <div>Expanded content for {repo.name}: prs goes here!</div>\n </ExpandableRowContent>\n </Td>\n </Tr>\n <Tr isExpanded={columnNames[expandedCellKey] === columnNames.workspaces}>\n <Td dataLabel={columnNames[expandedCellKey]} colSpan={6}>\n <ExpandableRowContent>\n <div>Expanded content for {repo.name}: workspaces goes here!</div>\n </ExpandableRowContent>\n </Td>\n </Tr>\n </Tbody>\n );\n })}\n </Table>\n );\n};\n","title":"Compound expandable","lang":"ts","className":""}}>
1857
-
1858
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1859
- {`To make a parent/child row pair compound expandable:`}
1860
- </p>
1861
-
1862
- <ol {...{"className":"pf-v6-c-content--ol pf-m-editorial ws-ol "}}>
1863
-
1864
-
1865
-
1866
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1867
- {`Pass `}
1868
-
1869
- <code {...{"className":"ws-code "}}>
1870
- {`isExpandable`}
1871
- </code>
1872
- {` to `}
1873
-
1874
- <code {...{"className":"ws-code "}}>
1875
- {`Table`}
1876
- </code>
1877
- {`.`}
1878
- </li>
1879
-
1880
-
1881
-
1882
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1883
- {`Pass a `}
1884
-
1885
- <code {...{"className":"ws-code "}}>
1886
- {`TdCompoundExpandType`}
1887
- </code>
1888
- {` object to the `}
1889
-
1890
- <code {...{"className":"ws-code "}}>
1891
- {`compoundExpand`}
1892
- </code>
1893
- {` prop on any `}
1894
-
1895
- <code {...{"className":"ws-code "}}>
1896
- {`Td`}
1897
- </code>
1898
- {` that has an expandable child row.`}
1899
- </li>
1900
-
1901
-
1902
-
1903
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1904
- {`Wrap the content of each child row cell in `}
1905
-
1906
- <code {...{"className":"ws-code "}}>
1907
- {`ExpandableRowContent`}
1908
- </code>
1909
- {`.`}
1910
- </li>
1911
-
1912
-
1913
-
1914
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
1915
- {`Pass `}
1916
-
1917
- <code {...{"className":"ws-code "}}>
1918
- {`isExpanded`}
1919
- </code>
1920
- {` to `}
1921
-
1922
- <code {...{"className":"ws-code "}}>
1923
- {`Tbody`}
1924
- </code>
1925
- {` and the `}
1926
-
1927
- <code {...{"className":"ws-code "}}>
1928
- {`Tr`}
1929
- </code>
1930
- {` containing expandable content, and pass `}
1931
-
1932
- <code {...{"className":"ws-code "}}>
1933
- {`isContentExpanded`}
1934
- </code>
1935
- {` to the `}
1936
-
1937
- <code {...{"className":"ws-code "}}>
1938
- {`Tr`}
1939
- </code>
1940
- {` that acts as the "control row".`}
1941
- </li>
1942
-
1943
-
1944
- </ol>
1945
-
1946
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1947
- {`The `}
1948
-
1949
- <code {...{"className":"ws-code "}}>
1950
- {`TdCompoundExpandType`}
1951
- </code>
1952
- {` expects an `}
1953
-
1954
- <code {...{"className":"ws-code "}}>
1955
- {`OnExpand`}
1956
- </code>
1957
- {` event handler with the following signature`}
1958
- </p>
1959
-
1960
- <Example {...{"code":"export type OnExpand = (\n event: React.MouseEvent,\n rowIndex: number,\n colIndex: number,\n isOpen: boolean,\n rowData: IRowData,\n extraData: IExtraData\n) => void;","className":""}}>
1961
-
1962
- </Example>
1963
-
1964
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1965
- {`For a more complex compound expandable implementation with nested tables, see our `}
1966
-
1967
- <PatternflyThemeLink {...{"to":"/components/table/react-demos/compound-expansion","className":""}}>
1968
- {`table compound expansion demo`}
1969
- </PatternflyThemeLink>
1970
- {`.`}
1971
- </p>
1972
- </Example>,
1973
- 'Cell width, breakpoint modifiers': props =>
1974
- <Example {...pageData} {...props} {...{"code":"import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string | null;\n prs: string | null;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TableCellWidth: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'three', workspaces: 'four', lastCommit: 'five' },\n { name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' },\n { name: 'one - 3', branches: 'two - 3', prs: 'three - 3', workspaces: 'four - 3', lastCommit: 'five - 3' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n return (\n <Table aria-label=\"Cell widths\">\n <Thead>\n <Tr>\n <Th width={15}>{columnNames.name}</Th>\n <Th width={15}>{columnNames.branches}</Th>\n <Th width={40} visibility={['hiddenOnMd', 'visibleOnLg']}>\n {columnNames.prs}\n </Th>\n <Th width={15}>{columnNames.workspaces}</Th>\n <Th width={15}>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo) => (\n <Tr key={repo.name}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs} visibility={['hiddenOnMd', 'visibleOnLg']}>\n {repo.prs}\n </Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Cell width, breakpoint modifiers","lang":"ts","className":""}}>
1975
-
1976
- </Example>,
1977
- 'Controlling text': props =>
1978
- <Example {...pageData} {...props} {...{"code":"import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\n// This example has been simplified to focus on the text modifier props. In real usage,\n// you may want to derive your rows from typed underlying data and minimal state. See other examples.\n\nconst columnNames = {\n truncate: 'Truncate (width 20%)',\n break: 'Break word',\n wrap: 'Wrapping table header text. This th text will wrap instead of truncate.',\n fit: 'Fit content',\n nowrap: 'No wrap'\n};\n\nexport const TableControllingText: React.FunctionComponent = () => (\n <Table aria-label=\"Controlling text\">\n <Thead>\n <Tr>\n <Th width={20}>{columnNames.truncate}</Th>\n <Th>{columnNames.break}</Th>\n <Th modifier=\"wrap\">{columnNames.wrap}</Th>\n <Th modifier=\"fitContent\">{columnNames.fit}</Th>\n <Th>{columnNames.nowrap}</Th>\n </Tr>\n </Thead>\n <Tbody>\n <Tr>\n <Td dataLabel={columnNames.truncate} modifier=\"truncate\">\n This text will truncate instead of wrap.\n </Td>\n <Td dataLabel={columnNames.break} modifier=\"breakWord\">\n <a>http://thisisaverylongurlthatneedstobreakusethebreakwordmodifier.org</a>\n </Td>\n <Td dataLabel={columnNames.wrap}>\n <p>\n By default,\n <code>thead</code> cells will truncate and\n <code>tbody</code> cells will wrap. Use\n <code>.pf-m-wrap</code> on a<code>th</code> to change its behavior.\n </p>\n </Td>\n <Td dataLabel={columnNames.fit}>\n This cell's content will adjust itself to the parent th width. This modifier only affects table layouts.\n </Td>\n <Td dataLabel={columnNames.nowrap} modifier=\"nowrap\">\n <a href=\"#\">No wrap</a>\n </Td>\n </Tr>\n </Tbody>\n </Table>\n);\n","title":"Controlling text","lang":"ts","className":""}}>
1979
-
1980
- </Example>,
1981
- 'Modifiers with table text': props =>
1982
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, TableText } from '@patternfly/react-table';\n\n// This example has been simplified to focus on the text modifier props. In real usage,\n// you may want to derive your rows from typed underlying data and minimal state. See other examples.\n\nconst columnNames = {\n truncate: 'Truncating text',\n wrap: 'Wrapping table header text. This th text will wrap instead of truncate.'\n};\n\nexport const TableTextModifiers: React.FunctionComponent = () => {\n const [focused, setFocused] = useState(false);\n\n return (\n <Table aria-label=\"Table text\">\n <Thead>\n <Tr>\n <Th width={30}>{columnNames.truncate}</Th>\n <Th>{columnNames.wrap}</Th>\n </Tr>\n </Thead>\n <Tbody>\n <Tr>\n <Td\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n tabIndex={0}\n dataLabel={columnNames.truncate}\n >\n <TableText focused={focused} wrapModifier=\"truncate\">\n This text will truncate instead of wrap.\n </TableText>\n </Td>\n <Td dataLabel={columnNames.wrap}>\n <TableText wrapModifier=\"nowrap\">\n <a href=\"#\">This is a link that needs to be on one line and fully readable.</a>\n </TableText>\n </Td>\n </Tr>\n </Tbody>\n </Table>\n );\n};\n","title":"Modifiers with table text","lang":"ts","className":""}}>
1983
-
1984
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1985
- {`If the "wrapModifier" property is set to "truncate", it's needed to ensure that the corresponding tooltip can be opened using both keyboard and screen reader. Since this particular Td element is generic and doesn't have any predefined decorators, the focus management required to trigger the tooltip needs to be handled manually by defining and manipulating the requisite props.`}
1986
- </p>
1987
- </Example>,
1988
- 'Empty state': props =>
1989
- <Example {...pageData} {...props} {...{"code":"import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\nimport {\n Bullseye,\n EmptyState,\n EmptyStateVariant,\n EmptyStateBody,\n Button,\n EmptyStateFooter,\n EmptyStateActions\n} from '@patternfly/react-core';\nimport SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';\n\n// This example has been simplified to focus on the empty state. In real usage,\n// you may want to derive your rows from typed underlying data and minimal state. See other examples.\n\nexport const TableEmptyState: React.FunctionComponent = () => (\n <Table aria-label=\"Empty state table\">\n <Thead>\n <Tr>\n <Th>Repositories</Th>\n <Th>Branches</Th>\n <Th>Pull requests</Th>\n <Th>Workspaces</Th>\n <Th>Last commit</Th>\n </Tr>\n </Thead>\n <Tbody>\n <Tr>\n <Td colSpan={8}>\n <Bullseye>\n <EmptyState headingLevel=\"h2\" titleText=\"No results found\" icon={SearchIcon} variant={EmptyStateVariant.sm}>\n <EmptyStateBody>Clear all filters and try again.</EmptyStateBody>\n <EmptyStateFooter>\n <EmptyStateActions>\n <Button variant=\"link\">Clear all filters</Button>\n </EmptyStateActions>\n </EmptyStateFooter>\n </EmptyState>\n </Bullseye>\n </Td>\n </Tr>\n </Tbody>\n </Table>\n);\n","title":"Empty state","lang":"ts","className":""}}>
1990
-
1991
- </Example>,
1992
- 'Favoritable (implemented with sortable)': props =>
1993
- <Example {...pageData} {...props} {...{"code":"import { useCallback, useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, ThProps } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n}\n\nexport const TableFavoritable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },\n { name: 'a', branches: 'two', prs: 'k', workspaces: 'four', lastCommit: 'five' },\n { name: 'p', branches: 'two', prs: 'b', workspaces: 'four', lastCommit: 'five' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n // Index of the currently sorted column\n // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key\n // as the identifier of the sorted column. See the \"Compound expandable\" example.\n const [activeSortIndex, setActiveSortIndex] = useState<number>();\n\n // Sort direction of the currently sorted column\n const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc'>();\n\n // Favorite state is similar to selection state, see Selectable with checkbox.\n const [favoriteRepoNames, setFavoriteRepoNames] = useState<string[]>([]);\n const setRepoFavorited = (repo: Repository, isFavoriting = true) =>\n setFavoriteRepoNames((prevFavorites) => {\n const otherFavorites = prevFavorites.filter((r) => r !== repo.name);\n return isFavoriting ? [...otherFavorites, repo.name] : otherFavorites;\n });\n const isRepoFavorited = (repo: Repository) => favoriteRepoNames.includes(repo.name);\n\n // State of the header cell to favorite / unfavorite all rows\n const [headerFavorited, setHeaderFavorited] = useState(false);\n\n // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.\n // In this example we only deal with booleans here because we only sort on the favorites column.\n // For more complex sorting, see Sortable.\n // Note: We also memoize the sortable values with useCallback to prevent rows jumping around when you change\n // the favorites while sorting on that column. Only updating the sort state will reorder the rows.\n const getSortableRowValues = useCallback(\n (repo: Repository): boolean[] => [isRepoFavorited(repo)],\n [activeSortIndex, activeSortDirection]\n );\n\n // Note that we perform the sort as part of the component's render logic and not in onSort.\n // We shouldn't store the list of data in state because we don't want to have to sync that with props.\n let sortedRepositories = repositories;\n if (activeSortIndex !== undefined) {\n sortedRepositories = repositories.sort((a, b) => {\n const aValue = getSortableRowValues(a)[activeSortIndex];\n const bValue = getSortableRowValues(b)[activeSortIndex];\n if (aValue === bValue) {\n return 0;\n }\n if (activeSortDirection === 'asc') {\n return aValue > bValue ? 1 : -1;\n } else {\n return bValue > aValue ? 1 : -1;\n }\n });\n }\n\n const getSortParams = (columnIndex: number): ThProps['sort'] => ({\n isFavorites: columnIndex === 0, // Not just statically true in case we add sorting on other columns later\n sortBy: {\n index: activeSortIndex,\n direction: activeSortDirection\n },\n onSort: (_event, index, direction) => {\n setActiveSortIndex(index);\n setActiveSortDirection(direction);\n },\n 'aria-label': 'Sort favorites',\n columnIndex,\n favoriteButtonProps: {\n favorited: headerFavorited,\n onClick: (_event) => {\n repositories.forEach((repo) => setRepoFavorited(repo, !headerFavorited));\n setHeaderFavorited(!headerFavorited);\n },\n 'aria-label': headerFavorited ? 'Unfavorite all' : 'Favorite all'\n }\n });\n\n return (\n <Table aria-label=\"Favoritable table\" variant=\"compact\">\n <Thead>\n <Tr>\n <Th sort={getSortParams(0)} />\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {sortedRepositories.map((repo, rowIndex) => (\n <Tr key={rowIndex}>\n <Td\n favorites={{\n isFavorited: isRepoFavorited(repo),\n onFavorite: (_event, isFavoriting) => {\n setRepoFavorited(repo, isFavoriting);\n\n if (\n isFavoriting &&\n repositories.filter((r) => r !== repo).every((r) => favoriteRepoNames.includes(r.name))\n ) {\n setHeaderFavorited(true);\n }\n\n if (!isFavoriting && favoriteRepoNames.length === 1 && favoriteRepoNames.includes(repo.name)) {\n setHeaderFavorited(false);\n }\n },\n rowIndex\n }}\n />\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Favoritable (implemented with sortable)","lang":"ts","className":""}}>
1994
-
1995
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
1996
- {`To make a row favoritable, the table needs a favoritable column.
1997
- Pass a `}
1998
-
1999
- <code {...{"className":"ws-code "}}>
2000
- {`TdFavoritesType`}
2001
- </code>
2002
- {` object via the `}
2003
-
2004
- <code {...{"className":"ws-code "}}>
2005
- {`favorites`}
2006
- </code>
2007
- {` prop on each rows's first `}
2008
-
2009
- <code {...{"className":"ws-code "}}>
2010
- {`Td`}
2011
- </code>
2012
- {` in the favoritable column.`}
2013
- </p>
2014
-
2015
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2016
- {`The `}
2017
-
2018
- <code {...{"className":"ws-code "}}>
2019
- {`TdFavoritesType`}
2020
- </code>
2021
- {` expects an `}
2022
-
2023
- <code {...{"className":"ws-code "}}>
2024
- {`OnFavorite`}
2025
- </code>
2026
- {` event handler with the following signature:`}
2027
- </p>
2028
-
2029
- <Example {...{"code":"type OnFavorite = (\n event: React.MouseEvent,\n isFavorited: boolean,\n rowIndex: number,\n rowData: IRowData,\n extraData: IExtraData\n) => void;","className":""}}>
2030
-
2031
- </Example>
2032
-
2033
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2034
- {`To make a favoritable column sortable, pass a `}
2035
-
2036
- <code {...{"className":"ws-code "}}>
2037
- {`ThSortType`}
2038
- </code>
2039
- {` object to the favoritable column's `}
2040
-
2041
- <code {...{"className":"ws-code "}}>
2042
- {`Th`}
2043
- </code>
2044
- {` with `}
2045
-
2046
- <code {...{"className":"ws-code "}}>
2047
- {`isFavorites`}
2048
- </code>
2049
- {` set to true.`}
2050
- </p>
2051
- </Example>,
2052
- 'Tree table': props =>
2053
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, TreeRowWrapper, TdProps } from '@patternfly/react-table';\nimport LeafIcon from '@patternfly/react-icons/dist/esm/icons/leaf-icon';\nimport FolderIcon from '@patternfly/react-icons/dist/esm/icons/folder-icon';\nimport FolderOpenIcon from '@patternfly/react-icons/dist/esm/icons/folder-open-icon';\n\ninterface RepositoriesTreeNode {\n name: string;\n branches: string;\n pullRequests: string;\n workspaces: string;\n children?: RepositoriesTreeNode[];\n}\n\nexport const TableTree: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const data: RepositoriesTreeNode[] = [\n {\n name: 'Repositories one',\n branches: 'Branch one',\n pullRequests: 'Pull request one',\n workspaces: 'Workplace one',\n children: [\n {\n name: 'Repositories two',\n branches: 'Branch two',\n pullRequests: 'Pull request two',\n workspaces: 'Workplace two',\n children: [\n {\n name: 'Repositories three',\n branches: 'Branch three',\n pullRequests: 'Pull request three',\n workspaces: 'Workplace three'\n },\n {\n name: 'Repositories four',\n branches: 'Branch four',\n pullRequests: 'Pull request four',\n workspaces: 'Workplace four'\n }\n ]\n },\n {\n name: 'Repositories five',\n branches: 'Branch five',\n pullRequests: 'Pull request five',\n workspaces: 'Workplace five'\n },\n {\n name: 'Repositories six',\n branches: 'Branch six',\n pullRequests: 'Pull request six',\n workspaces: 'Workplace six'\n }\n ]\n },\n {\n name: 'Repositories seven',\n branches: 'Branch seven',\n pullRequests: 'Pull request seven',\n workspaces: 'Workplace seven',\n children: [\n {\n name: 'Repositories eight',\n branches: 'Branch eight',\n pullRequests: 'Pull request eight',\n workspaces: 'Workplace eight'\n }\n ]\n },\n {\n name: 'Repositories nine',\n branches: 'Branch nine',\n pullRequests: 'Pull request nine',\n workspaces: 'Workplace nine'\n }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces'\n };\n\n const [expandedNodeNames, setExpandedNodeNames] = useState<string[]>(['Repositories one']);\n const [expandedDetailsNodeNames, setExpandedDetailsNodeNames] = useState<string[]>([]);\n const [selectedNodeNames, setSelectedNodeNames] = useState<string[]>([]);\n\n const getDescendants = (node: RepositoriesTreeNode): RepositoriesTreeNode[] => {\n if (!node.children || !node.children.length) {\n return [node];\n } else {\n let children: RepositoriesTreeNode[] = [];\n node.children.forEach((child) => {\n children = [...children, ...getDescendants(child)];\n });\n return children;\n }\n };\n\n const areAllDescendantsSelected = (node: RepositoriesTreeNode) =>\n getDescendants(node).every((n) => selectedNodeNames.includes(n.name));\n const areSomeDescendantsSelected = (node: RepositoriesTreeNode) =>\n getDescendants(node).some((n) => selectedNodeNames.includes(n.name));\n\n const isNodeChecked = (node: RepositoriesTreeNode) => {\n if (areAllDescendantsSelected(node)) {\n return true;\n }\n if (areSomeDescendantsSelected(node)) {\n return null;\n }\n return false;\n };\n\n /** \n Recursive function which flattens the data into an array of flattened TreeRowWrapper components\n params: \n - nodes - array of a single level of tree nodes\n - level - number representing how deeply nested the current row is\n - posinset - position of the row relative to this row's siblings\n - currentRowIndex - position of the row relative to the entire table\n - isHidden - defaults to false, true if this row's parent is expanded\n */\n const renderRows = (\n [node, ...remainingNodes]: RepositoriesTreeNode[],\n level = 1,\n posinset = 1,\n rowIndex = 0,\n isHidden = false\n ): React.ReactNode[] => {\n if (!node) {\n return [];\n }\n const isExpanded = expandedNodeNames.includes(node.name);\n const isDetailsExpanded = expandedDetailsNodeNames.includes(node.name);\n const isChecked = isNodeChecked(node);\n let icon = <LeafIcon />;\n if (node.children) {\n icon = isExpanded ? <FolderOpenIcon /> : <FolderIcon />;\n }\n\n const treeRow: TdProps['treeRow'] = {\n onCollapse: () =>\n setExpandedNodeNames((prevExpanded) => {\n const otherExpandedNodeNames = prevExpanded.filter((name) => name !== node.name);\n return isExpanded ? otherExpandedNodeNames : [...otherExpandedNodeNames, node.name];\n }),\n onToggleRowDetails: () =>\n setExpandedDetailsNodeNames((prevDetailsExpanded) => {\n const otherDetailsExpandedNodeNames = prevDetailsExpanded.filter((name) => name !== node.name);\n return isDetailsExpanded ? otherDetailsExpandedNodeNames : [...otherDetailsExpandedNodeNames, node.name];\n }),\n onCheckChange: (_event, isChecking) => {\n const nodeNamesToCheck = getDescendants(node).map((n) => n.name);\n setSelectedNodeNames((prevSelected) => {\n const otherSelectedNodeNames = prevSelected.filter((name) => !nodeNamesToCheck.includes(name));\n return !isChecking ? otherSelectedNodeNames : [...otherSelectedNodeNames, ...nodeNamesToCheck];\n });\n },\n rowIndex,\n props: {\n isExpanded,\n isDetailsExpanded,\n isHidden,\n 'aria-level': level,\n 'aria-posinset': posinset,\n 'aria-setsize': node.children ? node.children.length : 0,\n isChecked,\n checkboxId: `checkbox_id_${node.name.toLowerCase().replace(/\\s+/g, '_')}`,\n icon\n }\n };\n\n const childRows =\n node.children && node.children.length\n ? renderRows(node.children, level + 1, 1, rowIndex + 1, !isExpanded || isHidden)\n : [];\n\n return [\n <TreeRowWrapper key={node.name} row={{ props: treeRow.props }}>\n <Td dataLabel={columnNames.name} treeRow={treeRow}>\n {node.name}\n </Td>\n <Td dataLabel={columnNames.branches}>{node.branches}</Td>\n <Td dataLabel={columnNames.prs}>{node.pullRequests}</Td>\n <Td dataLabel={columnNames.workspaces}>{node.workspaces}</Td>\n </TreeRowWrapper>,\n ...childRows,\n ...renderRows(remainingNodes, level, posinset + 1, rowIndex + 1 + childRows.length, isHidden)\n ];\n };\n\n return (\n <Table isTreeTable aria-label=\"Tree table\">\n <Thead>\n <Tr>\n <Th width={40}>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n </Tr>\n </Thead>\n <Tbody>{renderRows(data)}</Tbody>\n </Table>\n );\n};\n","title":"Tree table","lang":"ts","className":""}}>
2054
-
2055
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2056
- {`To enable a tree table:`}
2057
- </p>
2058
-
2059
- <ol {...{"className":"pf-v6-c-content--ol pf-m-editorial ws-ol "}}>
2060
-
2061
-
2062
-
2063
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2064
- {`Pass the `}
2065
-
2066
- <code {...{"className":"ws-code "}}>
2067
- {`isTreeTable`}
2068
- </code>
2069
- {` prop to the `}
2070
-
2071
- <code {...{"className":"ws-code "}}>
2072
- {`Table`}
2073
- </code>
2074
- {` component`}
2075
- </li>
2076
-
2077
-
2078
-
2079
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2080
- {`Use a `}
2081
-
2082
- <code {...{"className":"ws-code "}}>
2083
- {`TreeRowWrapper`}
2084
- </code>
2085
- {` rather than `}
2086
-
2087
- <code {...{"className":"ws-code "}}>
2088
- {`Tr`}
2089
- </code>
2090
- </li>
2091
-
2092
-
2093
-
2094
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2095
- {`Pass the following `}
2096
-
2097
- <code {...{"className":"ws-code "}}>
2098
- {`props`}
2099
- </code>
2100
- {` to each row (both the `}
2101
-
2102
- <code {...{"className":"ws-code "}}>
2103
- {`TreeRowWrapper`}
2104
- </code>
2105
- {` and the `}
2106
-
2107
- <code {...{"className":"ws-code "}}>
2108
- {`treeRow`}
2109
- </code>
2110
- {` in the first column):`}
2111
-
2112
-
2113
-
2114
- <ul {...{"className":"pf-v6-c-content--ul pf-m-editorial ws-ul "}}>
2115
-
2116
-
2117
-
2118
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2119
-
2120
- <code {...{"className":"ws-code "}}>
2121
- {`isExpanded`}
2122
- </code>
2123
- {` - Flag indicating the node is expanded and its children are visible`}
2124
- </li>
2125
-
2126
-
2127
-
2128
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2129
-
2130
- <code {...{"className":"ws-code "}}>
2131
- {`isDetailsExpanded`}
2132
- </code>
2133
- {` - (optional) Flag indicating the row's details are visible in responsive view`}
2134
- </li>
2135
-
2136
-
2137
-
2138
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2139
-
2140
- <code {...{"className":"ws-code "}}>
2141
- {`isHidden`}
2142
- </code>
2143
- {` - Flag indicating the node's parent is expanded and this node is visible`}
2144
- </li>
2145
-
2146
-
2147
-
2148
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2149
-
2150
- <code {...{"className":"ws-code "}}>
2151
- {`aria-level`}
2152
- </code>
2153
- {` - number representing how many levels deep this node is nested`}
2154
- </li>
2155
-
2156
-
2157
-
2158
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2159
-
2160
- <code {...{"className":"ws-code "}}>
2161
- {`aria-posinset`}
2162
- </code>
2163
- {` - number representing where in the order this node sits amongst its siblings`}
2164
- </li>
2165
-
2166
-
2167
-
2168
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2169
-
2170
- <code {...{"className":"ws-code "}}>
2171
- {`aria-setsize`}
2172
- </code>
2173
- {` - number representing the number of children this node has`}
2174
- </li>
2175
-
2176
-
2177
-
2178
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2179
-
2180
- <code {...{"className":"ws-code "}}>
2181
- {`isChecked`}
2182
- </code>
2183
- {` - (optional) if this row uses checkboxes, flag indicating the checkbox checked`}
2184
- </li>
2185
-
2186
-
2187
-
2188
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2189
-
2190
- <code {...{"className":"ws-code "}}>
2191
- {`icon`}
2192
- </code>
2193
- {` - (optional) ReactNode icon to display before the row title`}
2194
- </li>
2195
-
2196
-
2197
-
2198
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2199
-
2200
- <code {...{"className":"ws-code "}}>
2201
- {`toggleAriaLabel`}
2202
- </code>
2203
- {` - (optional) accessible label for the expand/collapse children rows toggle arrow`}
2204
- </li>
2205
-
2206
-
2207
-
2208
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2209
-
2210
- <code {...{"className":"ws-code "}}>
2211
- {`checkAriaLabel`}
2212
- </code>
2213
- {` - (optional) accessible label for the checkbox`}
2214
- </li>
2215
-
2216
-
2217
-
2218
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2219
-
2220
- <code {...{"className":"ws-code "}}>
2221
- {`showDetailsAriaLabel`}
2222
- </code>
2223
- {` - (optional) accessible label for the show row details button in the responsive view`}
2224
- </li>
2225
-
2226
-
2227
- </ul>
2228
-
2229
-
2230
- </li>
2231
-
2232
-
2233
-
2234
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2235
- {`The first `}
2236
-
2237
- <code {...{"className":"ws-code "}}>
2238
- {`Td`}
2239
- </code>
2240
- {` in each row will pass the following to the `}
2241
-
2242
- <code {...{"className":"ws-code "}}>
2243
- {`treeRow`}
2244
- </code>
2245
- {` prop:`}
2246
-
2247
-
2248
-
2249
- <ul {...{"className":"pf-v6-c-content--ul pf-m-editorial ws-ul "}}>
2250
-
2251
-
2252
-
2253
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2254
-
2255
- <code {...{"className":"ws-code "}}>
2256
- {`onCollapse`}
2257
- </code>
2258
- {` - Callback when user expands/collapses a row to reveal/hide the row's children.`}
2259
- </li>
2260
-
2261
-
2262
-
2263
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2264
-
2265
- <code {...{"className":"ws-code "}}>
2266
- {`onCheckChange`}
2267
- </code>
2268
- {` - (optional) Callback when user changes the checkbox on a row.`}
2269
- </li>
2270
-
2271
-
2272
-
2273
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2274
-
2275
- <code {...{"className":"ws-code "}}>
2276
- {`onToggleRowDetails`}
2277
- </code>
2278
- {` - (optional) Callback when user shows/hides the row details in responsive view.`}
2279
- </li>
2280
-
2281
-
2282
-
2283
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2284
-
2285
- <code {...{"className":"ws-code "}}>
2286
- {`props`}
2287
- </code>
2288
- {` - (as defined above)`}
2289
- </li>
2290
-
2291
-
2292
-
2293
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2294
-
2295
- <code {...{"className":"ws-code "}}>
2296
- {`rowIndex`}
2297
- </code>
2298
- {` - number representing the index of the row`}
2299
- </li>
2300
-
2301
-
2302
- </ul>
2303
-
2304
-
2305
- </li>
2306
-
2307
-
2308
- </ol>
2309
-
2310
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2311
- {`Note: If this table is going to be tested using axe-core, the tests will flag the use of aria-level,
2312
- aria-posinset, and aria-setsize as violations. This is an intentional choice at this time so that
2313
- the voice over technologies will recognize the flat table structure as a tree.`}
2314
- </p>
2315
- </Example>,
2316
- 'Flat tree table with no inset': props =>
2317
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, TreeRowWrapper, TdProps } from '@patternfly/react-table';\nimport LeafIcon from '@patternfly/react-icons/dist/esm/icons/leaf-icon';\n\ninterface RepositoriesTreeNode {\n name: string;\n branches: string;\n pullRequests: string;\n workspaces: string;\n children?: RepositoriesTreeNode[];\n}\n\nexport const TableTreeNoInset: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const data: RepositoriesTreeNode[] = [\n {\n name: 'Repositories one',\n branches: 'Branch one',\n pullRequests: 'Pull request one',\n workspaces: 'Workplace one',\n children: []\n },\n {\n name: 'Repositories seven',\n branches: 'Branch seven',\n pullRequests: 'Pull request seven',\n workspaces: 'Workplace seven',\n children: []\n },\n {\n name: 'Repositories nine',\n branches: 'Branch nine',\n pullRequests: 'Pull request nine',\n workspaces: 'Workplace nine'\n }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces'\n };\n\n const [selectedNodeNames, setSelectedNodeNames] = useState<string[]>([]);\n const isNodeChecked = (node: RepositoriesTreeNode) => selectedNodeNames.includes(node.name);\n\n /**\n Recursive function which flattens the data into an array of flattened TreeRowWrapper components\n params:\n - nodes - array of a single level of tree nodes\n - level - number representing how deeply nested the current row is\n - posinset - position of the row relative to this row's siblings\n - currentRowIndex - position of the row relative to the entire table\n - isHidden - defaults to false, true if this row's parent is expanded\n */\n const renderRows = (\n [node, ...remainingNodes]: RepositoriesTreeNode[],\n level = 1,\n posinset = 1,\n rowIndex = 0,\n isHidden = false\n ): React.ReactNode[] => {\n if (!node) {\n return [];\n }\n const isChecked = isNodeChecked(node);\n const icon = <LeafIcon />;\n\n const treeRow: TdProps['treeRow'] = {\n onCollapse: () => {},\n onToggleRowDetails: () => {},\n onCheckChange: (_event, isChecking) => {\n setSelectedNodeNames((prevSelected) => {\n const otherSelectedNodeNames = prevSelected.filter((name) => name === node.name);\n return !isChecking ? otherSelectedNodeNames : [...otherSelectedNodeNames, node.name];\n });\n },\n rowIndex,\n props: {\n isHidden,\n 'aria-level': level,\n 'aria-posinset': posinset,\n 'aria-setsize': node.children ? node.children.length : 0,\n isChecked,\n checkboxId: `flat_checkbox_id_${node.name.toLowerCase().replace(/\\s+/g, '_')}`,\n icon\n }\n };\n\n const childRows =\n node.children && node.children.length ? renderRows(node.children, level + 1, 1, rowIndex + 1, isHidden) : [];\n\n return [\n <TreeRowWrapper key={node.name} row={{ props: treeRow.props }}>\n <Td dataLabel={columnNames.name} treeRow={treeRow}>\n {node.name}\n </Td>\n <Td dataLabel={columnNames.branches}>{node.branches}</Td>\n <Td dataLabel={columnNames.prs}>{node.pullRequests}</Td>\n <Td dataLabel={columnNames.workspaces}>{node.workspaces}</Td>\n </TreeRowWrapper>,\n ...childRows,\n ...renderRows(remainingNodes, level, posinset + 1, rowIndex + 1 + childRows.length, isHidden)\n ];\n };\n\n return (\n <Table isTreeTable aria-label=\"Tree table\" hasNoInset>\n <Thead>\n <Tr>\n <Th width={40}>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n </Tr>\n </Thead>\n <Tbody>{renderRows(data)}</Tbody>\n </Table>\n );\n};\n","title":"Flat tree table with no inset","lang":"ts","className":""}}>
2318
-
2319
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2320
- {`To remove the inset used to leave space for the expand/collapse toggle in a flat tree table, use the `}
2321
-
2322
- <code {...{"className":"ws-code "}}>
2323
- {`hasNoInset`}
2324
- </code>
2325
- {` prop on the `}
2326
-
2327
- <code {...{"className":"ws-code "}}>
2328
- {`Table`}
2329
- </code>
2330
- {` component.`}
2331
- </p>
2332
- </Example>,
2333
- 'Draggable row table': props =>
2334
- <Example {...pageData} {...props} {...{"code":"import { useRef, useState } from 'react';\nimport { Table, Thead, Tbody, Tr, Th, Td, TbodyProps, TrProps } from '@patternfly/react-table';\nimport styles from '@patternfly/react-styles/css/components/Table/table';\n\nexport const TableDraggable: React.FunctionComponent = () => {\n const [draggedItemId, setDraggedItemId] = useState<string | null>(null);\n const [draggingToItemIndex, setDraggingToItemIndex] = useState<number | null>(null);\n const [isDragging, setIsDragging] = useState(false);\n const [itemOrder, setItemOrder] = useState(['row1', 'row2', 'row3']);\n const [tempItemOrder, setTempItemOrder] = useState<string[]>([]);\n\n const bodyRef = useRef<HTMLTableSectionElement>(undefined);\n\n const onDragStart: TrProps['onDragStart'] = (evt) => {\n evt.dataTransfer.effectAllowed = 'move';\n evt.dataTransfer.setData('text/plain', evt.currentTarget.id);\n const draggedItemId = evt.currentTarget.id;\n\n evt.currentTarget.classList.add(styles.modifiers.ghostRow);\n evt.currentTarget.setAttribute('aria-pressed', 'true');\n\n setDraggedItemId(draggedItemId);\n setIsDragging(true);\n };\n\n const moveItem = (arr: string[], i1: string, toIndex: number) => {\n const fromIndex = arr.indexOf(i1);\n if (fromIndex === toIndex) {\n return arr;\n }\n const temp = arr.splice(fromIndex, 1);\n arr.splice(toIndex, 0, temp[0]);\n\n return arr;\n };\n\n const move = (itemOrder: string[]) => {\n const ulNode = bodyRef.current;\n const nodes = Array.from(ulNode.children);\n if (nodes.map((node) => node.id).every((id, i) => id === itemOrder[i])) {\n return;\n }\n while (ulNode.firstChild) {\n ulNode.removeChild(ulNode.lastChild);\n }\n\n itemOrder.forEach((id) => {\n ulNode.appendChild(nodes.find((n) => n.id === id));\n });\n };\n\n const onDragCancel = () => {\n Array.from(bodyRef.current.children).forEach((el) => {\n el.classList.remove(styles.modifiers.ghostRow);\n el.setAttribute('aria-pressed', 'false');\n });\n setDraggedItemId(null);\n setDraggingToItemIndex(null);\n setIsDragging(false);\n };\n\n const onDragLeave: TbodyProps['onDragLeave'] = (evt) => {\n if (!isValidDrop(evt)) {\n move(itemOrder);\n setDraggingToItemIndex(null);\n }\n };\n\n const isValidDrop = (evt: React.DragEvent<HTMLTableSectionElement | HTMLTableRowElement>) => {\n const ulRect = bodyRef.current.getBoundingClientRect();\n return (\n evt.clientX > ulRect.x &&\n evt.clientX < ulRect.x + ulRect.width &&\n evt.clientY > ulRect.y &&\n evt.clientY < ulRect.y + ulRect.height\n );\n };\n\n const onDrop: TrProps['onDrop'] = (evt) => {\n if (isValidDrop(evt)) {\n setItemOrder(tempItemOrder);\n } else {\n onDragCancel();\n }\n };\n\n const onDragOver: TbodyProps['onDragOver'] = (evt) => {\n evt.preventDefault();\n\n const curListItem = (evt.target as HTMLTableSectionElement).closest('tr');\n if (!curListItem || !bodyRef.current.contains(curListItem) || curListItem.id === draggedItemId) {\n return null;\n } else {\n const dragId = curListItem.id;\n const newDraggingToItemIndex = Array.from(bodyRef.current.children).findIndex((item) => item.id === dragId);\n if (newDraggingToItemIndex !== draggingToItemIndex) {\n const tempItemOrder = moveItem([...itemOrder], draggedItemId, newDraggingToItemIndex);\n move(tempItemOrder);\n setDraggingToItemIndex(newDraggingToItemIndex);\n setTempItemOrder(tempItemOrder);\n }\n }\n };\n\n const onDragEnd: TrProps['onDragEnd'] = (evt) => {\n const target = evt.target as HTMLTableRowElement;\n target.classList.remove(styles.modifiers.ghostRow);\n target.setAttribute('aria-pressed', 'false');\n setDraggedItemId(null);\n setDraggingToItemIndex(null);\n setIsDragging(false);\n };\n\n const columns = ['Repositories', 'Branches', 'Pull requests', 'Workspaces', 'Last commit'];\n const rows = [\n {\n id: 'row1',\n repository: 'one',\n branch: 'two',\n pullRequest: 'three',\n workspace: 'four',\n lastCommit: 'five'\n },\n {\n id: 'row2',\n repository: 'one -2',\n branch: null,\n pullRequest: null,\n workspace: 'four -2',\n lastCommit: 'five -2'\n },\n {\n id: 'row3',\n repository: 'one - 3',\n branch: 'two - 3',\n pullRequest: 'three - 3',\n workspace: 'four - 3',\n lastCommit: 'five - 3'\n }\n ];\n\n return (\n <Table aria-label=\"Draggable table\" className={isDragging ? styles.modifiers.dragOver : ''}>\n <Thead>\n <Tr>\n <Th screenReaderText=\"Drag and drop\" />\n {columns.map((column, columnIndex) => (\n <Th key={columnIndex}>{column}</Th>\n ))}\n </Tr>\n </Thead>\n <Tbody ref={bodyRef} onDragOver={onDragOver} onDrop={onDragOver} onDragLeave={onDragLeave}>\n {rows.map((row, rowIndex) => {\n const rowCellsToBuild = Object.keys(row).filter((rowCell) => rowCell !== 'id');\n return (\n <Tr key={rowIndex} id={row.id} draggable onDrop={onDrop} onDragEnd={onDragEnd} onDragStart={onDragStart}>\n <Td\n draggableRow={{\n id: `draggable-row-${row.id}`\n }}\n />\n {rowCellsToBuild.map((key, keyIndex) => (\n <Td key={`${rowIndex}_${keyIndex}`} dataLabel={columns[keyIndex]}>\n {row[key]}\n </Td>\n ))}\n </Tr>\n );\n })}\n </Tbody>\n </Table>\n );\n};\n","title":"Draggable row table","lang":"ts","isBeta":true,"className":""}}>
2335
-
2336
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2337
- {`To make a row draggable:`}
2338
- </p>
2339
-
2340
- <ol {...{"className":"pf-v6-c-content--ol pf-m-editorial ws-ol "}}>
2341
-
2342
-
2343
-
2344
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2345
- {`The table needs a draggable column.`}
2346
- </li>
2347
-
2348
-
2349
-
2350
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2351
- {`Each draggable `}
2352
-
2353
- <code {...{"className":"ws-code "}}>
2354
- {`Tr`}
2355
- </code>
2356
- {` needs to be passed `}
2357
-
2358
- <code {...{"className":"ws-code "}}>
2359
- {`draggable`}
2360
- </code>
2361
- {`, `}
2362
-
2363
- <code {...{"className":"ws-code "}}>
2364
- {`onDrop`}
2365
- </code>
2366
- {`, `}
2367
-
2368
- <code {...{"className":"ws-code "}}>
2369
- {`onDragEnd`}
2370
- </code>
2371
- {`, and `}
2372
-
2373
- <code {...{"className":"ws-code "}}>
2374
- {`onDragStart`}
2375
- </code>
2376
- {` props.`}
2377
- </li>
2378
-
2379
-
2380
-
2381
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2382
- {`The `}
2383
-
2384
- <code {...{"className":"ws-code "}}>
2385
- {`Tbody`}
2386
- </code>
2387
- {` needs `}
2388
-
2389
- <code {...{"className":"ws-code "}}>
2390
- {`onDragOver`}
2391
- </code>
2392
- {`, `}
2393
-
2394
- <code {...{"className":"ws-code "}}>
2395
- {`onDrop`}
2396
- </code>
2397
- {`, and `}
2398
-
2399
- <code {...{"className":"ws-code "}}>
2400
- {`onDragLeave`}
2401
- </code>
2402
- {` props.`}
2403
- </li>
2404
-
2405
-
2406
-
2407
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2408
- {`While the user is dragging a row, the `}
2409
-
2410
- <code {...{"className":"ws-code "}}>
2411
- {`pf-m-drag-over`}
2412
- </code>
2413
- {` class needs to be applied to `}
2414
-
2415
- <code {...{"className":"ws-code "}}>
2416
- {`Table`}
2417
- </code>
2418
- {`.`}
2419
- </li>
2420
-
2421
-
2422
-
2423
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2424
- {`The draggable `}
2425
-
2426
- <code {...{"className":"ws-code "}}>
2427
- {`Td`}
2428
- </code>
2429
- {` in each row needs a `}
2430
-
2431
- <code {...{"className":"ws-code "}}>
2432
- {`TdDraggableType`}
2433
- </code>
2434
- {` object passed to its `}
2435
-
2436
- <code {...{"className":"ws-code "}}>
2437
- {`draggable`}
2438
- </code>
2439
- {` prop.`}
2440
- </li>
2441
-
2442
-
2443
- </ol>
2444
- </Example>,
2445
- 'Sticky column': props =>
2446
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, InnerScrollContainer, ThProps } from '@patternfly/react-table';\n\ninterface Fact {\n name: string;\n state: string;\n detail1: string;\n detail2: string;\n detail3: string;\n detail4: string;\n detail5: string;\n detail6: string;\n detail7: string;\n}\n\nexport const TableStickyColumn: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const facts: Fact[] = Array.from({ length: 9 }, (_, index) => ({\n name: `Fact ${index + 1}`,\n state: `State ${index + 1}`,\n detail1: `Test cell ${index + 1}-3`,\n detail2: `Test cell ${index + 1}-4`,\n detail3: `Test cell ${index + 1}-5`,\n detail4: `Test cell ${index + 1}-6`,\n detail5: `Test cell ${index + 1}-7`,\n detail6: `Test cell ${index + 1}-8`,\n detail7: `Test cell ${index + 1}-9`\n }));\n\n const columnNames = {\n name: 'Fact',\n state: 'State',\n header3: 'Header 3',\n header4: 'Header 4',\n header5: 'Header 5',\n header6: 'Header 6',\n header7: 'Header 7',\n header8: 'Header 8',\n header9: 'Header 9'\n };\n\n // Index of the currently sorted column\n // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key\n // as the identifier of the sorted column. See the \"Compound expandable\" example.\n const [activeSortIndex, setActiveSortIndex] = useState<number | null>(null);\n\n // Sort direction of the currently sorted column\n const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null);\n\n // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.\n // This example is trivial since our data objects just contain strings, but if the data was more complex\n // this would be a place to return simplified string or number versions of each column to sort by.\n const getSortableRowValues = (fact: Fact): (string | number)[] => {\n const { name, state, detail1, detail2, detail3, detail4, detail5, detail6, detail7 } = fact;\n return [name, state, detail1, detail2, detail3, detail4, detail5, detail6, detail7];\n };\n\n // Note that we perform the sort as part of the component's render logic and not in onSort.\n // We shouldn't store the list of data in state because we don't want to have to sync that with props.\n let sortedFacts = facts;\n if (activeSortIndex !== null) {\n sortedFacts = facts.sort((a, b) => {\n const aValue = getSortableRowValues(a)[activeSortIndex];\n const bValue = getSortableRowValues(b)[activeSortIndex];\n if (aValue === bValue) {\n return 0;\n }\n if (activeSortDirection === 'asc') {\n return aValue > bValue ? 1 : -1;\n } else {\n return bValue > aValue ? 1 : -1;\n }\n });\n }\n\n const getSortParams = (columnIndex: number): ThProps['sort'] => ({\n sortBy: {\n index: activeSortIndex,\n direction: activeSortDirection\n },\n onSort: (_event, index, direction) => {\n setActiveSortIndex(index);\n setActiveSortDirection(direction);\n },\n columnIndex\n });\n\n return (\n <InnerScrollContainer>\n <Table aria-label=\"Sticky column table\" gridBreakPoint=\"\">\n <Thead>\n <Tr>\n <Th isStickyColumn hasRightBorder modifier=\"truncate\" sort={getSortParams(0)}>\n {columnNames.name}\n </Th>\n <Th modifier=\"truncate\" sort={getSortParams(1)}>\n {columnNames.state}\n </Th>\n <Th modifier=\"truncate\">{columnNames.header3}</Th>\n <Th modifier=\"truncate\">{columnNames.header4}</Th>\n <Th modifier=\"truncate\">{columnNames.header5}</Th>\n <Th modifier=\"truncate\">{columnNames.header6}</Th>\n <Th modifier=\"truncate\">{columnNames.header7}</Th>\n <Th modifier=\"truncate\">{columnNames.header8}</Th>\n <Th modifier=\"truncate\">{columnNames.header9}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {sortedFacts.map((fact) => (\n <Tr key={fact.name}>\n <Th isStickyColumn hasRightBorder modifier=\"truncate\">\n {fact.name}\n </Th>\n <Td modifier=\"nowrap\" dataLabel={columnNames.state}>\n {fact.state}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header3}>\n {fact.detail1}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header4}>\n {fact.detail2}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header5}>\n {fact.detail3}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header6}>\n {fact.detail4}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header7}>\n {fact.detail5}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header8}>\n {fact.detail6}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header9}>\n {fact.detail7}\n </Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </InnerScrollContainer>\n );\n};\n","title":"Sticky column","lang":"ts","className":""}}>
2447
-
2448
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2449
- {`To make a column sticky, wrap `}
2450
-
2451
- <code {...{"className":"ws-code "}}>
2452
- {`Table`}
2453
- </code>
2454
- {` with `}
2455
-
2456
- <code {...{"className":"ws-code "}}>
2457
- {`InnerScrollContainer`}
2458
- </code>
2459
- {` and add the following properties to the `}
2460
-
2461
- <code {...{"className":"ws-code "}}>
2462
- {`Th`}
2463
- </code>
2464
- {` or `}
2465
-
2466
- <code {...{"className":"ws-code "}}>
2467
- {`Td`}
2468
- </code>
2469
- {` that should be sticky:`}
2470
- </p>
2471
-
2472
- <ul {...{"className":"pf-v6-c-content--ul pf-m-editorial ws-ul "}}>
2473
-
2474
-
2475
-
2476
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2477
-
2478
- <code {...{"className":"ws-code "}}>
2479
- {`isStickyColumn`}
2480
- </code>
2481
- </li>
2482
-
2483
-
2484
-
2485
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2486
-
2487
- <code {...{"className":"ws-code "}}>
2488
- {`hasRightBorder`}
2489
- </code>
2490
- {` for a left-aligned sticky column, or `}
2491
-
2492
- <code {...{"className":"ws-code "}}>
2493
- {`hasLeftBorder`}
2494
- </code>
2495
- {` for a right-aligned sticky column.`}
2496
- </li>
2497
-
2498
-
2499
- </ul>
2500
-
2501
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2502
- {`To prevent the default text wrapping behavior and allow horizontal scrolling, all `}
2503
-
2504
- <code {...{"className":"ws-code "}}>
2505
- {`Th`}
2506
- </code>
2507
- {` or `}
2508
-
2509
- <code {...{"className":"ws-code "}}>
2510
- {`Td`}
2511
- </code>
2512
- {` cells should also have the `}
2513
-
2514
- <code {...{"className":"ws-code "}}>
2515
- {`modifier="nowrap"`}
2516
- </code>
2517
- {` property. To set the minimum width of the sticky column, use the `}
2518
-
2519
- <code {...{"className":"ws-code "}}>
2520
- {`stickyMinWidth`}
2521
- </code>
2522
- {` property. For multiple sticky columns, use the `}
2523
-
2524
- <code {...{"className":"ws-code "}}>
2525
- {`stickyLeftOffset`}
2526
- </code>
2527
- {` and `}
2528
-
2529
- <code {...{"className":"ws-code "}}>
2530
- {`stickyRightOffset`}
2531
- </code>
2532
- {` properties for additional left or right sticky columns.`}
2533
- </p>
2534
- </Example>,
2535
- 'Multiple left-aligned sticky columns': props =>
2536
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, InnerScrollContainer, ThProps } from '@patternfly/react-table';\nimport RhUiBlueprintIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-blueprint-icon';\n\ninterface Fact {\n name: string;\n state: string;\n detail1: string;\n detail2: string;\n detail3: string;\n detail4: string;\n detail5: string;\n detail6: string;\n detail7: string;\n}\n\nexport const TableMultipleStickyColumns: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const facts: Fact[] = Array.from({ length: 9 }, (_, index) => ({\n name: `Fact ${index + 1}`,\n state: `State ${index + 1}`,\n detail1: `Test cell ${index + 1}-3`,\n detail2: `Test cell ${index + 1}-4`,\n detail3: `Test cell ${index + 1}-5`,\n detail4: `Test cell ${index + 1}-6`,\n detail5: `Test cell ${index + 1}-7`,\n detail6: `Test cell ${index + 1}-8`,\n detail7: `Test cell ${index + 1}-9`\n }));\n\n const columnNames = {\n name: 'Fact',\n state: 'State',\n header3: 'Header 3',\n header4: 'Header 4',\n header5: 'Header 5',\n header6: 'Header 6',\n header7: 'Header 7',\n header8: 'Header 8',\n header9: 'Header 9'\n };\n\n // Index of the currently sorted column\n // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key\n // as the identifier of the sorted column. See the \"Compound expandable\" example.\n const [activeSortIndex, setActiveSortIndex] = useState<number | null>(null);\n\n // Sort direction of the currently sorted column\n const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null);\n\n // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.\n // This example is trivial since our data objects just contain strings, but if the data was more complex\n // this would be a place to return simplified string or number versions of each column to sort by.\n const getSortableRowValues = (fact: Fact): (string | number)[] => {\n const { name, state, detail1, detail2, detail3, detail4, detail5, detail6, detail7 } = fact;\n return [name, state, detail1, detail2, detail3, detail4, detail5, detail6, detail7];\n };\n\n // Note that we perform the sort as part of the component's render logic and not in onSort.\n // We shouldn't store the list of data in state because we don't want to have to sync that with props.\n let sortedFacts = facts;\n if (activeSortIndex !== null) {\n sortedFacts = facts.sort((a, b) => {\n const aValue = getSortableRowValues(a)[activeSortIndex];\n const bValue = getSortableRowValues(b)[activeSortIndex];\n if (aValue === bValue) {\n return 0;\n }\n if (activeSortDirection === 'asc') {\n return aValue > bValue ? 1 : -1;\n } else {\n return bValue > aValue ? 1 : -1;\n }\n });\n }\n\n const getSortParams = (columnIndex: number): ThProps['sort'] => ({\n sortBy: {\n index: activeSortIndex,\n direction: activeSortDirection\n },\n onSort: (_event, index, direction) => {\n setActiveSortIndex(index);\n setActiveSortDirection(direction);\n },\n columnIndex\n });\n\n return (\n <InnerScrollContainer>\n <Table aria-label=\"Multiple sticky column table\" gridBreakPoint=\"\">\n <Thead>\n <Tr>\n <Th isStickyColumn modifier=\"truncate\" sort={getSortParams(0)}>\n {columnNames.name}\n </Th>\n <Th\n isStickyColumn\n stickyMinWidth=\"120px\"\n stickyLeftOffset=\"120px\"\n hasRightBorder\n modifier=\"truncate\"\n sort={getSortParams(1)}\n >\n {columnNames.state}\n </Th>\n <Th modifier=\"truncate\">{columnNames.header3}</Th>\n <Th modifier=\"truncate\">{columnNames.header4}</Th>\n <Th modifier=\"truncate\">{columnNames.header5}</Th>\n <Th modifier=\"truncate\">{columnNames.header6}</Th>\n <Th modifier=\"truncate\">{columnNames.header7}</Th>\n <Th modifier=\"truncate\">{columnNames.header8}</Th>\n <Th modifier=\"truncate\">{columnNames.header9}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {sortedFacts.map((fact) => (\n <Tr key={fact.name}>\n <Th isStickyColumn modifier=\"truncate\">\n {fact.name}\n </Th>\n <Th isStickyColumn stickyMinWidth=\"120px\" stickyLeftOffset=\"120px\" modifier=\"truncate\" hasRightBorder>\n <RhUiBlueprintIcon />\n {` ${fact.state}`}\n </Th>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header3}>\n {fact.detail1}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header4}>\n {fact.detail2}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header5}>\n {fact.detail3}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header6}>\n {fact.detail4}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header7}>\n {fact.detail5}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header8}>\n {fact.detail6}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header9}>\n {fact.detail7}\n </Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </InnerScrollContainer>\n );\n};\n","title":"Multiple left-aligned sticky columns","lang":"ts","className":""}}>
2537
-
2538
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2539
- {`To make multiple left-aligned columns sticky:`}
2540
- </p>
2541
-
2542
- <ul {...{"className":"pf-v6-c-content--ul pf-m-editorial ws-ul "}}>
2543
-
2544
-
2545
-
2546
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2547
- {`wrap `}
2548
-
2549
- <code {...{"className":"ws-code "}}>
2550
- {`Table`}
2551
- </code>
2552
- {` with `}
2553
-
2554
- <code {...{"className":"ws-code "}}>
2555
- {`InnerScrollContainer`}
2556
- </code>
2557
- </li>
2558
-
2559
-
2560
-
2561
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2562
- {`add `}
2563
-
2564
- <code {...{"className":"ws-code "}}>
2565
- {`isStickyColumn`}
2566
- </code>
2567
- {` to all columns that should be sticky`}
2568
- </li>
2569
-
2570
-
2571
-
2572
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2573
- {`add `}
2574
-
2575
- <code {...{"className":"ws-code "}}>
2576
- {`hasRightBorder`}
2577
- </code>
2578
- {` to the rightmost sticky column`}
2579
- </li>
2580
-
2581
-
2582
-
2583
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2584
- {`add `}
2585
-
2586
- <code {...{"className":"ws-code "}}>
2587
- {`stickyLeftOffset`}
2588
- </code>
2589
- {` to each sticky column with a value that equals the combined width - set by `}
2590
-
2591
- <code {...{"className":"ws-code "}}>
2592
- {`stickyMindWidth`}
2593
- </code>
2594
- {` - of the previous sticky columns. The leftmost sticky column should have a value of `}
2595
-
2596
- <code {...{"className":"ws-code "}}>
2597
- {`0`}
2598
- </code>
2599
- {`, which is the default of this property.`}
2600
- </li>
2601
-
2602
-
2603
- </ul>
2604
-
2605
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2606
- {`To prevent the default text wrapping behavior and allow horizontal scrolling, all `}
2607
-
2608
- <code {...{"className":"ws-code "}}>
2609
- {`Th`}
2610
- </code>
2611
- {` or `}
2612
-
2613
- <code {...{"className":"ws-code "}}>
2614
- {`Td`}
2615
- </code>
2616
- {` cells should also have the `}
2617
-
2618
- <code {...{"className":"ws-code "}}>
2619
- {`modifier="nowrap"`}
2620
- </code>
2621
- {` property.`}
2622
- </p>
2623
- </Example>,
2624
- 'Multiple right-aligned sticky columns': props =>
2625
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, InnerScrollContainer, ThProps } from '@patternfly/react-table';\n\ninterface Fact {\n name: string;\n state: string;\n detail1: string;\n detail2: string;\n detail3: string;\n detail4: string;\n detail5: string;\n detail6: string;\n detail7: string;\n}\n\nexport const ComposableTableRightStickyColumn: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const facts: Fact[] = Array.from({ length: 9 }, (_, index) => ({\n name: `Fact ${index + 1}`,\n state: `State ${index + 1}`,\n detail1: `Test cell ${index + 1}-3`,\n detail2: `Test cell ${index + 1}-4`,\n detail3: `Test cell ${index + 1}-5`,\n detail4: `Test cell ${index + 1}-6`,\n detail5: `Test cell ${index + 1}-7`,\n detail6: `Test cell ${index + 1}-8`,\n detail7: `Test cell ${index + 1}-9`\n }));\n\n const columnNames = {\n name: 'Fact',\n state: 'State',\n header3: 'Header 3',\n header4: 'Header 4',\n header5: 'Header 5',\n header6: 'Header 6',\n header7: 'Header 7',\n header8: 'Header 8',\n header9: 'Header 9'\n };\n\n // Index of the currently sorted column\n // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key\n // as the identifier of the sorted column. See the \"Compound expandable\" example.\n const [activeSortIndex, setActiveSortIndex] = useState<number | null>(null);\n\n // Sort direction of the currently sorted column\n const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null);\n\n // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.\n // This example is trivial since our data objects just contain strings, but if the data was more complex\n // this would be a place to return simplified string or number versions of each column to sort by.\n const getSortableRowValues = (fact: Fact): (string | number)[] => {\n const { name, state, detail1, detail2, detail3, detail4, detail5, detail6, detail7 } = fact;\n return [name, state, detail1, detail2, detail3, detail4, detail5, detail6, detail7];\n };\n\n // Note that we perform the sort as part of the component's render logic and not in onSort.\n // We shouldn't store the list of data in state because we don't want to have to sync that with props.\n let sortedFacts = facts;\n if (activeSortIndex !== null) {\n sortedFacts = facts.sort((a, b) => {\n const aValue = getSortableRowValues(a)[activeSortIndex];\n const bValue = getSortableRowValues(b)[activeSortIndex];\n if (aValue === bValue) {\n return 0;\n }\n if (activeSortDirection === 'asc') {\n return aValue > bValue ? 1 : -1;\n } else {\n return bValue > aValue ? 1 : -1;\n }\n });\n }\n\n const getSortParams = (columnIndex: number): ThProps['sort'] => ({\n sortBy: {\n index: activeSortIndex,\n direction: activeSortDirection\n },\n onSort: (_event, index, direction) => {\n setActiveSortIndex(index);\n setActiveSortDirection(direction);\n },\n columnIndex\n });\n\n return (\n <InnerScrollContainer>\n <Table aria-label=\"Sticky column table\" gridBreakPoint=\"\">\n <Thead>\n <Tr>\n <Th modifier=\"truncate\" sort={getSortParams(0)}>\n {columnNames.name}\n </Th>\n <Th modifier=\"truncate\" sort={getSortParams(1)}>\n {columnNames.state}\n </Th>\n <Th modifier=\"truncate\">{columnNames.header3}</Th>\n <Th modifier=\"truncate\">{columnNames.header4}</Th>\n <Th modifier=\"truncate\">{columnNames.header5}</Th>\n <Th modifier=\"truncate\">{columnNames.header6}</Th>\n <Th modifier=\"truncate\">{columnNames.header7}</Th>\n <Th isStickyColumn hasLeftBorder stickyMinWidth=\"130px\" stickyRightOffset=\"130px\" modifier=\"truncate\">\n {columnNames.header8}\n </Th>\n <Th isStickyColumn stickyMinWidth=\"130px\" modifier=\"truncate\">\n {columnNames.header9}\n </Th>\n </Tr>\n </Thead>\n <Tbody>\n {sortedFacts.map((fact) => (\n <Tr key={fact.name}>\n <Th modifier=\"nowrap\">{fact.name}</Th>\n <Td modifier=\"nowrap\" dataLabel={columnNames.state}>\n {fact.state}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header3}>\n {fact.detail1}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header4}>\n {fact.detail2}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header5}>\n {fact.detail3}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header6}>\n {fact.detail4}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header7}>\n {fact.detail5}\n </Td>\n <Td\n isStickyColumn\n hasLeftBorder\n stickyMinWidth=\"130px\"\n stickyRightOffset=\"130px\"\n modifier=\"nowrap\"\n dataLabel={columnNames.header8}\n >\n {fact.detail6}\n </Td>\n <Td isStickyColumn stickyMinWidth=\"130px\" modifier=\"nowrap\" dataLabel={columnNames.header9}>\n {fact.detail7}\n </Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </InnerScrollContainer>\n );\n};\n","title":"Multiple right-aligned sticky columns","lang":"ts","className":""}}>
2626
-
2627
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2628
- {`To make multiple right-aligned columns sticky:`}
2629
- </p>
2630
-
2631
- <ul {...{"className":"pf-v6-c-content--ul pf-m-editorial ws-ul "}}>
2632
-
2633
-
2634
-
2635
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2636
- {`wrap `}
2637
-
2638
- <code {...{"className":"ws-code "}}>
2639
- {`Table`}
2640
- </code>
2641
- {` with `}
2642
-
2643
- <code {...{"className":"ws-code "}}>
2644
- {`InnerScrollContainer`}
2645
- </code>
2646
- </li>
2647
-
2648
-
2649
-
2650
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2651
- {`add `}
2652
-
2653
- <code {...{"className":"ws-code "}}>
2654
- {`isStickyColumn`}
2655
- </code>
2656
- {` to all columns that should be sticky`}
2657
- </li>
2658
-
2659
-
2660
-
2661
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2662
- {`add `}
2663
-
2664
- <code {...{"className":"ws-code "}}>
2665
- {`hasLeftBorder`}
2666
- </code>
2667
- {` to the leftmost sticky column`}
2668
- </li>
2669
-
2670
-
2671
-
2672
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2673
- {`add `}
2674
-
2675
- <code {...{"className":"ws-code "}}>
2676
- {`stickyRightOffset`}
2677
- </code>
2678
- {` to each sticky column with a value that equals the combined width - set by `}
2679
-
2680
- <code {...{"className":"ws-code "}}>
2681
- {`stickyMindWidth`}
2682
- </code>
2683
- {` - of the next sticky columns. The rightmost sticky column should have a value of `}
2684
-
2685
- <code {...{"className":"ws-code "}}>
2686
- {`0`}
2687
- </code>
2688
- {`, which is the default of this property.`}
2689
- </li>
2690
-
2691
-
2692
- </ul>
2693
-
2694
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2695
- {`To prevent the default text wrapping behavior and allow horizontal scrolling, all `}
2696
-
2697
- <code {...{"className":"ws-code "}}>
2698
- {`Th`}
2699
- </code>
2700
- {` or `}
2701
-
2702
- <code {...{"className":"ws-code "}}>
2703
- {`Td`}
2704
- </code>
2705
- {` cells should also have the `}
2706
-
2707
- <code {...{"className":"ws-code "}}>
2708
- {`modifier="nowrap"`}
2709
- </code>
2710
- {` property.`}
2711
- </p>
2712
- </Example>,
2713
- 'Dynamic sticky header': props =>
2714
- <Example {...pageData} {...props} {...{"code":"import { useLayoutEffect, useRef, useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, InnerScrollContainer } from '@patternfly/react-table';\nimport RhUiBlueprintIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-blueprint-icon';\n\ninterface Fact {\n name: string;\n state: string;\n detail1: string;\n detail2: string;\n detail3: string;\n detail4: string;\n detail5: string;\n detail6: string;\n detail7: string;\n}\n\nconst useIsStuckFromScrollParent = ({\n shouldTrack,\n scrollParentRef\n}: {\n /** Indicates whether to track the scroll top position of the scroll parent element */\n shouldTrack: boolean;\n /** Reference to the scroll parent element */\n scrollParentRef: React.RefObject<any>;\n}): boolean => {\n const [isStuck, setIsStuck] = useState(false);\n\n useLayoutEffect(() => {\n if (!shouldTrack) {\n setIsStuck(false);\n return;\n }\n\n const scrollElement = scrollParentRef.current;\n if (!scrollElement) {\n setIsStuck(false);\n return;\n }\n\n const syncFromScroll = () => {\n setIsStuck(scrollElement.scrollTop > 0);\n };\n syncFromScroll();\n scrollElement.addEventListener('scroll', syncFromScroll, { passive: true });\n return () => scrollElement.removeEventListener('scroll', syncFromScroll);\n }, [shouldTrack, scrollParentRef]);\n\n return isStuck;\n};\n\nexport const TableStickyHeaderDynamic: React.FunctionComponent = () => {\n const scrollContainerRef = useRef<HTMLDivElement>(null);\n const isStuck = useIsStuckFromScrollParent({ shouldTrack: true, scrollParentRef: scrollContainerRef });\n\n // In real usage, this data would come from some external source like an API via props.\n const facts: Fact[] = Array.from({ length: 9 }, (_, index) => ({\n name: `Fact ${index + 1}`,\n state: `State ${index + 1}`,\n detail1: `Test cell ${index + 1}-3`,\n detail2: `Test cell ${index + 1}-4`,\n detail3: `Test cell ${index + 1}-5`,\n detail4: `Test cell ${index + 1}-6`,\n detail5: `Test cell ${index + 1}-7`,\n detail6: `Test cell ${index + 1}-8`,\n detail7: `Test cell ${index + 1}-9`\n }));\n\n const columnNames = {\n name: 'Fact',\n state: 'State',\n header3: 'Header 3',\n header4: 'Header 4',\n header5: 'Header 5',\n header6: 'Header 6',\n header7: 'Header 7',\n header8: 'Header 8',\n header9: 'Header 9'\n };\n\n return (\n <div style={{ height: '400px' }}>\n <InnerScrollContainer ref={scrollContainerRef}>\n <Table\n aria-label=\"Dynamic sticky header table\"\n gridBreakPoint=\"\"\n isStickyHeaderBase\n isStickyHeaderStuck={isStuck}\n >\n <Thead>\n <Tr>\n <Th modifier=\"truncate\">{columnNames.name}</Th>\n <Th modifier=\"truncate\">{columnNames.state}</Th>\n <Th modifier=\"truncate\">{columnNames.header3}</Th>\n <Th modifier=\"truncate\">{columnNames.header4}</Th>\n <Th modifier=\"truncate\">{columnNames.header5}</Th>\n <Th modifier=\"truncate\">{columnNames.header6}</Th>\n <Th modifier=\"truncate\">{columnNames.header7}</Th>\n <Th modifier=\"truncate\">{columnNames.header8}</Th>\n <Th modifier=\"truncate\">{columnNames.header9}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {facts.map((fact) => (\n <Tr key={fact.name}>\n <Td modifier=\"nowrap\" dataLabel={columnNames.name}>\n {fact.name}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.state}>\n <RhUiBlueprintIcon />\n {` ${fact.state}`}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header3}>\n {fact.detail1}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header4}>\n {fact.detail2}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header5}>\n {fact.detail3}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header6}>\n {fact.detail4}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header7}>\n {fact.detail5}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header8}>\n {fact.detail6}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header9}>\n {fact.detail7}\n </Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </InnerScrollContainer>\n </div>\n );\n};\n","title":"Dynamic sticky header","lang":"ts","className":""}}>
2715
-
2716
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2717
- {`A sticky header may alternatively be implemented with two properties: `}
2718
-
2719
- <code {...{"className":"ws-code "}}>
2720
- {`isStickyHeaderBase`}
2721
- </code>
2722
- {` and `}
2723
-
2724
- <code {...{"className":"ws-code "}}>
2725
- {`isStickyHeaderStuck`}
2726
- </code>
2727
- {` - which allows separate control of the sticky position and sticky styling. `}
2728
-
2729
- <code {...{"className":"ws-code "}}>
2730
- {`isStickyHeaderBase`}
2731
- </code>
2732
- {` should always be applied to make the header position sticky, and `}
2733
-
2734
- <code {...{"className":"ws-code "}}>
2735
- {`isStickyHeaderStuck`}
2736
- </code>
2737
- {` may be applied dynamically to enable the sticky styling, such as when the sticky header is not at the top of the scroll parent as shown in the example.`}
2738
- </p>
2739
-
2740
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2741
-
2742
- <code {...{"className":"ws-code "}}>
2743
- {`isStickyHeader`}
2744
- </code>
2745
- {` acts as if both properties are present and true when applied, and is useful when dynamic sticky styling is not necessary.`}
2746
- </p>
2747
- </Example>,
2748
- 'Sticky columns and header': props =>
2749
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport {\n Table,\n Thead,\n Tr,\n Th,\n Tbody,\n Td,\n InnerScrollContainer,\n OuterScrollContainer,\n ThProps,\n ISortBy\n} from '@patternfly/react-table';\nimport RhUiBlueprintIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-blueprint-icon';\n\ninterface Fact {\n name: string;\n state: string;\n detail1: string;\n detail2: string;\n detail3: string;\n detail4: string;\n detail5: string;\n detail6: string;\n detail7: string;\n}\n\nexport const TableStickyColumnsAndHeader: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const facts: Fact[] = Array.from({ length: 9 }, (_, index) => ({\n name: `Fact ${index + 1}`,\n state: `State ${index + 1}`,\n detail1: `Test cell ${index + 1}-3`,\n detail2: `Test cell ${index + 1}-4`,\n detail3: `Test cell ${index + 1}-5`,\n detail4: `Test cell ${index + 1}-6`,\n detail5: `Test cell ${index + 1}-7`,\n detail6: `Test cell ${index + 1}-8`,\n detail7: `Test cell ${index + 1}-9`\n }));\n\n const columnNames = {\n name: 'Fact',\n state: 'State',\n header3: 'Header 3',\n header4: 'Header 4',\n header5: 'Header 5',\n header6: 'Header 6',\n header7: 'Header 7',\n header8: 'Header 8',\n header9: 'Header 9'\n };\n\n // Index of the currently sorted column\n // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key\n // as the identifier of the sorted column. See the \"Compound expandable\" example.\n const [activeSortIndex, setActiveSortIndex] = useState(-1);\n\n // Sort direction of the currently sorted column\n const [activeSortDirection, setActiveSortDirection] = useState<ISortBy['direction']>();\n\n // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.\n // This example is trivial since our data objects just contain strings, but if the data was more complex\n // this would be a place to return simplified string or number versions of each column to sort by.\n const getSortableRowValues = (fact: Fact): (string | number)[] => {\n const { name, state, detail1, detail2, detail3, detail4, detail5, detail6, detail7 } = fact;\n return [name, state, detail1, detail2, detail3, detail4, detail5, detail6, detail7];\n };\n\n // Note that we perform the sort as part of the component's render logic and not in onSort.\n // We shouldn't store the list of data in state because we don't want to have to sync that with props.\n let sortedFacts = facts;\n if (activeSortIndex > -1) {\n sortedFacts = facts.sort((a, b) => {\n const aValue = getSortableRowValues(a)[activeSortIndex];\n const bValue = getSortableRowValues(b)[activeSortIndex];\n if (aValue === bValue) {\n return 0;\n }\n if (activeSortDirection === 'asc') {\n return aValue > bValue ? 1 : -1;\n } else {\n return bValue > aValue ? 1 : -1;\n }\n });\n }\n\n const getSortParams = (columnIndex: number): ThProps['sort'] => ({\n sortBy: {\n index: activeSortIndex,\n direction: activeSortDirection\n },\n onSort: (_event, index, direction) => {\n setActiveSortIndex(index);\n setActiveSortDirection(direction);\n },\n columnIndex\n });\n\n return (\n <div style={{ height: '400px' }}>\n <OuterScrollContainer>\n <InnerScrollContainer>\n <Table aria-label=\"Sticky columns and header table\" gridBreakPoint=\"\" isStickyHeader>\n <Thead>\n <Tr>\n <Th isStickyColumn modifier=\"truncate\" sort={getSortParams(0)}>\n {columnNames.name}\n </Th>\n <Th\n isStickyColumn\n stickyMinWidth=\"120px\"\n stickyLeftOffset=\"120px\"\n hasRightBorder\n modifier=\"truncate\"\n sort={getSortParams(1)}\n >\n {columnNames.state}\n </Th>\n <Th modifier=\"truncate\">{columnNames.header3}</Th>\n <Th modifier=\"truncate\">{columnNames.header4}</Th>\n <Th modifier=\"truncate\">{columnNames.header5}</Th>\n <Th modifier=\"truncate\">{columnNames.header6}</Th>\n <Th modifier=\"truncate\">{columnNames.header7}</Th>\n <Th modifier=\"truncate\">{columnNames.header8}</Th>\n <Th modifier=\"truncate\">{columnNames.header9}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {sortedFacts.map((fact) => (\n <Tr key={fact.name}>\n <Th isStickyColumn modifier=\"truncate\">\n {fact.name}\n </Th>\n <Th isStickyColumn stickyMinWidth=\"120px\" stickyLeftOffset=\"120px\" modifier=\"truncate\" hasRightBorder>\n <RhUiBlueprintIcon />\n {` ${fact.state}`}\n </Th>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header3}>\n {fact.detail1}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header4}>\n {fact.detail2}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header5}>\n {fact.detail3}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header6}>\n {fact.detail4}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header7}>\n {fact.detail5}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header8}>\n {fact.detail6}\n </Td>\n <Td modifier=\"nowrap\" dataLabel={columnNames.header9}>\n {fact.detail7}\n </Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </InnerScrollContainer>\n </OuterScrollContainer>\n </div>\n );\n};\n","title":"Sticky columns and header","lang":"ts","className":""}}>
2750
-
2751
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2752
- {`To maintain proper sticky behavior across sticky columns and header, `}
2753
-
2754
- <code {...{"className":"ws-code "}}>
2755
- {`Table`}
2756
- </code>
2757
- {` must be wrapped with `}
2758
-
2759
- <code {...{"className":"ws-code "}}>
2760
- {`OuterScrollContainer`}
2761
- </code>
2762
- {` and `}
2763
-
2764
- <code {...{"className":"ws-code "}}>
2765
- {`InnerScrollContainer`}
2766
- </code>
2767
- {`.`}
2768
- </p>
2769
- </Example>,
2770
- 'Nested column headers': props =>
2771
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, InnerScrollContainer, ThProps } from '@patternfly/react-table';\nimport { Stack, StackItem, Timestamp } from '@patternfly/react-core';\ninterface PodConnection {\n source: {\n podName: string;\n port: { num: number; protocol: string };\n };\n destination: {\n podName: string;\n port: { num: number; protocol: string };\n };\n timestamp: string;\n protocol: string;\n flowRate: string;\n traffic: string;\n packets: number;\n}\n\nexport const TableNestedHeaders: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const connections: PodConnection[] = [\n {\n source: { podName: 'api-pod-source-name', port: { num: 443, protocol: 'HTTPS' } },\n destination: { podName: 'api-pod-destination-name', port: { num: 24, protocol: 'SMTP' } },\n timestamp: '2021-06-22T19:58:24.000Z',\n protocol: 'TCP',\n flowRate: '1.9 Kbps',\n traffic: '2.1 KB',\n packets: 3\n },\n {\n source: { podName: 'api-pod-source2-name', port: { num: 80, protocol: 'HTTP' } },\n destination: { podName: 'api-pod-destination2-name', port: { num: 24, protocol: 'SMTP' } },\n timestamp: '2021-06-22T21:42:01.000Z',\n protocol: 'UDP',\n flowRate: '3.4 Kbps',\n traffic: '6.1 KB',\n packets: 7\n }\n ];\n\n const columnNames = {\n pods: 'Pods',\n source: 'Source',\n destination: 'Destination',\n datetime: 'Date & Time',\n ports: 'Ports',\n protocol: 'Protocol',\n flowRate: 'Flow rate',\n traffic: 'Traffic',\n packets: 'Packets'\n };\n\n // Index of the currently sorted column\n // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key\n // as the identifier of the sorted column. See the \"Compound expandable\" example.\n const [activeSortIndex, setActiveSortIndex] = useState<number | null>(null);\n\n // Sort direction of the currently sorted column\n const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null);\n\n // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.\n const getSortableRowValues = (connection: PodConnection): (string | number)[] => {\n const { source, destination, timestamp, protocol, flowRate, traffic, packets } = connection;\n return [\n source.podName,\n destination.podName,\n timestamp,\n source.port.num,\n destination.port.num,\n protocol,\n flowRate,\n traffic,\n packets\n ];\n };\n\n // Note that we perform the sort as part of the component's render logic and not in onSort.\n // We shouldn't store the list of data in state because we don't want to have to sync that with props.\n let sortedConnections = connections;\n if (activeSortIndex !== null) {\n sortedConnections = connections.sort((a, b) => {\n const aValue = getSortableRowValues(a)[activeSortIndex];\n const bValue = getSortableRowValues(b)[activeSortIndex];\n if (typeof aValue === 'number') {\n // Numeric sort\n if (activeSortDirection === 'asc') {\n return (aValue as number) - (bValue as number);\n }\n return (bValue as number) - (aValue as number);\n } else {\n // String sort\n if (activeSortDirection === 'asc') {\n return (aValue as string).localeCompare(bValue as string);\n }\n return (bValue as string).localeCompare(aValue as string);\n }\n });\n }\n\n const getSortParams = (columnIndex: number): ThProps['sort'] => ({\n sortBy: {\n index: activeSortIndex,\n direction: activeSortDirection\n },\n onSort: (_event, index, direction) => {\n setActiveSortIndex(index);\n setActiveSortDirection(direction);\n },\n columnIndex\n });\n\n return (\n <InnerScrollContainer>\n <Table aria-label=\"Nested column headers table\" gridBreakPoint=\"\">\n <Thead hasNestedHeader>\n <Tr>\n <Th hasRightBorder colSpan={3}>\n {columnNames.pods}\n </Th>\n <Th hasRightBorder colSpan={2}>\n {columnNames.ports}\n </Th>\n <Th modifier=\"fitContent\" hasRightBorder rowSpan={2} sort={getSortParams(5)}>\n {columnNames.protocol}\n </Th>\n <Th modifier=\"fitContent\" hasRightBorder rowSpan={2} sort={getSortParams(6)}>\n {columnNames.flowRate}\n </Th>\n <Th modifier=\"fitContent\" hasRightBorder rowSpan={2} sort={getSortParams(7)}>\n {columnNames.traffic}\n </Th>\n <Th modifier=\"fitContent\" rowSpan={2} sort={getSortParams(8)}>\n {columnNames.packets}\n </Th>\n </Tr>\n <Tr>\n <Th isSubheader sort={getSortParams(0)}>\n {columnNames.source}\n </Th>\n <Th isSubheader sort={getSortParams(1)}>\n {columnNames.destination}\n </Th>\n <Th isSubheader modifier=\"fitContent\" hasRightBorder sort={getSortParams(2)}>\n {columnNames.datetime}\n </Th>\n <Th isSubheader modifier=\"fitContent\" sort={getSortParams(3)}>\n {columnNames.source}\n </Th>\n <Th isSubheader modifier=\"fitContent\" hasRightBorder sort={getSortParams(4)}>\n {columnNames.destination}\n </Th>\n </Tr>\n </Thead>\n <Tbody>\n {sortedConnections.map((connection) => (\n <Tr key={connection.source.podName}>\n <Td dataLabel={columnNames.source}>{connection.source.podName}</Td>\n <Td dataLabel={columnNames.destination}>{connection.destination.podName}</Td>\\\n <Td dataLabel={columnNames.datetime}>\n <div>\n <Timestamp dateFormat=\"full\" timeFormat=\"medium\" date={new Date(connection.timestamp)} />\n </div>\n </Td>\n <Td dataLabel={columnNames.source}>\n <Stack>\n <StackItem>\n <span>{connection.source.port.num}</span>\n </StackItem>\n <StackItem>\n <small>({connection.source.port.protocol})</small>\n </StackItem>\n </Stack>\n </Td>\n <Td dataLabel={columnNames.destination}>\n <Stack>\n <StackItem>\n <span>{connection.destination.port.num}</span>\n </StackItem>\n <StackItem>\n <small>({connection.destination.port.protocol})</small>\n </StackItem>\n </Stack>\n </Td>\n <Td dataLabel={columnNames.protocol}>{connection.protocol}</Td>\n <Td dataLabel={columnNames.flowRate}>{connection.flowRate}</Td>\n <Td dataLabel={columnNames.traffic}>{connection.traffic}</Td>\n <Td dataLabel={columnNames.packets}>{connection.packets}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </InnerScrollContainer>\n );\n};\n","title":"Nested column headers","lang":"ts","className":""}}>
2772
-
2773
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2774
- {`To make a nested column header:`}
2775
- </p>
2776
-
2777
- <ol {...{"className":"pf-v6-c-content--ol pf-m-editorial ws-ol "}}>
2778
-
2779
-
2780
-
2781
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2782
- {`Wrap `}
2783
-
2784
- <code {...{"className":"ws-code "}}>
2785
- {`Table`}
2786
- </code>
2787
- {` with `}
2788
-
2789
- <code {...{"className":"ws-code "}}>
2790
- {`InnerScrollContainer`}
2791
- </code>
2792
- {`.`}
2793
- </li>
2794
-
2795
-
2796
-
2797
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2798
- {`Pass `}
2799
-
2800
- <code {...{"className":"ws-code "}}>
2801
- {`nestedHeaderColumnSpans`}
2802
- </code>
2803
- {` to `}
2804
-
2805
- <code {...{"className":"ws-code "}}>
2806
- {`Table`}
2807
- </code>
2808
- {`. `}
2809
-
2810
- <code {...{"className":"ws-code "}}>
2811
- {`nestedHeaderColumnSpans`}
2812
- </code>
2813
- {` is an array of numbers representing the column spans of the top level columns to `}
2814
-
2815
- <code {...{"className":"ws-code "}}>
2816
- {`Table`}
2817
- </code>
2818
- {`, where each number is equal to the number of sub columns for a column, or `}
2819
-
2820
- <code {...{"className":"ws-code "}}>
2821
- {`1`}
2822
- </code>
2823
- {` if a column contains no sub columns.`}
2824
- </li>
2825
-
2826
-
2827
-
2828
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2829
- {`Pass `}
2830
-
2831
- <code {...{"className":"ws-code "}}>
2832
- {`hasNestedHeader`}
2833
- </code>
2834
- {` to `}
2835
-
2836
- <code {...{"className":"ws-code "}}>
2837
- {`Thead`}
2838
- </code>
2839
- {`.`}
2840
- </li>
2841
-
2842
-
2843
-
2844
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
2845
- {`Pass two `}
2846
-
2847
- <code {...{"className":"ws-code "}}>
2848
- {`Tr`}
2849
- </code>
2850
- {` as children of `}
2851
-
2852
- <code {...{"className":"ws-code "}}>
2853
- {`Thead`}
2854
- </code>
2855
- {`.`}
2856
- </li>
2857
-
2858
-
2859
- </ol>
2860
-
2861
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2862
- {`The first `}
2863
-
2864
- <code {...{"className":"ws-code "}}>
2865
- {`Tr`}
2866
- </code>
2867
- {` represents the top level of columns, and each must pass either `}
2868
-
2869
- <code {...{"className":"ws-code "}}>
2870
- {`rowSpan`}
2871
- </code>
2872
- {` if the column does not contain sub columns or `}
2873
-
2874
- <code {...{"className":"ws-code "}}>
2875
- {`colSpan`}
2876
- </code>
2877
- {` if the column contains sub columns. The value of `}
2878
-
2879
- <code {...{"className":"ws-code "}}>
2880
- {`rowSpan`}
2881
- </code>
2882
- {` is equal to the number of rows the nested header will span, typically `}
2883
-
2884
- <code {...{"className":"ws-code "}}>
2885
- {`2`}
2886
- </code>
2887
- {`, and the value of `}
2888
-
2889
- <code {...{"className":"ws-code "}}>
2890
- {`colSpan`}
2891
- </code>
2892
- {` is equal to the number of sub columns in a column. Each `}
2893
-
2894
- <code {...{"className":"ws-code "}}>
2895
- {`Th`}
2896
- </code>
2897
- {` except the last should also pass `}
2898
-
2899
- <code {...{"className":"ws-code "}}>
2900
- {`hasRightBorder`}
2901
- </code>
2902
- {`.`}
2903
- </p>
2904
-
2905
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2906
- {`The second `}
2907
-
2908
- <code {...{"className":"ws-code "}}>
2909
- {`Tr`}
2910
- </code>
2911
- {` represents the second level of sub columns. The `}
2912
-
2913
- <code {...{"className":"ws-code "}}>
2914
- {`Th`}
2915
- </code>
2916
- {` in this row each should pass `}
2917
-
2918
- <code {...{"className":"ws-code "}}>
2919
- {`isSubHeader`}
2920
- </code>
2921
- {`, and the last sub column of a column should also pass `}
2922
-
2923
- <code {...{"className":"ws-code "}}>
2924
- {`hasRightBorder`}
2925
- </code>
2926
- {`.`}
2927
- </p>
2928
- </Example>,
2929
- 'Nested column headers and expandable rows': props =>
2930
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, InnerScrollContainer, ExpandableRowContent } from '@patternfly/react-table';\nimport { Button } from '@patternfly/react-core';\n\ninterface Team {\n name: string;\n members: {\n lead: string;\n interaction: string;\n visual: string;\n };\n email: string;\n description: string;\n}\n\nexport const TableNestedExpandable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const teams: Team[] = [\n {\n name: 'Developer program 1',\n members: { lead: 'Person 1', interaction: 'Person 2', visual: 'Person 3' },\n email: 'devteam1@example.com',\n description:\n 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'\n },\n {\n name: 'Developer program 2',\n members: { lead: 'Person A', interaction: 'Person B', visual: 'Person C' },\n email: 'devteam2@example.com',\n description:\n 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'\n },\n {\n name: 'Developer program 3',\n members: { lead: 'Person X', interaction: 'Person Y', visual: 'Person Z' },\n email: 'devteam3@example.com',\n description:\n 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'\n }\n ];\n\n const columnNames = {\n team: 'Team',\n members: 'Members',\n lead: 'Design Lead',\n interaction: 'Interaction Designer',\n visual: 'Visual Designer',\n contact: 'Contact'\n };\n\n // In this example, expanded rows are tracked by the team names from each row. This could be any unique identifier.\n // This is to prevent state from being based on row order index in case we later add sorting.\n // Note that this behavior is very similar to selection state.\n const [expandedTeamNames, setExpandedTeamNames] = useState<string[]>([]);\n const setTeamExpanded = (team: Team, isExpanding = true) =>\n setExpandedTeamNames((prevExpanded) => {\n const otherExpandedTeamNames = prevExpanded.filter((t) => t !== team.name);\n return isExpanding ? [...otherExpandedTeamNames, team.name] : otherExpandedTeamNames;\n });\n const isTeamExpanded = (team: Team) => expandedTeamNames.includes(team.name);\n\n return (\n <InnerScrollContainer>\n <Table isExpandable hasAnimations aria-label=\"Nested column headers with expandable rows table\" gridBreakPoint=\"\">\n <Thead hasNestedHeader>\n <Tr>\n <Th screenReaderText=\"Row expansion\" rowSpan={2} />\n <Th width={35} rowSpan={2} hasRightBorder>\n {columnNames.team}\n </Th>\n <Th colSpan={3} hasRightBorder>\n {columnNames.members}\n </Th>\n <Th width={25} rowSpan={2}>\n {columnNames.contact}\n </Th>\n </Tr>\n <Tr resetOffset>\n <Th isSubheader>{columnNames.lead}</Th>\n <Th isSubheader>{columnNames.interaction}</Th>\n <Th isSubheader hasRightBorder>\n {columnNames.visual}\n </Th>\n </Tr>\n </Thead>\n {teams.map((team, rowIndex) => (\n <Tbody key={team.name} isExpanded={isTeamExpanded(team)}>\n <Tr isContentExpanded={isTeamExpanded(team)}>\n <Td\n expand={{\n rowIndex,\n isExpanded: isTeamExpanded(team),\n onToggle: () => setTeamExpanded(team, !isTeamExpanded(team)),\n expandId: 'composable-nested-expandable-example'\n }}\n />\n <Td dataLabel={columnNames.team}>{team.name}</Td>\n <Td dataLabel={columnNames.lead}>{team.members.lead}</Td>\n <Td dataLabel={columnNames.interaction}>{team.members.interaction}</Td>\n <Td dataLabel={columnNames.visual}>{team.members.visual}</Td>\n <Td dataLabel={columnNames.contact}>\n <Button variant=\"link\" component=\"a\" href={`mailto:${team.email}`} isInline>\n Email team {rowIndex}\n </Button>\n </Td>\n </Tr>\n <Tr isExpanded={isTeamExpanded(team)}>\n <Td dataLabel={`Team ${team.name} description`} colSpan={6}>\n <ExpandableRowContent>{team.description}</ExpandableRowContent>\n </Td>\n </Tr>\n </Tbody>\n ))}\n </Table>\n </InnerScrollContainer>\n );\n};\n","title":"Nested column headers and expandable rows","lang":"ts","className":""}}>
2931
-
2932
- </Example>,
2933
- 'Expandable with nested table': props =>
2934
- <Example {...pageData} {...props} {...{"code":"/* eslint-disable no-console */\nimport { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, ExpandableRowContent, ActionsColumn, IAction } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n nestedComponent?: React.ReactNode;\n link?: React.ReactNode;\n noPadding?: boolean;\n}\ninterface NestedRepository {\n name: string;\n branches: string | null;\n prs: string | null;\n workspaces: string | null;\n lastCommit: string | null;\n}\n\nconst NestedReposTable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const prs: NestedRepository[] = [\n { name: 'Repository 1', branches: '25', prs: '25', workspaces: '5', lastCommit: '2 days ago' },\n { name: 'Repository 2', branches: '25', prs: '25', workspaces: '5', lastCommit: '2 days ago' },\n { name: 'Repository 3', branches: '25', prs: '25', workspaces: '5', lastCommit: '2 days ago' },\n { name: 'Repository 4', branches: '25', prs: '25', workspaces: '5', lastCommit: '2 days ago' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n return (\n <Table aria-label=\"Simple table\" variant=\"compact\">\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {prs.map((repo) => (\n <Tr key={repo.name}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n\nexport const TableExpandable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'Node 1', branches: '10', prs: '2', nestedComponent: <NestedReposTable />, link: <a>Link 1</a> },\n { name: 'Node 2', branches: '3', prs: '4', link: <a>Link 2</a> },\n {\n name: 'Node 3',\n branches: '11',\n prs: '7',\n nestedComponent: (\n <p>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore\n magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\n consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla\n pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id\n est laborum.\n </p>\n ),\n link: <a>Link 3</a>\n },\n {\n name: 'Node 4',\n branches: '11',\n prs: '7',\n nestedComponent: 'Expandable row content has no padding.',\n link: <a>Link 4</a>,\n noPadding: true\n }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n link: 'Link',\n action: 'Action'\n };\n // In this example, expanded rows are tracked by the repo names from each row. This could be any unique identifier.\n // This is to prevent state from being based on row order index in case we later add sorting.\n // Note that this behavior is very similar to selection state.\n const initialExpandedRepoNames = repositories.filter((repo) => !!repo.nestedComponent).map((repo) => repo.name); // Default to all expanded\n const [expandedRepoNames, setExpandedRepoNames] = useState<string[]>(initialExpandedRepoNames);\n const setRepoExpanded = (repo: Repository, isExpanding = true) =>\n setExpandedRepoNames((prevExpanded) => {\n const otherExpandedRepoNames = prevExpanded.filter((r) => r !== repo.name);\n return isExpanding ? [...otherExpandedRepoNames, repo.name] : otherExpandedRepoNames;\n });\n const isRepoExpanded = (repo: Repository) => expandedRepoNames.includes(repo.name);\n\n const defaultActions = (repo: Repository): IAction[] => [\n {\n title: 'Some action',\n onClick: () => console.log(`clicked on Some action, on row ${repo.name}`)\n },\n {\n title: <a href=\"https://www.patternfly.org\">Link action</a>\n },\n {\n isSeparator: true\n },\n {\n title: 'Third action',\n onClick: () => console.log(`clicked on Third action, on row ${repo.name}`)\n }\n ];\n\n return (\n <Table isExpandable hasAnimations aria-label=\"Simple table\">\n <Thead>\n <Tr>\n <Th screenReaderText=\"Row expansion\" />\n <Th width={20}>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.link}</Th>\n <Th>{columnNames.action}</Th>\n </Tr>\n </Thead>\n {repositories.map((repo, rowIndex) => (\n <Tbody key={repo.name} isExpanded={isRepoExpanded(repo)}>\n <Tr isContentExpanded={isRepoExpanded(repo)}>\n <Td\n expand={\n repo.nestedComponent\n ? {\n rowIndex,\n isExpanded: isRepoExpanded(repo),\n onToggle: () => setRepoExpanded(repo, !isRepoExpanded(repo)),\n expandId: 'composable-nested-table-expandable-example'\n }\n : undefined\n }\n />\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.link}>{repo.link}</Td>\n <Td dataLabel={columnNames.action}>\n <ActionsColumn items={defaultActions(repo)} />\n </Td>\n </Tr>\n {repo.nestedComponent ? (\n <Tr isExpanded={isRepoExpanded(repo)}>\n <Td\n noPadding={repo.noPadding}\n dataLabel={`${columnNames.name} expended`}\n colSpan={Object.keys(columnNames).length + 1}\n >\n <ExpandableRowContent>{repo.nestedComponent}</ExpandableRowContent>\n </Td>\n </Tr>\n ) : null}\n </Tbody>\n ))}\n </Table>\n );\n};\n","title":"Expandable with nested table","lang":"ts","className":""}}>
2935
-
2936
- </Example>,
2937
- 'Nested sticky header': props =>
2938
- <Example {...pageData} {...props} {...{"code":"import { useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, InnerScrollContainer, ThProps } from '@patternfly/react-table';\nimport { Stack, StackItem, Timestamp } from '@patternfly/react-core';\ninterface PodConnection {\n source: {\n podName: string;\n port: { num: number; protocol: string };\n };\n destination: {\n podName: string;\n port: { num: number; protocol: string };\n };\n timestamp: string;\n protocol: string;\n flowRate: string;\n traffic: string;\n packets: number;\n}\n\nexport const TableNestedHeaders: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const connections: PodConnection[] = [\n {\n source: { podName: 'api-pod-source-name', port: { num: 443, protocol: 'HTTPS' } },\n destination: { podName: 'api-pod-destination-name', port: { num: 24, protocol: 'SMTP' } },\n timestamp: '2021-06-22T19:58:24.000Z',\n protocol: 'TCP',\n flowRate: '1.9 Kbps',\n traffic: '2.1 KB',\n packets: 3\n },\n {\n source: { podName: 'api-pod-source2-name', port: { num: 80, protocol: 'HTTP' } },\n destination: { podName: 'api-pod-destination2-name', port: { num: 24, protocol: 'SMTP' } },\n timestamp: '2021-06-22T21:42:01.000Z',\n protocol: 'UDP',\n flowRate: '3.4 Kbps',\n traffic: '6.1 KB',\n packets: 7\n }\n ];\n\n const columnNames = {\n pods: 'Pods',\n source: 'Source',\n destination: 'Destination',\n datetime: 'Date & Time',\n ports: 'Ports',\n protocol: 'Protocol',\n flowRate: 'Flow rate',\n traffic: 'Traffic',\n packets: 'Packets'\n };\n\n // Index of the currently sorted column\n // Note: if you intend to make columns reorderable, you may instead want to use a non-numeric key\n // as the identifier of the sorted column. See the \"Compound expandable\" example.\n const [activeSortIndex, setActiveSortIndex] = useState<number | null>(null);\n\n // Sort direction of the currently sorted column\n const [activeSortDirection, setActiveSortDirection] = useState<'asc' | 'desc' | null>(null);\n\n // Since OnSort specifies sorted columns by index, we need sortable values for our object by column index.\n const getSortableRowValues = (connection: PodConnection): (string | number)[] => {\n const { source, destination, timestamp, protocol, flowRate, traffic, packets } = connection;\n return [\n source.podName,\n destination.podName,\n timestamp,\n source.port.num,\n destination.port.num,\n protocol,\n flowRate,\n traffic,\n packets\n ];\n };\n\n // Note that we perform the sort as part of the component's render logic and not in onSort.\n // We shouldn't store the list of data in state because we don't want to have to sync that with props.\n let sortedConnections = connections;\n if (activeSortIndex !== null) {\n sortedConnections = connections.sort((a, b) => {\n const aValue = getSortableRowValues(a)[activeSortIndex];\n const bValue = getSortableRowValues(b)[activeSortIndex];\n if (typeof aValue === 'number') {\n // Numeric sort\n if (activeSortDirection === 'asc') {\n return (aValue as number) - (bValue as number);\n }\n return (bValue as number) - (aValue as number);\n } else {\n // String sort\n if (activeSortDirection === 'asc') {\n return (aValue as string).localeCompare(bValue as string);\n }\n return (bValue as string).localeCompare(aValue as string);\n }\n });\n }\n\n const getSortParams = (columnIndex: number): ThProps['sort'] => ({\n sortBy: {\n index: activeSortIndex as number,\n direction: activeSortDirection as 'asc' | 'desc'\n },\n onSort: (_event, index, direction) => {\n setActiveSortIndex(index);\n setActiveSortDirection(direction);\n },\n columnIndex\n });\n\n return (\n <InnerScrollContainer>\n <Table aria-label=\"Nested column headers table\" gridBreakPoint=\"\" isStickyHeader>\n <Thead hasNestedHeader>\n <Tr>\n <Th hasRightBorder colSpan={3}>\n {columnNames.pods}\n </Th>\n <Th hasRightBorder colSpan={2}>\n {columnNames.ports}\n </Th>\n <Th modifier=\"fitContent\" hasRightBorder rowSpan={2} sort={getSortParams(5)}>\n {columnNames.protocol}\n </Th>\n <Th modifier=\"fitContent\" hasRightBorder rowSpan={2} sort={getSortParams(6)}>\n {columnNames.flowRate}\n </Th>\n <Th modifier=\"fitContent\" hasRightBorder rowSpan={2} sort={getSortParams(7)}>\n {columnNames.traffic}\n </Th>\n <Th modifier=\"fitContent\" rowSpan={2} sort={getSortParams(8)}>\n {columnNames.packets}\n </Th>\n </Tr>\n <Tr>\n <Th isSubheader sort={getSortParams(0)}>\n {columnNames.source}\n </Th>\n <Th isSubheader sort={getSortParams(1)}>\n {columnNames.destination}\n </Th>\n <Th isSubheader modifier=\"fitContent\" hasRightBorder sort={getSortParams(2)}>\n {columnNames.datetime}\n </Th>\n <Th isSubheader modifier=\"fitContent\" sort={getSortParams(3)}>\n {columnNames.source}\n </Th>\n <Th isSubheader modifier=\"fitContent\" hasRightBorder sort={getSortParams(4)}>\n {columnNames.destination}\n </Th>\n </Tr>\n </Thead>\n <Tbody>\n {sortedConnections.map((connection) => (\n <Tr key={connection.source.podName}>\n <Td dataLabel={columnNames.source}>{connection.source.podName}</Td>\n <Td dataLabel={columnNames.destination}>{connection.destination.podName}</Td>\\\n <Td dataLabel={columnNames.datetime}>\n <div>\n <Timestamp dateFormat=\"full\" timeFormat=\"medium\" date={new Date(connection.timestamp)} />\n </div>\n </Td>\n <Td dataLabel={columnNames.source}>\n <Stack>\n <StackItem>\n <span>{connection.source.port.num}</span>\n </StackItem>\n <StackItem>\n <small>({connection.source.port.protocol})</small>\n </StackItem>\n </Stack>\n </Td>\n <Td dataLabel={columnNames.destination}>\n <Stack>\n <StackItem>\n <span>{connection.destination.port.num}</span>\n </StackItem>\n <StackItem>\n <small>({connection.destination.port.protocol})</small>\n </StackItem>\n </Stack>\n </Td>\n <Td dataLabel={columnNames.protocol}>{connection.protocol}</Td>\n <Td dataLabel={columnNames.flowRate}>{connection.flowRate}</Td>\n <Td dataLabel={columnNames.traffic}>{connection.traffic}</Td>\n <Td dataLabel={columnNames.packets}>{connection.packets}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n </InnerScrollContainer>\n );\n};\n","title":"Nested sticky header","lang":"ts","className":""}}>
2939
-
2940
- </Example>,
2941
- 'Striped': props =>
2942
- <Example {...pageData} {...props} {...{"code":"import { Table, Caption, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: number;\n prs: number;\n workspaces: number;\n lastCommit: string;\n}\n\nexport const TableStriped: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'Repository 1', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' },\n { name: 'Repository 2', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' },\n { name: 'Repository 3', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' },\n { name: 'Repository 4', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n return (\n <Table aria-label=\"Simple table\" isStriped>\n <Caption>Simple striped table using composable components</Caption>\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo) => (\n <Tr key={repo.name}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Striped","lang":"ts","className":""}}>
2943
-
2944
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2945
- {`To apply striping to a basic table, add the `}
2946
-
2947
- <code {...{"className":"ws-code "}}>
2948
- {`isStriped`}
2949
- </code>
2950
- {` property to `}
2951
-
2952
- <code {...{"className":"ws-code "}}>
2953
- {`Table`}
2954
- </code>
2955
- {`.`}
2956
- </p>
2957
- </Example>,
2958
- 'Striped expandable': props =>
2959
- <Example {...pageData} {...props} {...{"code":"import { Fragment, useState } from 'react';\nimport { Table, Thead, Tr, Th, Tbody, Td, ExpandableRowContent } from '@patternfly/react-table';\nimport { Checkbox } from '@patternfly/react-core';\n\ninterface Repository {\n name: string;\n branches: string;\n prs: string;\n workspaces: string;\n lastCommit: string;\n details?: {\n detail1?: string;\n detail2?: string;\n detail3?: string;\n detailFormat: 0 | 1 | 2 | 3;\n };\n}\n\nexport const TableStripedExpandable: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'one', branches: 'two', prs: 'a', workspaces: 'four', lastCommit: 'five' },\n {\n name: 'parent 1',\n branches: 'two',\n prs: 'k',\n workspaces: 'four',\n lastCommit: 'five',\n // This `details` structure is just for this example. You can drive expanded content from any kind of data.\n details: { detailFormat: 0, detail1: 'single cell' }\n },\n {\n name: 'parent 2',\n branches: 'two',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: { detailFormat: 1, detail1: 'single cell - fullWidth' }\n },\n {\n name: 'parent 3',\n branches: '2',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: { detailFormat: 2, detail1: 'single cell - noPadding' }\n },\n {\n name: 'parent 4',\n branches: '2',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: { detailFormat: 3, detail1: 'single cell - fullWidth & noPadding' }\n },\n {\n name: 'parent 5',\n branches: '2',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: {\n detailFormat: 0,\n detail1: \"spans 'Repositories and 'Branches'\",\n detail2: \"spans 'Pull requests' and 'Workspaces', and 'Last commit'\"\n }\n },\n {\n name: 'parent 6',\n branches: '2',\n prs: 'b',\n workspaces: 'four',\n lastCommit: 'five',\n details: {\n detailFormat: 1,\n detail1: \"fullWidth, spans the collapsible column and 'Repositories'\",\n detail2: \"fullWidth, spans 'Branches' and 'Pull requests'\",\n detail3: \"fullWidth, spans 'Workspaces' and 'Last commit'\"\n }\n }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n // In this example, expanded rows are tracked by the repo names from each row. This could be any unique identifier.\n // This is to prevent state from being based on row order index in case we later add sorting.\n // Note that this behavior is very similar to selection state.\n const initialExpandedRepoNames = repositories.filter((repo) => !!repo.details).map((repo) => repo.name); // Default to all expanded\n const [expandedRepoNames, setExpandedRepoNames] = useState<string[]>(initialExpandedRepoNames);\n const setRepoExpanded = (repo: Repository, isExpanding = true) =>\n setExpandedRepoNames((prevExpanded) => {\n const otherExpandedRepoNames = prevExpanded.filter((r) => r !== repo.name);\n return isExpanding ? [...otherExpandedRepoNames, repo.name] : otherExpandedRepoNames;\n });\n const isRepoExpanded = (repo: Repository) => expandedRepoNames.includes(repo.name);\n\n const [isExampleCompact, setIsExampleCompact] = useState(true);\n\n return (\n <Fragment>\n <Checkbox\n label=\"Compact\"\n isChecked={isExampleCompact}\n onChange={(_event, checked) => setIsExampleCompact(checked)}\n aria-label=\"toggle striped compact variation\"\n id=\"toggle-compact-striped\"\n name=\"toggle-compact-striped\"\n />\n <Table\n aria-label=\"Expandable table\"\n variant={isExampleCompact ? 'compact' : undefined}\n isStriped\n isExpandable\n hasAnimations\n >\n <Thead>\n <Tr>\n <Th screenReaderText=\"Row expansion\" />\n <Th width={25}>{columnNames.name}</Th>\n <Th width={10}>{columnNames.branches}</Th>\n <Th width={15}>{columnNames.prs}</Th>\n <Th width={30}>{columnNames.workspaces}</Th>\n <Th width={10}>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n {repositories.map((repo, rowIndex) => {\n // Some arbitrary examples of how you could customize the child row based on your needs\n let childIsFullWidth = false;\n let childHasNoPadding = false;\n let detail1Colspan = 1;\n let detail2Colspan = 1;\n let detail3Colspan = 1;\n if (repo.details) {\n const { detail1, detail2, detail3, detailFormat } = repo.details;\n const numColumns = 5;\n childIsFullWidth = [1, 3].includes(detailFormat);\n childHasNoPadding = [2, 3].includes(detailFormat);\n if (detail1 && !detail2 && !detail3) {\n detail1Colspan = childIsFullWidth ? numColumns : numColumns + 1; // Account for toggle column\n } else if (detail1 && detail2 && !detail3) {\n detail1Colspan = 2;\n detail2Colspan = childIsFullWidth ? 3 : 4;\n } else if (detail1 && detail2 && detail3) {\n detail1Colspan = 2;\n detail2Colspan = 2;\n detail3Colspan = childIsFullWidth ? 1 : 2;\n }\n }\n return (\n <Tbody key={repo.name} isExpanded={isRepoExpanded(repo)}>\n <Tr isContentExpanded={isRepoExpanded(repo)}>\n <Td\n expand={\n repo.details\n ? {\n rowIndex,\n isExpanded: isRepoExpanded(repo),\n onToggle: () => setRepoExpanded(repo, !isRepoExpanded(repo))\n }\n : undefined\n }\n />\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n {repo.details ? (\n <Tr isExpanded={isRepoExpanded(repo)}>\n {!childIsFullWidth ? <Td /> : null}\n {repo.details.detail1 ? (\n <Td dataLabel=\"Repo detail 1\" noPadding={childHasNoPadding} colSpan={detail1Colspan}>\n <ExpandableRowContent>{repo.details.detail1}</ExpandableRowContent>\n </Td>\n ) : null}\n {repo.details.detail2 ? (\n <Td dataLabel=\"Repo detail 2\" noPadding={childHasNoPadding} colSpan={detail2Colspan}>\n <ExpandableRowContent>{repo.details.detail2}</ExpandableRowContent>\n </Td>\n ) : null}\n {repo.details.detail3 ? (\n <Td dataLabel=\"Repo detail 3\" noPadding={childHasNoPadding} colSpan={detail3Colspan}>\n <ExpandableRowContent>{repo.details.detail3}</ExpandableRowContent>\n </Td>\n ) : null}\n </Tr>\n ) : null}\n </Tbody>\n );\n })}\n </Table>\n </Fragment>\n );\n};\n","title":"Striped expandable","lang":"ts","className":""}}>
2960
-
2961
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2962
- {`To apply striping to an expandable table, add the `}
2963
-
2964
- <code {...{"className":"ws-code "}}>
2965
- {`isStriped`}
2966
- </code>
2967
- {` and `}
2968
-
2969
- <code {...{"className":"ws-code "}}>
2970
- {`isExpandable`}
2971
- </code>
2972
- {` properties to `}
2973
-
2974
- <code {...{"className":"ws-code "}}>
2975
- {`Table`}
2976
- </code>
2977
- {`.`}
2978
- </p>
2979
- </Example>,
2980
- 'Striped multiple tobdy': props =>
2981
- <Example {...pageData} {...props} {...{"code":"import { Fragment } from 'react';\nimport { Table, Caption, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: number;\n description?: string;\n prs: number;\n workspaces: number;\n lastCommit: string;\n}\n\nexport const TableStripedMultipleTbody: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories1: Repository[] = [\n {\n name: 'tbody 1 - Repository 1',\n description: '(odd rows striped)',\n branches: 10,\n prs: 25,\n workspaces: 5,\n lastCommit: '2 days ago'\n },\n { name: 'tbody 1 - Repository 2', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' },\n {\n name: 'tbody 1 - Repository 3',\n description: '(odd rows striped)',\n branches: 10,\n prs: 25,\n workspaces: 5,\n lastCommit: '2 days ago'\n }\n ];\n\n const repositories2: Repository[] = [\n { name: 'tbody 2 - Repository 4', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' },\n {\n name: 'tbody 2 - Repository 5',\n description: '(even rows striped)',\n branches: 10,\n prs: 25,\n workspaces: 5,\n lastCommit: '2 days ago'\n },\n { name: 'tbody 2 - Repository 6', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' },\n {\n name: 'tbody 2 - Repository 7',\n description: '(even rows striped)',\n branches: 10,\n prs: 25,\n workspaces: 5,\n lastCommit: '2 days ago'\n }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n return (\n <Table aria-label=\"Simple table\">\n <Caption>Striped table using multiple tbody components</Caption>\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody isOddStriped>\n {repositories1.map((repo) => (\n <Tr key={repo.name}>\n <Td dataLabel={columnNames.name}>\n {repo.description ? (\n <Fragment>\n {repo.name}\n <br />\n <small>{repo.description}</small>\n </Fragment>\n ) : (\n repo.name\n )}\n </Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n <Tbody isEvenStriped>\n {repositories2.map((repo) => (\n <Tr key={repo.name}>\n <Td dataLabel={columnNames.name}>\n {repo.description ? (\n <Fragment>\n {repo.name}\n <br />\n <small>{repo.description}</small>\n </Fragment>\n ) : (\n repo.name\n )}\n </Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Striped multiple tobdy","lang":"ts","className":""}}>
2982
-
2983
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
2984
- {`When there are multiple `}
2985
-
2986
- <code {...{"className":"ws-code "}}>
2987
- {`Tbody`}
2988
- </code>
2989
- {` components within a table, a more granular application of striping may be controlled by adding either the `}
2990
-
2991
- <code {...{"className":"ws-code "}}>
2992
- {`isEvenStriped`}
2993
- </code>
2994
- {` or `}
2995
-
2996
- <code {...{"className":"ws-code "}}>
2997
- {`isOddStriped`}
2998
- </code>
2999
- {` properties to `}
3000
-
3001
- <code {...{"className":"ws-code "}}>
3002
- {`Tbody`}
3003
- </code>
3004
- {`. These properties will stripe even or odd rows within that `}
3005
-
3006
- <code {...{"className":"ws-code "}}>
3007
- {`Tbody`}
3008
- </code>
3009
- {` respectively.`}
3010
- </p>
3011
- </Example>,
3012
- 'Striped tr': props =>
3013
- <Example {...pageData} {...props} {...{"code":"import { Table, Caption, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';\n\ninterface Repository {\n name: string;\n branches: number;\n prs: number;\n workspaces: number;\n lastCommit: string;\n}\n\nexport const TableStripedTr: React.FunctionComponent = () => {\n // In real usage, this data would come from some external source like an API via props.\n const repositories: Repository[] = [\n { name: 'Repository 1', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' },\n { name: 'Repository 2', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' },\n { name: 'Repository 3', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' },\n { name: 'Repository 4', branches: 10, prs: 25, workspaces: 5, lastCommit: '2 days ago' }\n ];\n\n const columnNames = {\n name: 'Repositories',\n branches: 'Branches',\n prs: 'Pull requests',\n workspaces: 'Workspaces',\n lastCommit: 'Last commit'\n };\n\n return (\n <Table aria-label=\"Simple table\">\n <Caption>Manually striped table using composable components</Caption>\n <Thead>\n <Tr>\n <Th>{columnNames.name}</Th>\n <Th>{columnNames.branches}</Th>\n <Th>{columnNames.prs}</Th>\n <Th>{columnNames.workspaces}</Th>\n <Th>{columnNames.lastCommit}</Th>\n </Tr>\n </Thead>\n <Tbody>\n {repositories.map((repo, index) => (\n <Tr key={repo.name} {...(index % 2 === 0 && { isStriped: true })}>\n <Td dataLabel={columnNames.name}>{repo.name}</Td>\n <Td dataLabel={columnNames.branches}>{repo.branches}</Td>\n <Td dataLabel={columnNames.prs}>{repo.prs}</Td>\n <Td dataLabel={columnNames.workspaces}>{repo.workspaces}</Td>\n <Td dataLabel={columnNames.lastCommit}>{repo.lastCommit}</Td>\n </Tr>\n ))}\n </Tbody>\n </Table>\n );\n};\n","title":"Striped tr","lang":"ts","className":""}}>
3014
-
3015
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
3016
- {`To manually control striping, add the `}
3017
-
3018
- <code {...{"className":"ws-code "}}>
3019
- {`isStriped`}
3020
- </code>
3021
- {` property to each desired `}
3022
-
3023
- <code {...{"className":"ws-code "}}>
3024
- {`Tr`}
3025
- </code>
3026
- {`. This replaces adding the `}
3027
-
3028
- <code {...{"className":"ws-code "}}>
3029
- {`isStriped`}
3030
- </code>
3031
- {` property to `}
3032
-
3033
- <code {...{"className":"ws-code "}}>
3034
- {`Table`}
3035
- </code>
3036
- {`.`}
3037
- </p>
3038
- </Example>
3039
- };
3040
-
3041
- const Component = () => (
3042
- <React.Fragment>
3043
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
3044
- {`Note: Table lives in its own package at `}
3045
- <PatternflyThemeLink {...{"to":"https://www.npmjs.com/package/@patternfly/react-table","className":""}}>
3046
- {`@patternfly/react-table`}
3047
- </PatternflyThemeLink>
3048
- {`!`}
3049
- </p>
3050
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
3051
- {`The `}
3052
- <code {...{"className":"ws-code "}}>
3053
- {`Table`}
3054
- </code>
3055
- {` component takes an explicit and declarative approach, and its implementation closely mirrors that of an HTML table.`}
3056
- </p>
3057
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
3058
- {`The documentation for the deprecated table implementation can be found under the `}
3059
- <PatternflyThemeLink {...{"to":"/components/table/react-deprecated","className":""}}>
3060
- {`React deprecated`}
3061
- </PatternflyThemeLink>
3062
- {` tab. It is configuration based and takes a less declarative and more implicit approach to laying out the table structure, such as the rows and cells within it.`}
3063
- </p>
3064
- <AutoLinkHeader {...{"id":"table-examples","headingLevel":"h2","className":"ws-title ws-h2"}}>
3065
- {`Table examples`}
3066
- </AutoLinkHeader>
3067
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
3068
- {`This `}
3069
- <code {...{"className":"ws-code "}}>
3070
- {`Table`}
3071
- </code>
3072
- {` component differs from the deprecated `}
3073
- <code {...{"className":"ws-code "}}>
3074
- {`Table`}
3075
- </code>
3076
- {` component, in that it allows you to compose the table by nesting the relevant `}
3077
- <code {...{"className":"ws-code "}}>
3078
- {`Thead`}
3079
- </code>
3080
- {`, `}
3081
- <code {...{"className":"ws-code "}}>
3082
- {`Tbody`}
3083
- </code>
3084
- {`, `}
3085
- <code {...{"className":"ws-code "}}>
3086
- {`Tr`}
3087
- </code>
3088
- {`, `}
3089
- <code {...{"className":"ws-code "}}>
3090
- {`Th`}
3091
- </code>
3092
- {` and `}
3093
- <code {...{"className":"ws-code "}}>
3094
- {`Td`}
3095
- </code>
3096
- {` components within it. For a less declarative and more implicit approach, use the `}
3097
- <code {...{"className":"ws-code "}}>
3098
- {`Table`}
3099
- </code>
3100
- {` component instead.`}
3101
- </p>
3102
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
3103
- {`Some general notes:`}
3104
- </p>
3105
- <ul {...{"className":"pf-v6-c-content--ul pf-m-editorial ws-ul "}}>
3106
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
3107
- {`Provide `}
3108
- <code {...{"className":"ws-code "}}>
3109
- {`dataLabel`}
3110
- </code>
3111
- {` prop to the `}
3112
- <code {...{"className":"ws-code "}}>
3113
- {`Td`}
3114
- </code>
3115
- {` components so that in mobile view the cell has a label to provide context.`}
3116
- </li>
3117
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
3118
- {`If you want a table caption, simply place a `}
3119
- <code {...{"className":"ws-code "}}>
3120
- {`<Caption>My caption</Caption>`}
3121
- </code>
3122
- {` as the first child within a `}
3123
- <code {...{"className":"ws-code "}}>
3124
- {`Table`}
3125
- </code>
3126
- {`.`}
3127
- </li>
3128
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
3129
- {`You can set the `}
3130
- <code {...{"className":"ws-code "}}>
3131
- {`Table`}
3132
- </code>
3133
- {` variant to `}
3134
- <code {...{"className":"ws-code "}}>
3135
- {`compact`}
3136
- </code>
3137
- </li>
3138
- </ul>
3139
- {React.createElement(pageData.examples["Basic"])}
3140
- {React.createElement(pageData.examples["Plain"])}
3141
- {React.createElement(pageData.examples["Custom row wrapper, header tooltips & popovers"])}
3142
- {React.createElement(pageData.examples["Sortable & wrapping headers"])}
3143
- {React.createElement(pageData.examples["Sortable - custom control"])}
3144
- {React.createElement(pageData.examples["Selectable with checkbox"])}
3145
- {React.createElement(pageData.examples["Selectable with indeterminate state"])}
3146
- {React.createElement(pageData.examples["Selectable radio input"])}
3147
- {React.createElement(pageData.examples["Row click handler, clickable rows"])}
3148
- {React.createElement(pageData.examples["Editable rows"])}
3149
- {React.createElement(pageData.examples["Actions"])}
3150
- {React.createElement(pageData.examples["Actions Overflow"])}
3151
- {React.createElement(pageData.examples["Expandable"])}
3152
- {React.createElement(pageData.examples["Compound expandable"])}
3153
- {React.createElement(pageData.examples["Cell width, breakpoint modifiers"])}
3154
- {React.createElement(pageData.examples["Controlling text"])}
3155
- {React.createElement(pageData.examples["Modifiers with table text"])}
3156
- {React.createElement(pageData.examples["Empty state"])}
3157
- {React.createElement(pageData.examples["Favoritable (implemented with sortable)"])}
3158
- {React.createElement(pageData.examples["Tree table"])}
3159
- {React.createElement(pageData.examples["Flat tree table with no inset"])}
3160
- {React.createElement(pageData.examples["Draggable row table"])}
3161
- <AutoLinkHeader {...{"id":"sticky-table-modifiers","headingLevel":"h3","className":"ws-title ws-h3"}}>
3162
- {`Sticky table modifiers`}
3163
- </AutoLinkHeader>
3164
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
3165
- {`To make certain columns and the header sticky, the table must be wrapped in a combination of `}
3166
- <code {...{"className":"ws-code "}}>
3167
- {`OuterScrollContainer`}
3168
- </code>
3169
- {` and `}
3170
- <code {...{"className":"ws-code "}}>
3171
- {`InnerScrollContainer`}
3172
- </code>
3173
- {`. For sticky columns, only `}
3174
- <code {...{"className":"ws-code "}}>
3175
- {`InnerScrollContainer`}
3176
- </code>
3177
- {` is required. For sticky headers, and sticky headers with sticky columns, both containers are required to ensure the sticky behavior behaves correctly.`}
3178
- </p>
3179
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
3180
- <strong {...{"className":""}}>
3181
- {`Note:`}
3182
- </strong>
3183
- {` Sticky table headers and columns have a higher `}
3184
- <code {...{"className":"ws-code "}}>
3185
- {`z-index`}
3186
- </code>
3187
- {` than the `}
3188
- <code {...{"className":"ws-code "}}>
3189
- {`z-index`}
3190
- </code>
3191
- {` used for menus (dropdown, select, etc). The intent is that the contents of a scrollable table will scroll under the sticky header/column, including any expanded menus. However, there may be use cases where a menu needs to appear on top of a sticky header/column, such as an expanded menu in a toolbar above a table with a sticky header.`}
3192
- </p>
3193
- <p {...{"className":"pf-v6-c-content--p pf-m-editorial ws-p "}}>
3194
- {`There are a few ways this can be handled:`}
3195
- </p>
3196
- <ul {...{"className":"pf-v6-c-content--ul pf-m-editorial ws-ul "}}>
3197
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
3198
- {`Manipulate the `}
3199
- <code {...{"className":"ws-code "}}>
3200
- {`z-index`}
3201
- </code>
3202
- {` of the menu and/or table headers/columns manually.`}
3203
- </li>
3204
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
3205
- {`Use the `}
3206
- <code {...{"className":"ws-code "}}>
3207
- {`menuAppendTo`}
3208
- </code>
3209
- {` prop in non-composable react components with menus to append the menu to an element outside of the table (e.g., the table's parent element) so that the menu has a higher stacking context than - and can appear on top of - sticky headers/columns as well as appear outside of any scrollable content in the table.`}
3210
- </li>
3211
- <li {...{"className":"pf-v6-c-content--li pf-m-editorial ws-li "}}>
3212
- {`In the case where the menu is outside of the table (e.g., above the table in a toolbar, or below the table and the menu expands up), assign the entire table a lower `}
3213
- <code {...{"className":"ws-code "}}>
3214
- {`z-index`}
3215
- </code>
3216
- {` than the `}
3217
- <code {...{"className":"ws-code "}}>
3218
- {`z-index`}
3219
- </code>
3220
- {` of the menu. This creates a lower stacking context for the entire table compared to the menu, while preserving the stacking context of the elements inside of the table.`}
3221
- </li>
3222
- </ul>
3223
- {React.createElement(pageData.examples["Sticky column"])}
3224
- {React.createElement(pageData.examples["Multiple left-aligned sticky columns"])}
3225
- {React.createElement(pageData.examples["Multiple right-aligned sticky columns"])}
3226
- {React.createElement(pageData.examples["Dynamic sticky header"])}
3227
- {React.createElement(pageData.examples["Sticky columns and header"])}
3228
- {React.createElement(pageData.examples["Nested column headers"])}
3229
- {React.createElement(pageData.examples["Nested column headers and expandable rows"])}
3230
- {React.createElement(pageData.examples["Expandable with nested table"])}
3231
- {React.createElement(pageData.examples["Nested sticky header"])}
3232
- {React.createElement(pageData.examples["Striped"])}
3233
- {React.createElement(pageData.examples["Striped expandable"])}
3234
- {React.createElement(pageData.examples["Striped multiple tobdy"])}
3235
- {React.createElement(pageData.examples["Striped tr"])}
3236
- </React.Fragment>
3237
- );
3238
- Component.displayName = 'ComponentsTableReactDocs';
3239
- Component.pageData = pageData;
3240
-
3241
- export default Component;