playbook_ui 16.4.0.pre.alpha.testingmetadata15413 → 16.4.0.pre.alpha.testingmetadata15451

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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +5 -2
  3. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableHeader.tsx +9 -11
  4. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_grouped_headers_composition.jsx +235 -0
  5. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_grouped_headers_composition.md +17 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_grouped_headers_composition_mock_data.json +98 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -0
  9. data/app/pb_kits/playbook/pb_avatar/kit.schema.json +1 -1
  10. data/app/pb_kits/playbook/pb_background/kit.schema.json +1 -1
  11. data/app/pb_kits/playbook/pb_badge/kit.schema.json +1 -1
  12. data/app/pb_kits/playbook/pb_body/kit.schema.json +1 -1
  13. data/app/pb_kits/playbook/pb_bread_crumbs/kit.schema.json +1 -1
  14. data/app/pb_kits/playbook/pb_button/kit.schema.json +1 -1
  15. data/app/pb_kits/playbook/pb_button_toolbar/kit.schema.json +1 -1
  16. data/app/pb_kits/playbook/pb_caption/kit.schema.json +1 -1
  17. data/app/pb_kits/playbook/pb_card/kit.schema.json +1 -1
  18. data/app/pb_kits/playbook/pb_checkbox/kit.schema.json +1 -1
  19. data/app/pb_kits/playbook/pb_circle_icon_button/kit.schema.json +1 -1
  20. data/app/pb_kits/playbook/pb_collapsible/kit.schema.json +1 -1
  21. data/app/pb_kits/playbook/pb_contact/kit.schema.json +1 -1
  22. data/app/pb_kits/playbook/pb_copy_button/kit.schema.json +1 -1
  23. data/app/pb_kits/playbook/pb_currency/kit.schema.json +1 -1
  24. data/app/pb_kits/playbook/pb_dashboard_value/kit.schema.json +1 -1
  25. data/app/pb_kits/playbook/pb_date/kit.schema.json +1 -1
  26. data/app/pb_kits/playbook/pb_date_picker/kit.schema.json +1 -1
  27. data/app/pb_kits/playbook/pb_date_range_inline/kit.schema.json +1 -1
  28. data/app/pb_kits/playbook/pb_date_range_stacked/kit.schema.json +1 -1
  29. data/app/pb_kits/playbook/pb_date_stacked/kit.schema.json +1 -1
  30. data/app/pb_kits/playbook/pb_date_time/kit.schema.json +1 -1
  31. data/app/pb_kits/playbook/pb_date_time_stacked/kit.schema.json +1 -1
  32. data/app/pb_kits/playbook/pb_date_year_stacked/kit.schema.json +1 -1
  33. data/app/pb_kits/playbook/pb_detail/kit.schema.json +1 -1
  34. data/app/pb_kits/playbook/pb_dialog/kit.schema.json +1 -1
  35. data/app/pb_kits/playbook/pb_distribution_bar/kit.schema.json +1 -1
  36. data/app/pb_kits/playbook/pb_draggable/kit.schema.json +1 -1
  37. data/app/pb_kits/playbook/pb_dropdown/kit.schema.json +1 -1
  38. data/app/pb_kits/playbook/pb_empty_state/kit.schema.json +1 -1
  39. data/app/pb_kits/playbook/pb_file_upload/kit.schema.json +1 -1
  40. data/app/pb_kits/playbook/pb_filter/kit.schema.json +1 -1
  41. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/kit.schema.json +1 -1
  42. data/app/pb_kits/playbook/pb_flex/kit.schema.json +16 -1
  43. data/app/pb_kits/playbook/pb_form/kit.schema.json +1 -1
  44. data/app/pb_kits/playbook/pb_form_group/kit.schema.json +1 -1
  45. data/app/pb_kits/playbook/pb_form_pill/kit.schema.json +1 -1
  46. data/app/pb_kits/playbook/pb_hashtag/kit.schema.json +1 -1
  47. data/app/pb_kits/playbook/pb_highlight/kit.schema.json +1 -1
  48. data/app/pb_kits/playbook/pb_home_address_street/kit.schema.json +1 -1
  49. data/app/pb_kits/playbook/pb_icon/kit.schema.json +1 -1
  50. data/app/pb_kits/playbook/pb_icon_circle/kit.schema.json +1 -1
  51. data/app/pb_kits/playbook/pb_icon_stat_value/kit.schema.json +1 -1
  52. data/app/pb_kits/playbook/pb_icon_value/kit.schema.json +1 -1
  53. data/app/pb_kits/playbook/pb_image/kit.schema.json +1 -1
  54. data/app/pb_kits/playbook/pb_label_pill/kit.schema.json +1 -1
  55. data/app/pb_kits/playbook/pb_label_value/kit.schema.json +1 -1
  56. data/app/pb_kits/playbook/pb_layout/kit.schema.json +1 -1
  57. data/app/pb_kits/playbook/pb_legend/kit.schema.json +1 -1
  58. data/app/pb_kits/playbook/pb_link/kit.schema.json +1 -1
  59. data/app/pb_kits/playbook/pb_list/kit.schema.json +1 -1
  60. data/app/pb_kits/playbook/pb_loading_inline/kit.schema.json +1 -1
  61. data/app/pb_kits/playbook/pb_map/kit.schema.json +1 -1
  62. data/app/pb_kits/playbook/pb_message/kit.schema.json +1 -1
  63. data/app/pb_kits/playbook/pb_multi_level_select/kit.schema.json +1 -1
  64. data/app/pb_kits/playbook/pb_multiple_users/kit.schema.json +1 -1
  65. data/app/pb_kits/playbook/pb_multiple_users_stacked/kit.schema.json +1 -1
  66. data/app/pb_kits/playbook/pb_nav/kit.schema.json +1 -1
  67. data/app/pb_kits/playbook/pb_online_status/kit.schema.json +1 -1
  68. data/app/pb_kits/playbook/pb_overlay/kit.schema.json +1 -1
  69. data/app/pb_kits/playbook/pb_pagination/kit.schema.json +1 -1
  70. data/app/pb_kits/playbook/pb_passphrase/kit.schema.json +1 -1
  71. data/app/pb_kits/playbook/pb_person/kit.schema.json +1 -1
  72. data/app/pb_kits/playbook/pb_person_contact/kit.schema.json +1 -1
  73. data/app/pb_kits/playbook/pb_phone_number_input/kit.schema.json +1 -1
  74. data/app/pb_kits/playbook/pb_pill/kit.schema.json +1 -1
  75. data/app/pb_kits/playbook/pb_popover/kit.schema.json +1 -1
  76. data/app/pb_kits/playbook/pb_progress_pills/kit.schema.json +1 -1
  77. data/app/pb_kits/playbook/pb_progress_simple/kit.schema.json +1 -1
  78. data/app/pb_kits/playbook/pb_progress_step/kit.schema.json +1 -1
  79. data/app/pb_kits/playbook/pb_radio/kit.schema.json +1 -1
  80. data/app/pb_kits/playbook/pb_rich_text_editor/kit.schema.json +1 -1
  81. data/app/pb_kits/playbook/pb_section_separator/kit.schema.json +1 -1
  82. data/app/pb_kits/playbook/pb_select/docs/_select_attributes.html.erb +1 -0
  83. data/app/pb_kits/playbook/pb_select/docs/_select_blank.html.erb +1 -0
  84. data/app/pb_kits/playbook/pb_select/docs/_select_custom_select.html.erb +1 -1
  85. data/app/pb_kits/playbook/pb_select/docs/_select_custom_select_subheaders.html.erb +1 -1
  86. data/app/pb_kits/playbook/pb_select/docs/_select_default.html.erb +1 -0
  87. data/app/pb_kits/playbook/pb_select/docs/_select_disabled.html.erb +1 -0
  88. data/app/pb_kits/playbook/pb_select/docs/_select_disabled_options.html.erb +1 -0
  89. data/app/pb_kits/playbook/pb_select/docs/_select_error.html.erb +1 -0
  90. data/app/pb_kits/playbook/pb_select/docs/_select_inline.html.erb +1 -0
  91. data/app/pb_kits/playbook/pb_select/docs/_select_inline_compact.html.erb +1 -0
  92. data/app/pb_kits/playbook/pb_select/docs/_select_inline_show_arrow.html.erb +1 -0
  93. data/app/pb_kits/playbook/pb_select/docs/_select_multiple.html.erb +1 -0
  94. data/app/pb_kits/playbook/pb_select/docs/_select_required.html.erb +1 -0
  95. data/app/pb_kits/playbook/pb_select/docs/_select_required_indicator.html.erb +1 -0
  96. data/app/pb_kits/playbook/pb_select/docs/_select_value_text_same.html.erb +1 -0
  97. data/app/pb_kits/playbook/pb_select/kit.schema.json +1 -1
  98. data/app/pb_kits/playbook/pb_select/select.html.erb +5 -5
  99. data/app/pb_kits/playbook/pb_select/select.rb +5 -0
  100. data/app/pb_kits/playbook/pb_selectable_card/_selectable_card.scss +39 -0
  101. data/app/pb_kits/playbook/pb_selectable_card/kit.schema.json +1 -1
  102. data/app/pb_kits/playbook/pb_selectable_card_icon/kit.schema.json +1 -1
  103. data/app/pb_kits/playbook/pb_selectable_icon/kit.schema.json +1 -1
  104. data/app/pb_kits/playbook/pb_selectable_list/kit.schema.json +1 -1
  105. data/app/pb_kits/playbook/pb_skeleton_loading/kit.schema.json +1 -1
  106. data/app/pb_kits/playbook/pb_source/kit.schema.json +1 -1
  107. data/app/pb_kits/playbook/pb_star_rating/kit.schema.json +1 -1
  108. data/app/pb_kits/playbook/pb_stat_change/kit.schema.json +1 -1
  109. data/app/pb_kits/playbook/pb_stat_value/kit.schema.json +1 -1
  110. data/app/pb_kits/playbook/pb_table/kit.schema.json +1 -1
  111. data/app/pb_kits/playbook/pb_text_input/kit.schema.json +1 -1
  112. data/app/pb_kits/playbook/pb_textarea/kit.schema.json +1 -1
  113. data/app/pb_kits/playbook/pb_time/kit.schema.json +1 -1
  114. data/app/pb_kits/playbook/pb_time_picker/kit.schema.json +1 -1
  115. data/app/pb_kits/playbook/pb_time_range_inline/kit.schema.json +1 -1
  116. data/app/pb_kits/playbook/pb_time_stacked/kit.schema.json +1 -1
  117. data/app/pb_kits/playbook/pb_timeline/kit.schema.json +1 -1
  118. data/app/pb_kits/playbook/pb_timestamp/kit.schema.json +1 -1
  119. data/app/pb_kits/playbook/pb_title/kit.schema.json +1 -1
  120. data/app/pb_kits/playbook/pb_title_count/kit.schema.json +1 -1
  121. data/app/pb_kits/playbook/pb_title_detail/kit.schema.json +1 -1
  122. data/app/pb_kits/playbook/pb_toggle/kit.schema.json +1 -1
  123. data/app/pb_kits/playbook/pb_tooltip/kit.schema.json +1 -1
  124. data/app/pb_kits/playbook/pb_typeahead/kit.schema.json +1 -1
  125. data/app/pb_kits/playbook/pb_user/kit.schema.json +1 -1
  126. data/app/pb_kits/playbook/pb_user_badge/kit.schema.json +1 -1
  127. data/app/pb_kits/playbook/pb_weekday_stacked/kit.schema.json +1 -1
  128. data/dist/chunks/vendor.js +2 -2
  129. data/dist/menu.yml +1 -1
  130. data/dist/playbook.css +1 -1
  131. data/lib/playbook/version.rb +1 -1
  132. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ad106d09640e4bc94cbbca0010eadf35beadbadbd60082fbdb503064d0a705a
4
- data.tar.gz: f2ff748c85103836acec1d9b2137e5c067b624adf3b48bbfdbf530fc3970e15b
3
+ metadata.gz: ad7665b0e0228f1fdc181e63385f1c9753a1622228b52ac9e971db17ec738ea3
4
+ data.tar.gz: b79b48a87f203827fcf6498fd60fdcc52621fdae015fc96568afdb22a7983393
5
5
  SHA512:
6
- metadata.gz: 3ab469f3903ec4dfaacd620b292159c86b860f672def1f7c3feed6ab5cebf11ba7bfecc903e7c29f81be0d2507b657fb36e8e4ddfc88c1550283336e89475484
7
- data.tar.gz: 6313817507b70b0b40bfa9aca1f06881829ebae83c7c49c7b90b17498a9a305a4d5866b2ec40faafd42c3fc7231898487667b00308576c87df3355de84dd3027
6
+ metadata.gz: 86ff2ff126d9de6a01d8bb9066098a578ae88a38d33f6f757b70f52215d48f34e56628e47096c3d2deea8cd114ffdd936a13310850012343c909188e63dd7890
7
+ data.tar.gz: 861382f43f67421bc4a54418a41ffe8e8cd36e56c8dbf415f6af4918fd184b62b2b3bd9fc073073fb10d9c6d071066f1d0e1495118429d1dd2903365212a74ff
@@ -33,7 +33,8 @@ export const AdvancedTableProvider = ({ children, ...props }: {
33
33
 
34
34
  const measureHeights = useCallback(() => {
35
35
  if (headerRef.current) {
36
- const headerRect = headerRef.current.getBoundingClientRect();
36
+ const headerElement = headerRef.current as HTMLElement;
37
+ const headerRect = headerElement.getBoundingClientRect();
37
38
  if (headerRect.height > 0) {
38
39
  setHeaderHeight(headerRect.height);
39
40
  }
@@ -67,9 +68,11 @@ export const AdvancedTableProvider = ({ children, ...props }: {
67
68
  };
68
69
  }, [measureHeights]);
69
70
 
71
+ const headerGroupCount = table?.getHeaderGroups()?.length ?? 0;
72
+
70
73
  useEffect(() => {
71
74
  measureHeights();
72
- }, [table?.getRowModel().rows.length, measureHeights]);
75
+ }, [table?.getRowModel().rows.length, headerGroupCount, measureHeights]);
73
76
 
74
77
 
75
78
  // Create a flattened data array that includes ALL components for virtualization
@@ -64,14 +64,12 @@ export const TableHeader = ({
64
64
  );
65
65
 
66
66
  const renderRegularTableHeader = () => (
67
- <thead className={classes}
67
+ <thead className={classes}
68
68
  id={id}
69
+ ref={headerRef}
69
70
  >
70
- {table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject>, index: number) => (
71
- <tr
72
- key={`${headerGroup.id}-headerGroup`}
73
- ref={index === 0 ? headerRef : null}
74
- >
71
+ {table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject>) => (
72
+ <tr key={`${headerGroup.id}-headerGroup`}>
75
73
  {!hasAnySubRows && selectableRows && (
76
74
  <th className={customCellClassnames}>
77
75
  <Checkbox
@@ -105,16 +103,16 @@ export const TableHeader = ({
105
103
  );
106
104
 
107
105
  const renderVirtualizedTableHeader = () => (
108
- <thead
109
- className={classes}
106
+ <thead
107
+ className={classes}
110
108
  data-virtualized="true"
111
109
  id={id}
110
+ ref={headerRef}
112
111
  >
113
- {table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject>, index: number) => (
114
- <tr
112
+ {table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject>) => (
113
+ <tr
115
114
  className="virtualized-header-row-header"
116
115
  key={`${headerGroup.id}-headerGroup-virtualized`}
117
- ref={index === 0 ? headerRef : null}
118
116
  >
119
117
  {!hasAnySubRows && selectableRows && (
120
118
  <th className={classnames(customCellClassnames, "virtualized-header-cell")}>
@@ -0,0 +1,235 @@
1
+ /* eslint-disable react/no-multi-comp, react/prop-types */
2
+ import React, { useCallback, useState } from "react"
3
+
4
+ import AdvancedTable from "../_advanced_table"
5
+ import Flex from "../../pb_flex/_flex"
6
+ import Icon from "../../pb_icon/_icon"
7
+ import List from "../../pb_list/_list"
8
+ import ListItem from "../../pb_list/_list_item"
9
+ import PbReactPopover from "../../pb_popover/_popover"
10
+ import SectionSeparator from "../../pb_section_separator/_section_separator"
11
+ import StarRating from "../../pb_star_rating/_star_rating"
12
+ import COMPOSITION_MOCK_DATA from "./advanced_table_grouped_headers_composition_mock_data.json"
13
+
14
+ const LEAF_COUNT = "newEnrollments"
15
+ const LEAF_SCHEDULED = "scheduledMeetings"
16
+
17
+ const ICON_UNSORTED = "arrow-up-arrow-down"
18
+ const iconSorted = (desc) => (desc ? "arrow-up-wide-short" : "arrow-down-short-wide")
19
+
20
+ const STAR_MENU = [
21
+ { id: LEAF_COUNT, label: "Count" },
22
+ { id: LEAF_SCHEDULED, label: "Scheduled" },
23
+ ]
24
+
25
+ const MENU_BTN = {
26
+ alignItems: "center",
27
+ background: "transparent",
28
+ border: "none",
29
+ cursor: "pointer",
30
+ display: "flex",
31
+ font: "inherit",
32
+ gap: 12,
33
+ justifyContent: "space-between",
34
+ padding: "8px 14px",
35
+ textAlign: "left",
36
+ width: "100%",
37
+ }
38
+
39
+ const labelStyle = (active) => ({
40
+ color: active ? "#0056cf" : "#242930",
41
+ flex: 1,
42
+ fontSize: 14,
43
+ fontWeight: active ? 600 : 400,
44
+ })
45
+
46
+ /** Hooks + popover; `header` callback cannot use hooks directly. */
47
+ const StarMetricGroupHeader = ({ table }) => {
48
+ const [open, setOpen] = useState(false)
49
+ const menuId = "playbook-star-metric-sort-menu"
50
+
51
+ const sort0 = table.getState().sorting[0]
52
+ const groupActive =
53
+ sort0?.id === LEAF_COUNT || sort0?.id === LEAF_SCHEDULED
54
+
55
+ const close = useCallback((shouldClose) => setOpen(!shouldClose), [])
56
+ const toggle = useCallback((e) => {
57
+ e.stopPropagation()
58
+ setOpen((v) => !v)
59
+ }, [])
60
+
61
+ const applySort = useCallback(
62
+ (columnId, e) => {
63
+ e.stopPropagation()
64
+ const cur = table.getState().sorting[0]
65
+ const nextDesc = cur?.id === columnId ? !cur.desc : true
66
+ table.setSorting([{ desc: nextDesc, id: columnId }])
67
+ setOpen(false)
68
+ },
69
+ [table]
70
+ )
71
+
72
+ return (
73
+ <PbReactPopover
74
+ closeOnClick="outside"
75
+ offset
76
+ padding="none"
77
+ placement="bottom-start"
78
+ reference={
79
+ <button
80
+ aria-controls={menuId}
81
+ aria-expanded={open}
82
+ aria-haspopup="menu"
83
+ aria-label="Sort by Count or Scheduled"
84
+ onClick={toggle}
85
+ style={{
86
+ background: open ? "rgba(115, 134, 169, 0.14)" : "transparent",
87
+ border: "none",
88
+ borderRadius: 6,
89
+ cursor: "pointer",
90
+ font: "inherit",
91
+ margin: 0,
92
+ padding: "2px 4px",
93
+ }}
94
+ title="Open menu to sort by Count or Scheduled"
95
+ type="button"
96
+ >
97
+ <Flex alignItems="center"
98
+ gap="xs"
99
+ justifyContent="center"
100
+ >
101
+ <StarRating
102
+ backgroundType="outline"
103
+ colorOption="primary"
104
+ justifyContent="center"
105
+ maxWidth="102px"
106
+ rating={5}
107
+ />
108
+ <Icon
109
+ color={groupActive ? "primary" : "default"}
110
+ fixedWidth
111
+ icon={
112
+ groupActive
113
+ ? iconSorted(Boolean(sort0?.desc))
114
+ : ICON_UNSORTED
115
+ }
116
+ size="md"
117
+ />
118
+ </Flex>
119
+ </button>
120
+ }
121
+ shouldClosePopover={close}
122
+ show={open}
123
+ zIndex={1200}
124
+ >
125
+ <Flex id={menuId}
126
+ minWidth="220px"
127
+ orientation="column"
128
+ >
129
+ <List borderless
130
+ padding="none"
131
+ >
132
+ {STAR_MENU.map(({ id, label }, i) => {
133
+ const active = sort0?.id === id
134
+ return (
135
+ <React.Fragment key={id}>
136
+ {i > 0 ? <SectionSeparator margin="none" /> : null}
137
+ <ListItem padding="none">
138
+ <button
139
+ onClick={(e) => applySort(id, e)}
140
+ style={MENU_BTN}
141
+ type="button"
142
+ >
143
+ <span style={labelStyle(active)}>{label}</span>
144
+ <Icon
145
+ color={active ? "primary" : "default"}
146
+ fixedWidth
147
+ icon={
148
+ active
149
+ ? iconSorted(Boolean(sort0?.desc))
150
+ : ICON_UNSORTED
151
+ }
152
+ size="md"
153
+ />
154
+ </button>
155
+ </ListItem>
156
+ </React.Fragment>
157
+ )
158
+ })}
159
+ </List>
160
+ </Flex>
161
+ </PbReactPopover>
162
+ )
163
+ }
164
+
165
+ const AdvancedTableGroupedHeadersComposition = (props) => {
166
+ const [pinnedRows, setPinnedRows] = useState({ top: ["12"] })
167
+
168
+ const columnDefinitions = [
169
+ {
170
+ accessor: "year",
171
+ cellAccessors: ["quarter", "month", "day"],
172
+ label: "Year",
173
+ },
174
+ {
175
+ columns: [
176
+ {
177
+ columns: [
178
+ {
179
+ accessor: "newEnrollments",
180
+ enableSort: true,
181
+ id: LEAF_COUNT,
182
+ label: "Count",
183
+ },
184
+ {
185
+ accessor: "scheduledMeetings",
186
+ enableSort: true,
187
+ id: LEAF_SCHEDULED,
188
+ label: "Scheduled",
189
+ },
190
+ ],
191
+ id: "starLeafPair",
192
+ label: "Metrics",
193
+ },
194
+ ],
195
+ header: ({ table }) => (
196
+ <Flex justify="center">
197
+ <StarMetricGroupHeader table={table} />
198
+ </Flex>
199
+ ),
200
+ id: "starMetricGroup",
201
+ label: "Rating group (custom header)",
202
+ },
203
+ {
204
+ columns: [
205
+ { accessor: "attendanceRate", label: "Attendance" },
206
+ {
207
+ accessor: "classCompletionRate",
208
+ enableSort: true,
209
+ id: "classCompletionRate",
210
+ label: "Completion %",
211
+ },
212
+ ],
213
+ label: "Performance",
214
+ },
215
+ ]
216
+
217
+ return (
218
+ <div>
219
+ <AdvancedTable
220
+ columnDefinitions={columnDefinitions}
221
+ enableSortingRemoval
222
+ maxHeight="md"
223
+ pinnedRows={{ onChange: setPinnedRows, value: pinnedRows }}
224
+ tableData={COMPOSITION_MOCK_DATA}
225
+ tableProps={{ sticky: true }}
226
+ {...props}
227
+ >
228
+ <AdvancedTable.Header enableSorting />
229
+ <AdvancedTable.Body />
230
+ </AdvancedTable>
231
+ </div>
232
+ )
233
+ }
234
+
235
+ export default AdvancedTableGroupedHeadersComposition
@@ -0,0 +1,17 @@
1
+ ### Grouped headers, custom UI, sort, and pinned rows
2
+
3
+ This example combines patterns that often show up together in product tables:
4
+
5
+ 1. **Multi-level headers** — nested `columns` in `columnDefinitions`.
6
+ 2. **Custom group header** — a `header` function returning React (here: `StarRating`, sort icon, `PbReactPopover` menu). Parent groups are not sort targets; **only leaf columns** use `enableSort: true` (see **Enable Sort By Column (Multi-Column)**).
7
+ 3. **Programmatic sort** — the group `header` receives TanStack’s **`table`**. Call **`table.setSorting([{ id: "<leafColumnId>", desc: boolean }])`** using the same **`id`** values as your leaf columns. Match **direction icons** to the built-in kit (`arrow-up-arrow-down` unsorted; `arrow-up-wide-short` / `arrow-down-short-wide` when sorted).
8
+ 4. **Pinned row + sticky header** — `pinnedRows` with row **`id`**s and `tableProps={{ sticky: true }}` (see **Pinned Rows**).
9
+
10
+ **Implementation notes**
11
+
12
+ - **`header` must be a child component** if you need hooks (e.g. popover open state). Render it from `header: ({ table }) => <YourHeader table={table} />`.
13
+ - **Avoid wrapping the primary control in `Tooltip`** — it can steal the first tap/click. Use a **`title`** on the button or copy in the doc instead.
14
+ - **Popover rows:** use a plain **`<button>`** as `reference`, `closeOnClick="outside"`. Picking the **same** metric again should **toggle** `desc`; switching to another leaf column often defaults to **`desc: true`** to align with **`sortDescFirst`**.
15
+ - **Data:** **`advanced_table_grouped_headers_composition_mock_data.json`** — twelve flat rows (2015–2026) with **varied** Count / Scheduled / % values so sorting is obvious; **`id` `"12"`** is **2026** and is pinned by default.
16
+
17
+ Other building blocks on this kit: **Custom Header with Multiple Headers**, **Sticky Header**.
@@ -0,0 +1,98 @@
1
+ [
2
+ {
3
+ "id": "1",
4
+ "year": "2015",
5
+ "newEnrollments": "12",
6
+ "scheduledMeetings": "40",
7
+ "attendanceRate": "62%",
8
+ "classCompletionRate": "28%"
9
+ },
10
+ {
11
+ "id": "2",
12
+ "year": "2016",
13
+ "newEnrollments": "88",
14
+ "scheduledMeetings": "12",
15
+ "attendanceRate": "71%",
16
+ "classCompletionRate": "55%"
17
+ },
18
+ {
19
+ "id": "3",
20
+ "year": "2017",
21
+ "newEnrollments": "34",
22
+ "scheduledMeetings": "67",
23
+ "attendanceRate": "58%",
24
+ "classCompletionRate": "41%"
25
+ },
26
+ {
27
+ "id": "4",
28
+ "year": "2018",
29
+ "newEnrollments": "05",
30
+ "scheduledMeetings": "91",
31
+ "attendanceRate": "44%",
32
+ "classCompletionRate": "73%"
33
+ },
34
+ {
35
+ "id": "5",
36
+ "year": "2019",
37
+ "newEnrollments": "61",
38
+ "scheduledMeetings": "19",
39
+ "attendanceRate": "83%",
40
+ "classCompletionRate": "36%"
41
+ },
42
+ {
43
+ "id": "6",
44
+ "year": "2020",
45
+ "newEnrollments": "19",
46
+ "scheduledMeetings": "54",
47
+ "attendanceRate": "67%",
48
+ "classCompletionRate": "62%"
49
+ },
50
+ {
51
+ "id": "7",
52
+ "year": "2021",
53
+ "newEnrollments": "73",
54
+ "scheduledMeetings": "08",
55
+ "attendanceRate": "52%",
56
+ "classCompletionRate": "49%"
57
+ },
58
+ {
59
+ "id": "8",
60
+ "year": "2022",
61
+ "newEnrollments": "50",
62
+ "scheduledMeetings": "50",
63
+ "attendanceRate": "75%",
64
+ "classCompletionRate": "45%"
65
+ },
66
+ {
67
+ "id": "9",
68
+ "year": "2023",
69
+ "newEnrollments": "95",
70
+ "scheduledMeetings": "03",
71
+ "attendanceRate": "69%",
72
+ "classCompletionRate": "81%"
73
+ },
74
+ {
75
+ "id": "10",
76
+ "year": "2024",
77
+ "newEnrollments": "27",
78
+ "scheduledMeetings": "76",
79
+ "attendanceRate": "91%",
80
+ "classCompletionRate": "22%"
81
+ },
82
+ {
83
+ "id": "11",
84
+ "year": "2025",
85
+ "newEnrollments": "41",
86
+ "scheduledMeetings": "33",
87
+ "attendanceRate": "48%",
88
+ "classCompletionRate": "94%"
89
+ },
90
+ {
91
+ "id": "12",
92
+ "year": "2026",
93
+ "newEnrollments": "66",
94
+ "scheduledMeetings": "66",
95
+ "attendanceRate": "55%",
96
+ "classCompletionRate": "58%"
97
+ }
98
+ ]
@@ -75,6 +75,7 @@ examples:
75
75
  - advanced_table_column_visibility_with_state: Column Visibility Control With State
76
76
  - advanced_table_column_visibility_custom: Column Visibility Control with Custom Dropdown
77
77
  - advanced_table_column_visibility_multi: Column Visibility Control with Multi-Header Columns
78
+ - advanced_table_grouped_headers_composition: Grouped headers, custom headers, sort, and pinned rows
78
79
  - advanced_table_scrollbar_none: Advanced Table Scrollbar None
79
80
  - advanced_table_row_styling: Row Styling
80
81
  - advanced_table_padding_control_per_row: Padding Control using Row Styling
@@ -42,6 +42,7 @@ export { default as AdvancedTableInfiniteScroll} from './_advanced_table_infinit
42
42
  export {default as AdvancedTableWithCustomHeader} from './_advanced_table_with_custom_header.jsx'
43
43
  export { default as AdvancedTableCustomSort } from './_advanced_table_custom_sort.jsx'
44
44
  export { default as AdvancedTableWithCustomHeaderMultiHeader } from './_advanced_table_with_custom_header_multi_header.jsx'
45
+ export { default as AdvancedTableGroupedHeadersComposition } from './_advanced_table_grouped_headers_composition.jsx'
45
46
  export { default as AdvancedTableSortPerColumn } from './_advanced_table_sort_per_column.jsx'
46
47
  export { default as AdvancedTableSortPerColumnForMultiColumn } from './_advanced_table_sort_per_column_for_multi_column.jsx'
47
48
  export { default as AdvancedTablePaddingControl } from './_advanced_table_padding_control.jsx'
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Avatar",
4
- "description": "Avatar displays a user's picture. This helps aid easy recognition",
4
+ "description": "Avatar component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Background",
4
- "description": "The background kit is used for adding a background to a page or to",
4
+ "description": "Background component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Badge",
4
- "description": "Badges can be used for notification, tags, and status. They are used",
4
+ "description": "Badge component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Body",
4
- "description": "Default text style for paragraphs. Follow hiearchy when using \"light\"",
4
+ "description": "Body component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "BreadCrumbs",
4
- "description": "BreadCrumbs can be used for keeping a user aware of their route location.",
4
+ "description": "BreadCrumbs component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Button",
4
- "description": "Interactive element for triggering actions. Primary variant for main CTAs, secondary for less prominent actions, danger for destructive actions.",
4
+ "description": "Button component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "ButtonToolbar",
4
- "description": "This kit should primarily hold the most commonly used buttons.",
4
+ "description": "ButtonToolbar component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Caption",
4
- "description": "Use to provide supplementary context. Default size is best when providing",
4
+ "description": "Caption component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Card",
4
- "description": "Content container with optional header, body sections, and various styling options.",
4
+ "description": "Card component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Checkbox",
4
- "description": "Checkbox is used for a list of selections that are meant to have",
4
+ "description": "Checkbox component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "CircleIconButton",
4
- "description": "When using Icon Circle Button, the icon must be clear a clear indication",
4
+ "description": "CircleIconButton component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Collapsible",
4
- "description": "Collapsible content that can be expanded or collapsed.",
4
+ "description": "Collapsible component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Contact",
4
- "description": "Use to display customer's or user's contact information.",
4
+ "description": "Contact component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "CopyButton",
4
- "description": "Button that copies text to clipboard with visual feedback.",
4
+ "description": "CopyButton component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Currency",
4
- "description": "Use to display monetary amounts, typically on dashboards or other",
4
+ "description": "Currency component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "DashboardValue",
4
- "description": "Use in dashboards to give the viewer a quick overview of important",
4
+ "description": "DashboardValue component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "Date",
4
- "description": "Use to display the date. Year will not display if it is the current",
4
+ "description": "Date component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "DatePicker",
4
- "description": "Playbook's date picker is built using flatpickr, a vanilla js library.",
4
+ "description": "DatePicker component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "DateRangeInline",
4
- "description": "Use to display a date range. Year will not show if it is the current",
4
+ "description": "DateRangeInline component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "DateRangeStacked",
4
- "description": "Displays a date range in a vertical stacked format.",
4
+ "description": "DateRangeStacked component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://playbook.powerapp.cloud/schemas/kit-schema.json",
3
3
  "name": "DateStacked",
4
- "description": "Use to display the date, stacking month and day. Year will not show",
4
+ "description": "DateStacked component",
5
5
  "platforms": [
6
6
  "react",
7
7
  "rails"