playbook_ui 14.21.0.pre.rc.1 → 14.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) 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 +32 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +13 -3
  11. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +7 -1
  12. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +61 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.html.erb +33 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.jsx +0 -1
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows.jsx +57 -0
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows_react.md +5 -0
  17. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_scrollbar_none.html.erb +33 -0
  18. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_scrollbar_none.jsx +53 -0
  19. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +6 -2
  20. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +3 -1
  21. data/app/pb_kits/playbook/pb_advanced_table/index.js +2 -0
  22. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +1 -4
  23. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +7 -0
  24. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_date_display.html.erb +13 -0
  25. data/app/pb_kits/playbook/pb_draggable/context/index.tsx +17 -58
  26. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +12 -3
  27. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.jsx +42 -0
  28. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.md +1 -0
  29. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +2 -0
  30. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -1
  31. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +14 -10
  32. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +26 -15
  33. data/app/pb_kits/playbook/pb_popover/index.ts +9 -4
  34. data/app/pb_kits/playbook/pb_table/docs/_table_with_selectable_rows.html.erb +3 -51
  35. data/app/pb_kits/playbook/pb_table/styles/_mobile_collapse.scss +1 -1
  36. data/dist/chunks/{_typeahead-CRW6dJbW.js → _typeahead-BlPRej0F.js} +2 -2
  37. data/dist/chunks/_weekday_stacked-DqVFJsbt.js +45 -0
  38. data/dist/chunks/lazysizes-DHz07jlL.js +1 -0
  39. data/dist/chunks/lib-D4vXIZF5.js +29 -0
  40. data/dist/chunks/{pb_form_validation-BZ2AVAi_.js → pb_form_validation-DyvJ8iPe.js} +1 -1
  41. data/dist/chunks/vendor.js +1 -1
  42. data/dist/playbook-doc.js +1 -1
  43. data/dist/playbook-rails-react-bindings.js +1 -1
  44. data/dist/playbook-rails.js +1 -1
  45. data/dist/playbook.css +1 -1
  46. data/lib/playbook/version.rb +1 -1
  47. metadata +15 -7
  48. data/dist/chunks/_weekday_stacked-C4d17aYW.js +0 -45
  49. data/dist/chunks/lazysizes-B7xYodB-.js +0 -1
  50. data/dist/chunks/lib-D5R1BjUn.js +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 336755bba6712ce669382639b82a88f8dc5020d88808191eb6897897716d64a9
4
- data.tar.gz: 0eb8dd3e40bca57c15ee0da1e9da8c94c8fb72ee1ffef42e383385b087008ec1
3
+ metadata.gz: 238f79cc12714d5308f9c5313a0de643c70640fd43be04fe4836dbe2728d5041
4
+ data.tar.gz: 958840963550c75488c8d2150609e48717d7438a5c470368df8a5fce4ee5ff90
5
5
  SHA512:
6
- metadata.gz: d577d4f25a65cbda96295bedb5e4b339fa67c17a5f6cbcd1ea98d102cc1c851d8a715432c49c436fc64478964fe8f885f5eee8ce3b6ccbdf72d6ae3c05bc16cd
7
- data.tar.gz: 60a7fa9c59ca86adf29b72a9624d42300d3ae46059ccefc0e1ac228984346258673cc5dc46996eb06b70197093848bb078d47e81ed14fa045250920274990f29
6
+ metadata.gz: 1bc844d45f1484a2010f47afb1399692251afe1a703e498d03fd85b6514a173ae00e2fcd2ac671379aad75887ee3994717467f7525385e69567ff7afe66181b3
7
+ data.tar.gz: 17d63048ce235183acd0e78586247b70854cd7e66cecff34726bc3f7763198e31017cdef6b932113d1045bf570bffb2c822e0dedc28649ad38fe42ed2c7d6e72
@@ -47,7 +47,7 @@ export const CustomCell = ({
47
47
  <Flex
48
48
  alignItems="center"
49
49
  columnGap="xs"
50
- justify={!hasAnySubRows && !inlineRowLoading ? "end" : "start"}
50
+ justify={"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)) {
131
+ if (header?.index === 0 && hasAnySubRows || (header?.index === 0 && inlineRowLoading) || (header?.index === 0 && isToggleExpansionEnabled)) {
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
@@ -33,6 +33,16 @@
33
33
  }
34
34
  }
35
35
 
36
+ @mixin scrollbar-styling {
37
+ &::-webkit-scrollbar {
38
+ width: 8px;
39
+ }
40
+
41
+ -ms-overflow-style: none !important;
42
+ scrollbar-width: thin !important;
43
+ scrollbar-color: rgb(0 0 0 / .20) transparent;
44
+ }
45
+
36
46
  [id$="-span"] {
37
47
  word-wrap: normal;
38
48
  }
@@ -87,6 +97,16 @@
87
97
  }
88
98
  }
89
99
 
100
+ &.advanced-table-hide-scrollbar {
101
+ &::-webkit-scrollbar {
102
+ display: none !important;
103
+ }
104
+
105
+ -ms-overflow-style: none !important;
106
+ scrollbar-width: none !important;
107
+ }
108
+
109
+
90
110
  .row-selection-actions-card {
91
111
  border-bottom-right-radius: 0px !important;
92
112
  border-bottom-left-radius: 0px !important;
@@ -226,30 +246,37 @@
226
246
  &.advanced-table-max-height-xs {
227
247
  max-height: 320px;
228
248
  overflow-y: auto;
249
+ @include scrollbar-styling;
229
250
  }
230
251
  &.advanced-table-max-height-sm {
231
252
  max-height: 480px;
232
253
  overflow-y: auto;
254
+ @include scrollbar-styling;
233
255
  }
234
256
  &.advanced-table-max-height-md {
235
257
  max-height: 768px;
236
258
  overflow-y: auto;
259
+ @include scrollbar-styling;
237
260
  }
238
261
  &.advanced-table-max-height-lg {
239
262
  max-height: 1024px;
240
263
  overflow-y: auto;
264
+ @include scrollbar-styling;
241
265
  }
242
266
  &.advanced-table-max-height-xl {
243
267
  max-height: 1280px;
244
268
  overflow-y: auto;
269
+ @include scrollbar-styling;
245
270
  }
246
271
  &.advanced-table-max-height-xxl {
247
272
  max-height: 1440px;
248
273
  overflow-y: auto;
274
+ @include scrollbar-styling;
249
275
  }
250
276
  &.advanced-table-max-height-xxxl {
251
277
  max-height: 1920px;
252
278
  overflow-y: auto;
279
+ @include scrollbar-styling;
253
280
  }
254
281
 
255
282
  // Fullscreen
@@ -588,6 +615,11 @@
588
615
  }
589
616
  }
590
617
 
618
+ // Row Pinning - additional inline styles in RegularTableView.tsx
619
+ .pinned-row {
620
+ box-shadow: 0 4px 10px 0 rgba($shadow, 0.16) !important;
621
+ }
622
+
591
623
  &.dark {
592
624
  // Override default border color for dark mode
593
625
  --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,8 +51,13 @@ 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",
60
+ scrollBarNone?: boolean,
56
61
  selectableRows?: boolean,
57
62
  showActionsBar?: boolean,
58
63
  sortControl?: GenericObject
@@ -91,7 +96,9 @@ const AdvancedTable = (props: AdvancedTableProps) => {
91
96
  onToggleExpansionClick,
92
97
  pagination = false,
93
98
  paginationProps,
99
+ pinnedRows,
94
100
  responsive = "scroll",
101
+ scrollBarNone= false,
95
102
  showActionsBar = true,
96
103
  selectableRows,
97
104
  sortControl,
@@ -136,6 +143,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
136
143
  tableOptions,
137
144
  onRowSelectionChange,
138
145
  columnVisibilityControl,
146
+ pinnedRows,
139
147
  });
140
148
 
141
149
  // Initialize table actions
@@ -241,10 +249,11 @@ const AdvancedTable = (props: AdvancedTableProps) => {
241
249
  maxHeight ? `advanced-table-max-height-${maxHeight}` : '',
242
250
  {
243
251
  'advanced-table-fullscreen': isFullscreen,
244
- 'advanced-table-allow-fullscreen': allowFullScreen
252
+ 'advanced-table-allow-fullscreen': allowFullScreen,
245
253
  },
246
254
  {'advanced-table-sticky-left-columns': stickyLeftColumn && stickyLeftColumn.length > 0},
247
255
  columnGroupBorderColor ? `column-group-border-${columnGroupBorderColor}` : '',
256
+ scrollBarNone ? 'advanced-table-hide-scrollbar' : '',
248
257
  globalProps(props),
249
258
  className
250
259
  );
@@ -302,6 +311,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
302
311
  isFullscreen={isFullscreen}
303
312
  loading={loading}
304
313
  onExpandByDepthClick={onExpandByDepthClick}
314
+ pinnedRows={pinnedRows}
305
315
  responsive={responsive}
306
316
  selectableRows={selectableRows}
307
317
  setExpanded={setExpanded}
@@ -29,9 +29,11 @@ module Playbook
29
29
  default: true
30
30
  prop :actions, type: Playbook::Props::Array,
31
31
  default: []
32
+ prop :scroll_bar_none, type: Playbook::Props::Boolean,
33
+ default: false
32
34
 
33
35
  def classname
34
- additional_classes = [responsive_classname, max_height_classname]
36
+ additional_classes = [responsive_classname, max_height_classname, hide_scroll_bar_class]
35
37
  additional_classes << "column-group-border-#{column_group_border_color}" if column_group_border_color != "none"
36
38
  generate_classname("pb_advanced_table", *additional_classes, separator: " ")
37
39
  end
@@ -44,6 +46,10 @@ module Playbook
44
46
  max_height.present? ? "advanced-table-max-height-#{max_height}" : ""
45
47
  end
46
48
 
49
+ def hide_scroll_bar_class
50
+ scroll_bar_none ? "advanced-table-hide-scrollbar " : ""
51
+ end
52
+
47
53
  def selected_rows
48
54
  @selected_rows ||= []
49
55
  end
@@ -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
+ })