playbook_ui 14.20.0.pre.alpha.PLAY2170checkboxrailsindeterminatelogicinkitPOC7980 → 14.20.0.pre.alpha.PLAY2178advancedtablerowpinning7983

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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +116 -49
  3. data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +58 -2
  4. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +1 -1
  5. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +16 -4
  6. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableHeader.tsx +7 -3
  7. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +13 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +10 -3
  9. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +61 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows.jsx +57 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows_react.md +5 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +2 -1
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
  14. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +12 -8
  15. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +6 -3
  16. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +48 -2
  17. data/app/pb_kits/playbook/pb_dropdown/index.js +24 -0
  18. data/dist/chunks/_weekday_stacked-KnBjAMoL.js +45 -0
  19. data/dist/chunks/vendor.js +1 -1
  20. data/dist/menu.yml +1 -1
  21. data/dist/playbook-doc.js +1 -1
  22. data/dist/playbook-rails.js +1 -1
  23. data/dist/playbook.css +1 -1
  24. data/lib/playbook/version.rb +1 -1
  25. metadata +4 -4
  26. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate_rails.md +0 -1
  27. data/app/pb_kits/playbook/pb_checkbox/index.js +0 -56
  28. data/dist/chunks/_weekday_stacked-C4d17aYW.js +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f08c1401569be7ec360234581ac1f8e77974bd468dd40578153aa3b26de92f58
4
- data.tar.gz: 317f542180cb2ab211d2a509b2380f549023af74b9d28d854cc2c39fcbed451b
3
+ metadata.gz: 5e193bba835e739b1f100fc8f6dd9a6a350d05d107f30920aa4cab96511f8101
4
+ data.tar.gz: 4bad0ac7db2f0fff41ffe99e0863bce698b55dbf135e69d72da8698d74e3f5ba
5
5
  SHA512:
6
- metadata.gz: 41b11eac6492594abe461ef0ee462353ba7b37b717d0edfe26a0f4356cf4eba17578036933c0ea97021263ac47e55b38435fcebe882b976b54e5a3e6ee28ca8b
7
- data.tar.gz: d36b7e00aa603dbe91fb5a068f68937a82bcb3f849a6fe8acf24209174f13bd7ca3d2750b211d88caa74dc92d7816eef36626d768668e576529c7574bec9beef
6
+ metadata.gz: 718a64fef8b65a401ee28133274da854894174d054fcc2afb408d1f9ee01653b11d786225489e0250e041fad112dde64afaadd6fd48c9e7dc70e8e8ec6ad64fc
7
+ data.tar.gz: 61315558b9260c2e4a407cd06ae21690ff4d9902242b5081238731550f135bf340b20e8f6859f12a7bd7926bf4ce53fd79b99b2badc2e79e97bc206fa9f307a2
@@ -19,6 +19,71 @@ type RegularTableViewProps = {
19
19
  subRowHeaders?: string[]
20
20
  }
21
21
 
22
+ // Helper function for Table Rendering
23
+ const TableCellRenderer = ({
24
+ row,
25
+ collapsibleTrail = true,
26
+ loading = false,
27
+ stickyLeftColumn,
28
+ columnPinning
29
+ }: {
30
+ row: Row<GenericObject>
31
+ collapsibleTrail?: boolean
32
+ loading?: boolean | string
33
+ stickyLeftColumn?: string[]
34
+ columnPinning: { left: string[] }
35
+ }) => {
36
+ return (
37
+ <>
38
+ {row.getVisibleCells().map((cell: Cell<GenericObject, unknown>, i: number) => {
39
+ const isPinnedLeft = columnPinning.left.includes(cell.column.id);
40
+ const isLastCell = (() => {
41
+ const parent = cell.column.parent;
42
+ if (!parent) {
43
+ const last = row.getVisibleCells().at(-1);
44
+ return last?.column.id === cell.column.id;
45
+ }
46
+
47
+ const visibleSiblings = parent.columns.filter(col => col.getIsVisible());
48
+ return visibleSiblings.at(-1)?.id === cell.column.id;
49
+ })();
50
+
51
+ const { column } = cell;
52
+
53
+ return (
54
+ <td
55
+ align="right"
56
+ className={classnames(
57
+ `${cell.id}-cell position_relative`,
58
+ isChrome() ? "chrome-styles" : "",
59
+ isPinnedLeft && 'pinned-left',
60
+ stickyLeftColumn && stickyLeftColumn.length > 0 && isPinnedLeft && 'sticky-left',
61
+ isLastCell && 'last-cell',
62
+ )}
63
+ key={`${cell.id}-data`}
64
+ style={{
65
+ left: isPinnedLeft
66
+ ? i === 1 // Accounting for set min-width for first column
67
+ ? '180px'
68
+ : `${column.getStart("left")}px`
69
+ : undefined,
70
+ }}
71
+ >
72
+ {collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
73
+ <span id={`${cell.id}-span`}>
74
+ {loading ? (
75
+ <LoadingCell />
76
+ ) : (
77
+ flexRender(cell.column.columnDef.cell, cell.getContext())
78
+ )}
79
+ </span>
80
+ </td>
81
+ );
82
+ })}
83
+ </>
84
+ )
85
+ }
86
+
22
87
  export const RegularTableView = ({
23
88
  collapsibleTrail = true,
24
89
  subRowHeaders,
@@ -28,11 +93,14 @@ export const RegularTableView = ({
28
93
  handleExpandOrCollapse,
29
94
  inlineRowLoading,
30
95
  loading,
31
- responsive,
32
96
  table,
33
97
  selectableRows,
34
98
  hasAnySubRows,
35
- stickyLeftColumn
99
+ stickyLeftColumn,
100
+ pinnedRows,
101
+ headerHeight,
102
+ rowHeight,
103
+ sampleRowRef,
36
104
  } = useContext(AdvancedTableContext)
37
105
 
38
106
 
@@ -50,9 +118,44 @@ export const RegularTableView = ({
50
118
  const columnPinning = table.getState().columnPinning || { left: [] };
51
119
  const columnDefinitions = table.options.meta?.columnDefinitions || [];
52
120
 
121
+ // Row pinning
122
+ function PinnedRow({ row }: { row: Row<any> }) {
123
+ return (
124
+ <tr
125
+ className={classnames(
126
+ `pinned-row`,
127
+ )}
128
+ style={{
129
+ backgroundColor: 'white',
130
+ position: 'sticky',
131
+ top:
132
+ row.getIsPinned() === 'top'
133
+ ? `${row.getPinnedIndex() * rowHeight + headerHeight}px`
134
+ : undefined,
135
+ zIndex: '3'
136
+ }}
137
+ >
138
+ <TableCellRenderer
139
+ collapsibleTrail={collapsibleTrail}
140
+ columnPinning={columnPinning}
141
+ loading={loading}
142
+ row={row}
143
+ stickyLeftColumn={stickyLeftColumn}
144
+ />
145
+ </tr>
146
+ )
147
+ }
148
+
149
+ const totalRows = pinnedRows ? table.getCenterRows() : table.getRowModel().rows
150
+
53
151
  return (
54
152
  <>
55
- {table.getRowModel().rows.map((row: Row<GenericObject>) => {
153
+ {pinnedRows && table.getTopRows().map((row: Row<GenericObject>) => (
154
+ <PinnedRow key={row.id}
155
+ row={row}
156
+ />
157
+ ))}
158
+ {totalRows.map((row: Row<GenericObject>, rowIndex: number) => {
56
159
  const isExpandable = row.getIsExpanded();
57
160
  const isFirstChildofSubrow = row.depth > 0 && row.index === 0;
58
161
  const rowHasNoChildren = row.original?.children && !row.original.children.length ? true : false;
@@ -60,6 +163,7 @@ export const RegularTableView = ({
60
163
  const isDataLoading = isExpandable && (inlineRowLoading && rowHasNoChildren) && (row.depth < columnDefinitions[0]?.cellAccessors?.length);
61
164
  const rowBackground = isExpandable && ((!inlineRowLoading && row.getCanExpand()) || (inlineRowLoading && rowHasNoChildren));
62
165
  const rowColor = row.getIsSelected() ? "bg-row-selection" : rowBackground ? "bg-silver" : "bg-white";
166
+ const isFirstRegularRow = rowIndex === 0 && !row.getIsPinned();
63
167
 
64
168
  return (
65
169
  <React.Fragment key={`${row.index}-${row.id}-${row.depth}-row`}>
@@ -77,6 +181,7 @@ export const RegularTableView = ({
77
181
  <tr
78
182
  className={`${rowColor} ${row.depth > 0 ? `depth-sub-row-${row.depth}` : ""}`}
79
183
  id={`${row.index}-${row.id}-${row.depth}-row`}
184
+ ref={isFirstRegularRow ? sampleRowRef : null}
80
185
  >
81
186
  {/* Render custom checkbox column when we want selectableRows for non-expanding tables */}
82
187
  {selectableRows && !hasAnySubRows && (
@@ -90,51 +195,13 @@ export const RegularTableView = ({
90
195
  />
91
196
  </td>
92
197
  )}
93
-
94
- {row.getVisibleCells().map((cell: Cell<GenericObject, unknown>, i: number) => {
95
- const isPinnedLeft = columnPinning.left.includes(cell.column.id);
96
- const isLastCell = (() => {
97
- const parent = cell.column.parent;
98
- if (!parent) {
99
- const last = row.getVisibleCells().at(-1);
100
- return last?.column.id === cell.column.id;
101
- }
102
-
103
- const visibleSiblings = parent.columns.filter(col => col.getIsVisible());
104
- return visibleSiblings.at(-1)?.id === cell.column.id;
105
- })();
106
-
107
- const { column } = cell;
108
- return (
109
- <td
110
- align="right"
111
- className={classnames(
112
- `${cell.id}-cell position_relative`,
113
- isChrome() ? "chrome-styles" : "",
114
- isPinnedLeft && 'pinned-left',
115
- stickyLeftColumn && stickyLeftColumn.length > 0 && isPinnedLeft && 'sticky-left',
116
- isLastCell && 'last-cell',
117
- )}
118
- key={`${cell.id}-data`}
119
- style={{
120
- left: isPinnedLeft
121
- ? i === 1 //Accounting for set min-width for first column
122
- ? '180px'
123
- : `${column.getStart("left")}px`
124
- : undefined,
125
- }}
126
- >
127
- {collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
128
- <span id={`${cell.id}-span`}>
129
- {loading ? (
130
- <LoadingCell />
131
- ) : (
132
- flexRender(cell.column.columnDef.cell, cell.getContext())
133
- )}
134
- </span>
135
- </td>
136
- );
137
- })}
198
+ <TableCellRenderer
199
+ collapsibleTrail={collapsibleTrail}
200
+ columnPinning={columnPinning}
201
+ loading={loading}
202
+ row={row}
203
+ stickyLeftColumn={stickyLeftColumn}
204
+ />
138
205
  </tr>
139
206
 
140
207
  {/* Display LoadingInline if Row Data is querying and there are no children already */}
@@ -154,4 +221,4 @@ export const RegularTableView = ({
154
221
  );
155
222
  }
156
223
 
157
- export default RegularTableView;
224
+ export default RegularTableView;
@@ -1,4 +1,4 @@
1
- import React, { createContext, useRef, useMemo, useEffect } from 'react';
1
+ import React, { createContext, useRef, useMemo, useEffect, useState, useCallback } from 'react';
2
2
  import { useVirtualizer } from '@tanstack/react-virtual';
3
3
 
4
4
  import { Row } from "@tanstack/react-table";
@@ -23,6 +23,54 @@ export const AdvancedTableProvider = ({ children, ...props }: {
23
23
 
24
24
  const table = props.table;
25
25
  const isVirtualized = props.virtualizedRows || props.enableVirtualization;
26
+
27
+ // Pinned Row: height calculations for Header and Row
28
+ const headerRef = useRef(null);
29
+ const sampleRowRef = useRef(null);
30
+
31
+ const [headerHeight, setHeaderHeight] = useState(44);
32
+ const [rowHeight, setRowHeight] = useState(38);
33
+
34
+ const measureHeights = useCallback(() => {
35
+ if (headerRef.current) {
36
+ const headerRect = headerRef.current.getBoundingClientRect();
37
+ if (headerRect.height > 0) {
38
+ setHeaderHeight(headerRect.height);
39
+ }
40
+ }
41
+ if (sampleRowRef.current) {
42
+ const rowRect = sampleRowRef.current.getBoundingClientRect();
43
+ if (rowRect.height > 0) {
44
+ setRowHeight(rowRect.height);
45
+ }
46
+ }
47
+ }, []);
48
+
49
+ useEffect(() => {
50
+ const resizeObserver = new ResizeObserver(() => {
51
+ measureHeights();
52
+ });
53
+
54
+ if (headerRef.current) {
55
+ resizeObserver.observe(headerRef.current);
56
+ }
57
+
58
+ if (sampleRowRef.current) {
59
+ resizeObserver.observe(sampleRowRef.current);
60
+ }
61
+
62
+ const timeoutId = setTimeout(measureHeights, 100);
63
+
64
+ return () => {
65
+ resizeObserver.disconnect();
66
+ clearTimeout(timeoutId);
67
+ };
68
+ }, [measureHeights]);
69
+
70
+ useEffect(() => {
71
+ measureHeights();
72
+ }, [table?.getRowModel().rows.length, measureHeights]);
73
+
26
74
 
27
75
  // Create a flattened data array that includes ALL components for virtualization
28
76
  const flattenedItems = useMemo(() => {
@@ -132,7 +180,15 @@ export const AdvancedTableProvider = ({ children, ...props }: {
132
180
  virtualizer: isVirtualized ? virtualizer : null,
133
181
  flattenedItems,
134
182
  virtualizedRows: isVirtualized,
135
- enableVirtualization: isVirtualized
183
+ enableVirtualization: isVirtualized,
184
+ rowPinning: props.rowPinning,
185
+ setRowPinning: props.setRowPinning,
186
+ keepPinnedRows: props.keepPinnedRows,
187
+ headerHeight,
188
+ rowHeight,
189
+ headerRef,
190
+ sampleRowRef,
191
+ measureHeights,
136
192
  };
137
193
 
138
194
  return (
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect } from 'react';
2
- import { Row } from "@tanstack/react-table";
2
+ import { Row, RowPinningState } from "@tanstack/react-table";
3
3
  import { GenericObject } from "../../types";
4
4
  import { updateExpandAndCollapseState } from "../Utilities/ExpansionControlHelpers";
5
5
 
@@ -6,7 +6,8 @@ import {
6
6
  getPaginationRowModel,
7
7
  getSortedRowModel,
8
8
  RowSelectionState,
9
- Row
9
+ Row,
10
+ RowPinningState
10
11
  } from "@tanstack/react-table";
11
12
  import { GenericObject } from "../../types";
12
13
  import { createColumnHelper } from "@tanstack/react-table";
@@ -23,11 +24,14 @@ interface UseTableStateProps {
23
24
  loading?: boolean | string;
24
25
  pagination?: boolean;
25
26
  paginationProps?: GenericObject;
27
+ pinnedRows?: {
28
+ value?: RowPinningState;
29
+ onChange?: (value: RowPinningState) => void;
30
+ };
26
31
  virtualizedRows?: boolean;
27
32
  tableOptions?: GenericObject;
28
33
  onRowSelectionChange?: (arg: RowSelectionState) => void;
29
34
  columnVisibilityControl?: GenericObject;
30
-
31
35
  }
32
36
 
33
37
  export function useTableState({
@@ -43,18 +47,24 @@ export function useTableState({
43
47
  paginationProps,
44
48
  virtualizedRows = false,
45
49
  tableOptions,
46
- columnVisibilityControl
50
+ columnVisibilityControl,
51
+ pinnedRows,
47
52
  }: UseTableStateProps) {
48
53
  // Create a local state for expanded and setExpanded if expandedControl not used
49
54
  const [localExpanded, setLocalExpanded] = useState({});
50
55
  const [loadingStateRowCount, setLoadingStateRowCount] = useState(initialLoadingRowsCount);
51
56
  const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
52
57
  const [localColumnVisibility, setLocalColumnVisibility] = useState({});
58
+ const [localRowPinning, setLocalRowPinning] = useState<RowPinningState>({
59
+ top: [],
60
+ });
53
61
  // Determine whether to use the prop or the local state
54
62
  const expanded = expandedControl ? expandedControl.value : localExpanded;
55
63
  const setExpanded = expandedControl ? expandedControl.onChange : setLocalExpanded;
56
64
  const columnVisibility = (columnVisibilityControl && columnVisibilityControl.value) ? columnVisibilityControl.value : localColumnVisibility;
57
65
  const setColumnVisibility = (columnVisibilityControl && columnVisibilityControl.onChange) ? columnVisibilityControl.onChange : setLocalColumnVisibility;
66
+ const rowPinning = pinnedRows && pinnedRows.value || localRowPinning;
67
+ const setRowPinning = (pinnedRows && pinnedRows.onChange) ? pinnedRows.onChange : setLocalRowPinning;
58
68
 
59
69
  // Virtualized data handling (chunked loading)
60
70
  const fetchSize = 20; // Number of rows per "page"
@@ -115,6 +125,7 @@ export function useTableState({
115
125
  ...(sortControl && { sorting }),
116
126
  ...(selectableRows && { rowSelection }),
117
127
  ...(columnVisibility && { columnVisibility }),
128
+ ...(pinnedRows && { rowPinning }),
118
129
  },
119
130
  }), [
120
131
  expanded,
@@ -123,6 +134,7 @@ export function useTableState({
123
134
  selectableRows,
124
135
  rowSelection,
125
136
  columnVisibility,
137
+ rowPinning,
126
138
  ]);
127
139
 
128
140
  // Pagination configuration
@@ -153,7 +165,7 @@ export function useTableState({
153
165
  enableSortingRemoval: false,
154
166
  sortDescFirst: true,
155
167
  onRowSelectionChange: setRowSelection,
156
- getRowId: selectableRows ? row => row.id : undefined,
168
+ getRowId: (selectableRows || pinnedRows) ? row => row.id : undefined,
157
169
  onColumnVisibilityChange: setColumnVisibility,
158
170
  meta: {
159
171
  columnDefinitions
@@ -39,7 +39,8 @@ export const TableHeader = ({
39
39
  hasAnySubRows,
40
40
  showActionsBar,
41
41
  selectableRows,
42
- responsive
42
+ responsive,
43
+ headerRef
43
44
  } = useContext(AdvancedTableContext)
44
45
 
45
46
  const classes = classnames(
@@ -62,8 +63,11 @@ export const TableHeader = ({
62
63
  id={id}
63
64
  >
64
65
  {/* Get the header groups (only one in this example) */}
65
- {table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject>) => (
66
- <tr key={`${headerGroup.id}-headerGroup`}>
66
+ {table.getHeaderGroups().map((headerGroup: HeaderGroup<GenericObject>, index: number) => (
67
+ <tr
68
+ key={`${headerGroup.id}-headerGroup`}
69
+ ref={index === 0 ? headerRef : null}
70
+ >
67
71
  {!hasAnySubRows && selectableRows && (
68
72
  <th className={customCellClassnames}>
69
73
  <Checkbox
@@ -368,6 +368,10 @@
368
368
  box-shadow: 1px 0px 0px 0px var(--column-border-color) !important;
369
369
  }
370
370
 
371
+ .pb_table_td:nth-child(2) {
372
+ box-shadow: inset 1px 0px 0px 0px var(--column-border-color) !important;
373
+ }
374
+
371
375
  // Color for collapsible trail
372
376
  .collapsible-trail {
373
377
  background-color: $border_light !important;
@@ -564,6 +568,10 @@
564
568
  box-shadow: $shadow_deep !important;
565
569
  }
566
570
 
571
+ .pb_table_td:nth-child(2) {
572
+ box-shadow: 0 0 0 0 !important;
573
+ }
574
+
567
575
  .pb_advanced_table_header,
568
576
  .pb_advanced_table_body {
569
577
  th.sticky-left,
@@ -580,6 +588,11 @@
580
588
  }
581
589
  }
582
590
 
591
+ // Row Pinning - additional inline styles in RegularTableView.tsx
592
+ .pinned-row {
593
+ box-shadow: 0 4px 10px 0 rgba($shadow, 0.16) !important;
594
+ }
595
+
583
596
  &.dark {
584
597
  // Override default border color for dark mode
585
598
  --column-border-color: #{$border_dark};
@@ -2,7 +2,7 @@ import React, { useRef, useEffect, useState, useCallback } from "react";
2
2
  import classnames from "classnames";
3
3
 
4
4
  import { GenericObject } from "../types";
5
- import { Row, RowSelectionState } from "@tanstack/react-table";
5
+ import { Row, RowSelectionState, RowPinningState } from "@tanstack/react-table";
6
6
 
7
7
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props";
8
8
  import { globalProps, GlobalProps } from "../utilities/globalProps";
@@ -51,7 +51,11 @@ type AdvancedTableProps = {
51
51
  onRowToggleClick?: (arg: Row<GenericObject>) => void
52
52
  onToggleExpansionClick?: (arg: Row<GenericObject>) => void
53
53
  pagination?: boolean,
54
- paginationProps?: GenericObject
54
+ paginationProps?: GenericObject,
55
+ pinnedRows?: {
56
+ value?: RowPinningState;
57
+ onChange?: (value: RowPinningState) => void;
58
+ };
55
59
  responsive?: "scroll" | "none",
56
60
  selectableRows?: boolean,
57
61
  showActionsBar?: boolean,
@@ -91,6 +95,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
91
95
  onToggleExpansionClick,
92
96
  pagination = false,
93
97
  paginationProps,
98
+ pinnedRows,
94
99
  responsive = "scroll",
95
100
  showActionsBar = true,
96
101
  selectableRows,
@@ -136,6 +141,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
136
141
  tableOptions,
137
142
  onRowSelectionChange,
138
143
  columnVisibilityControl,
144
+ pinnedRows,
139
145
  });
140
146
 
141
147
  // Initialize table actions
@@ -241,7 +247,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
241
247
  maxHeight ? `advanced-table-max-height-${maxHeight}` : '',
242
248
  {
243
249
  'advanced-table-fullscreen': isFullscreen,
244
- 'advanced-table-allow-fullscreen': allowFullScreen
250
+ 'advanced-table-allow-fullscreen': allowFullScreen,
245
251
  },
246
252
  {'advanced-table-sticky-left-columns': stickyLeftColumn && stickyLeftColumn.length > 0},
247
253
  columnGroupBorderColor ? `column-group-border-${columnGroupBorderColor}` : '',
@@ -302,6 +308,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
302
308
  isFullscreen={isFullscreen}
303
309
  loading={loading}
304
310
  onExpandByDepthClick={onExpandByDepthClick}
311
+ pinnedRows={pinnedRows}
305
312
  responsive={responsive}
306
313
  selectableRows={selectableRows}
307
314
  setExpanded={setExpanded}
@@ -3,6 +3,12 @@ import { render, screen, waitFor } from "../utilities/test-utils"
3
3
 
4
4
  import { AdvancedTable, Pill } from "playbook-ui"
5
5
 
6
+ global.ResizeObserver = class {
7
+ observe() {}
8
+ unobserve() {}
9
+ disconnect() {}
10
+ };
11
+
6
12
  const MOCK_DATA = [
7
13
  {
8
14
  year: "2021",
@@ -72,6 +78,36 @@ const MOCK_DATA_LOADING = [
72
78
  },
73
79
  ]
74
80
 
81
+ const MOCK_DATA_WITH_ID = [
82
+ {
83
+ id: "1",
84
+ year: "2021",
85
+ quarter: null,
86
+ month: null,
87
+ day: null,
88
+ newEnrollments: "20",
89
+ scheduledMeetings: "10",
90
+ },
91
+ {
92
+ id: "2",
93
+ year: "2022",
94
+ quarter: null,
95
+ month: null,
96
+ day: null,
97
+ newEnrollments: "25",
98
+ scheduledMeetings: "15",
99
+ },
100
+ {
101
+ id: "3",
102
+ year: "2023",
103
+ quarter: null,
104
+ month: null,
105
+ day: null,
106
+ newEnrollments: "30",
107
+ scheduledMeetings: "20",
108
+ },
109
+ ]
110
+
75
111
  const columnDefinitions = [
76
112
  {
77
113
  accessor: "year",
@@ -512,3 +548,28 @@ test("allowFullScreen prop adds fullscreen class", () => {
512
548
  const tableContainer = screen.getByRole("table").closest("div")
513
549
  expect(tableContainer).toHaveClass("advanced-table-allow-fullscreen")
514
550
  })
551
+
552
+ test("pinnedRows prop renders pinned rows at top", () => {
553
+ const pinnedRowsControl = {
554
+ value: { top: ["1", "3"] },
555
+ onChange: jest.fn()
556
+ }
557
+
558
+ render(
559
+ <AdvancedTable
560
+ columnDefinitions={columnDefinitions}
561
+ data={{ testid: testId }}
562
+ pinnedRows={pinnedRowsControl}
563
+ tableData={MOCK_DATA_WITH_ID}
564
+ />
565
+ )
566
+
567
+ const kit = screen.getByTestId(testId)
568
+ const pinnedRows = kit.querySelectorAll(".pinned-row")
569
+
570
+ expect(pinnedRows).toHaveLength(2)
571
+
572
+ const firstPinnedRow = pinnedRows[0]
573
+ expect(firstPinnedRow).toHaveStyle("position: sticky")
574
+ expect(firstPinnedRow).toHaveStyle("background-color: white")
575
+ })
@@ -0,0 +1,57 @@
1
+ import React, { useState } from "react"
2
+ import AdvancedTable from '../_advanced_table'
3
+ import MOCK_DATA from "./advanced_table_mock_data_with_id.json"
4
+
5
+ const AdvancedTableRowPinning = (props) => {
6
+ const columnDefinitions = [
7
+ {
8
+ accessor: "year",
9
+ label: "Year",
10
+ cellAccessors: ["quarter", "month", "day"],
11
+ },
12
+ {
13
+ accessor: "newEnrollments",
14
+ label: "New Enrollments",
15
+ },
16
+ {
17
+ accessor: "scheduledMeetings",
18
+ label: "Scheduled Meetings",
19
+ },
20
+ {
21
+ accessor: "attendanceRate",
22
+ label: "Attendance Rate",
23
+ },
24
+ {
25
+ accessor: "completedClasses",
26
+ label: "Completed Classes",
27
+ },
28
+ {
29
+ accessor: "classCompletionRate",
30
+ label: "Class Completion Rate",
31
+ },
32
+ {
33
+ accessor: "graduatedStudents",
34
+ label: "Graduated Students",
35
+ },
36
+ ]
37
+
38
+ const [pinnedRows, setPinnedRows] = useState({top: ["8", "9", "10", "11", "12", "13", "14"]})
39
+
40
+ return (
41
+ <div>
42
+ <AdvancedTable
43
+ columnDefinitions={columnDefinitions}
44
+ maxHeight="xs"
45
+ pinnedRows={{value: pinnedRows, onChange: setPinnedRows}}
46
+ tableData={MOCK_DATA}
47
+ tableProps={{sticky: true}}
48
+ {...props}
49
+ >
50
+ <AdvancedTable.Header enableSorting />
51
+ <AdvancedTable.Body />
52
+ </AdvancedTable>
53
+ </div>
54
+ )
55
+ }
56
+
57
+ export default AdvancedTableRowPinning
@@ -0,0 +1,5 @@
1
+ Use the `pinnedRows` prop to pin specific rows to the top of an Advanced Table. Pinned rows will remain at the top when scrolling through table data and reorganizing via sorting.
2
+
3
+ **NOTE:** This prop is in Beta. Current Requirements for V1:
4
+ - Sticky header required: Pinned rows must be used with `sticky: true` via `tableProps` (works with both responsive and non-responsive tables)
5
+ - Row ids required: Pass an array of row ids to the `top` property. For expandable rows, both parent and all its child row ids must be included individually
@@ -52,4 +52,5 @@ examples:
52
52
  - advanced_table_column_visibility: Column Visibility Control
53
53
  - advanced_table_column_visibility_with_state: Column Visibility Control With State
54
54
  - advanced_table_column_visibility_custom: Column Visibility Control with Custom Dropdown
55
- - advanced_table_column_visibility_multi: Column Visibility Control with Multi-Header Columns
55
+ - advanced_table_column_visibility_multi: Column Visibility Control with Multi-Header Columns
56
+ - advanced_table_pinned_rows: Pinned Rows
@@ -31,4 +31,5 @@ export { default as AdvancedTableColumnBorderColor} from './_advanced_table_colu
31
31
  export { default as AdvancedTableColumnVisibility } from './_advanced_table_column_visibility.jsx'
32
32
  export { default as AdvancedTableColumnVisibilityCustom } from './_advanced_table_column_visibility_custom.jsx'
33
33
  export { default as AdvancedTableColumnVisibilityMulti } from './_advanced_table_column_visibility_multi.jsx'
34
- export { default as AdvancedTableColumnVisibilityWithState } from './_advanced_table_column_visibility_with_state.jsx'
34
+ export { default as AdvancedTableColumnVisibilityWithState } from './_advanced_table_column_visibility_with_state.jsx'
35
+ export { default as AdvancedTablePinnedRows } from './_advanced_table_pinned_rows.jsx'