playbook_ui 14.20.0.pre.alpha.PLAY22398040 → 14.20.0.pre.alpha.PLAY22408048

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/CustomCell.tsx +1 -1
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +116 -49
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +1 -1
  5. data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +58 -2
  6. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +1 -1
  7. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +16 -4
  8. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableHeader.tsx +7 -3
  9. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +5 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +10 -3
  11. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +61 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.jsx +1 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows.jsx +57 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows_react.md +5 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +3 -3
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
  17. data/app/pb_kits/playbook/pb_advanced_table/index.js +2 -0
  18. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +13 -4
  19. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +17 -58
  20. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +12 -3
  21. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.jsx +42 -0
  22. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.md +1 -0
  23. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +2 -0
  24. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -1
  25. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +14 -10
  26. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +26 -15
  27. data/app/pb_kits/playbook/pb_popover/index.ts +9 -4
  28. data/app/pb_kits/playbook/pb_table/styles/_mobile_collapse.scss +1 -1
  29. data/dist/chunks/{_typeahead-CRW6dJbW.js → _typeahead-C0OGBz_9.js} +1 -1
  30. data/dist/chunks/_weekday_stacked-BbY_mMKV.js +45 -0
  31. data/dist/chunks/{lib-D5R1BjUn.js → lib-CLDGEByA.js} +1 -1
  32. data/dist/chunks/{pb_form_validation-BZ2AVAi_.js → pb_form_validation-C-ccDsK6.js} +1 -1
  33. data/dist/chunks/vendor.js +1 -1
  34. data/dist/playbook-doc.js +1 -1
  35. data/dist/playbook-rails-react-bindings.js +1 -1
  36. data/dist/playbook-rails.js +1 -1
  37. data/dist/playbook.css +1 -1
  38. data/lib/playbook/version.rb +1 -1
  39. metadata +9 -6
  40. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.html.erb +0 -33
  41. data/dist/chunks/_weekday_stacked-CqoPM8b5.js +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f9b73bf216122da05b15be6a562c084b95c5c011fbabb7ae4502e9dc06978ee
4
- data.tar.gz: 5ddf37622560ee3bb1fa365366b8d995a585f603b0b3b4aec477389839f4b8ba
3
+ metadata.gz: 87bff1055c6b09cbc4779f19f9dabbec88eb15968de35487bff99e9f94a0385c
4
+ data.tar.gz: a64264b358a91d00ce79688e42ebe1f35c44a5b62e23c5e59a6404a985596385
5
5
  SHA512:
6
- metadata.gz: 4bf87d2e1b2eb86d80d3119e131ebdb1231031850088a38cd509755d920e1266fd5e96f2f9ce269d9d9da112dc73d7df48b7461e47dd871df45e429674f06bb1
7
- data.tar.gz: 8950bd1fd63f45160188a9d0433dc0f1d8231710d606c704b86ae4b310afb4332b1f434a60aa56876030aef0bd8e2e893c3c1f37288a45b7b3dcd55662d2d3e8
6
+ metadata.gz: 80d9da8956b81920b2a2847ca764c4b50683eb350b32bd94948d8fcdd4a9560f00a8b6e0d91c8525d95bbf438eb528f3c59b0a71e22af982a79207cd17c77e35
7
+ data.tar.gz: b8e950da00f3107b3775c7e104999320216c9dd4c922dbd11a0357f60a1b539bc5f287d0300092945f38c7ff082fe5b075d54bade46f712ce535ce50c1512156
@@ -47,7 +47,7 @@ export const CustomCell = ({
47
47
  <Flex
48
48
  alignItems="center"
49
49
  columnGap="xs"
50
- justify={"start"}
50
+ justify={!hasAnySubRows && !inlineRowLoading ? "end" : "start"}
51
51
  orientation="row"
52
52
  >
53
53
  {
@@ -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;
@@ -128,7 +128,7 @@ const isToggleExpansionEnabled =
128
128
 
129
129
  let justifyHeader:justifyTypes;
130
130
 
131
- if (header?.index === 0 && hasAnySubRows || (header?.index === 0 && inlineRowLoading) || (header?.index === 0 && isToggleExpansionEnabled)) {
131
+ if (header?.index === 0 && hasAnySubRows || (header?.index === 0 && inlineRowLoading)) {
132
132
  justifyHeader = enableSorting ? "between" : "start";
133
133
  } else {
134
134
  justifyHeader = isLeafColumn ? "end" : "center";
@@ -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
@@ -588,6 +588,11 @@
588
588
  }
589
589
  }
590
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
+
591
596
  &.dark {
592
597
  // Override default border color for dark mode
593
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
+ })
@@ -39,6 +39,7 @@ const AdvancedTableNoSubrows = (props) => {
39
39
  <div>
40
40
  <AdvancedTable
41
41
  columnDefinitions={columnDefinitions}
42
+ enableToggleExpansion="all"
42
43
  tableData={MOCK_DATA}
43
44
  {...props}
44
45
  />
@@ -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
@@ -13,7 +13,6 @@ examples:
13
13
  - advanced_table_column_headers: Multi-Header Columns
14
14
  - advanced_table_column_headers_multiple: Multi-Header Columns (Multiple Levels)
15
15
  - advanced_table_column_border_color_rails: Column Group Border Color
16
- - advanced_table_no_subrows: Table with No Subrows or Expansion
17
16
  - advanced_table_selectable_rows_rails: Selectable Rows
18
17
  - advanced_table_selectable_rows_no_subrows_rails: Selectable Rows (No Subrows)
19
18
  - advanced_table_selectable_rows_actions_rails: Selectable Rows (With Actions)
@@ -43,7 +42,7 @@ examples:
43
42
  - advanced_table_column_headers_multiple: Multi-Header Columns (Multiple Levels)
44
43
  - advanced_table_column_headers_custom_cell: Multi-Header Columns with Custom Cells
45
44
  - advanced_table_column_border_color: Column Group Border Color
46
- - advanced_table_no_subrows: Table with No Subrows or Expansion
45
+ # - advanced_table_no_subrows: Table with No Subrows
47
46
  - advanced_table_selectable_rows: Selectable Rows
48
47
  - advanced_table_selectable_rows_no_subrows_react: Selectable Rows (No Subrows)
49
48
  - advanced_table_selectable_rows_actions: Selectable Rows (With Actions)
@@ -53,4 +52,5 @@ examples:
53
52
  - advanced_table_column_visibility: Column Visibility Control
54
53
  - advanced_table_column_visibility_with_state: Column Visibility Control With State
55
54
  - advanced_table_column_visibility_custom: Column Visibility Control with Custom Dropdown
56
- - 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