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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/Components/CustomCell.tsx +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +116 -49
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +58 -2
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +1 -1
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +16 -4
- data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableHeader.tsx +7 -3
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +32 -0
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +13 -3
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +7 -1
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +61 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.html.erb +33 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_no_subrows.jsx +0 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows.jsx +57 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pinned_rows_react.md +5 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_scrollbar_none.html.erb +33 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_scrollbar_none.jsx +53 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +6 -2
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +3 -1
- data/app/pb_kits/playbook/pb_advanced_table/index.js +2 -0
- data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +1 -4
- data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +7 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_date_display.html.erb +13 -0
- data/app/pb_kits/playbook/pb_draggable/context/index.tsx +17 -58
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +12 -3
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.jsx +42 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_close_on_select.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -1
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +14 -10
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +26 -15
- data/app/pb_kits/playbook/pb_popover/index.ts +9 -4
- data/app/pb_kits/playbook/pb_table/docs/_table_with_selectable_rows.html.erb +3 -51
- data/app/pb_kits/playbook/pb_table/styles/_mobile_collapse.scss +1 -1
- data/dist/chunks/{_typeahead-CRW6dJbW.js → _typeahead-BlPRej0F.js} +2 -2
- data/dist/chunks/_weekday_stacked-DqVFJsbt.js +45 -0
- data/dist/chunks/lazysizes-DHz07jlL.js +1 -0
- data/dist/chunks/lib-D4vXIZF5.js +29 -0
- data/dist/chunks/{pb_form_validation-BZ2AVAi_.js → pb_form_validation-DyvJ8iPe.js} +1 -1
- data/dist/chunks/vendor.js +1 -1
- data/dist/playbook-doc.js +1 -1
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +15 -7
- data/dist/chunks/_weekday_stacked-C4d17aYW.js +0 -45
- data/dist/chunks/lazysizes-B7xYodB-.js +0 -1
- data/dist/chunks/lib-D5R1BjUn.js +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 238f79cc12714d5308f9c5313a0de643c70640fd43be04fe4836dbe2728d5041
|
4
|
+
data.tar.gz: 958840963550c75488c8d2150609e48717d7438a5c470368df8a5fce4ee5ff90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bc844d45f1484a2010f47afb1399692251afe1a703e498d03fd85b6514a173ae00e2fcd2ac671379aad75887ee3994717467f7525385e69567ff7afe66181b3
|
7
|
+
data.tar.gz: 17d63048ce235183acd0e78586247b70854cd7e66cecff34726bc3f7763198e31017cdef6b932113d1045bf570bffb2c822e0dedc28649ad38fe42ed2c7d6e72
|
@@ -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.
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
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
|
+
})
|