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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +116 -49
- 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 +13 -0
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +10 -3
- 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_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/example.yml +2 -1
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
- data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +12 -8
- data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +6 -3
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +48 -2
- data/app/pb_kits/playbook/pb_dropdown/index.js +24 -0
- data/dist/chunks/_weekday_stacked-KnBjAMoL.js +45 -0
- data/dist/chunks/vendor.js +1 -1
- data/dist/menu.yml +1 -1
- data/dist/playbook-doc.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +4 -4
- data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate_rails.md +0 -1
- data/app/pb_kits/playbook/pb_checkbox/index.js +0 -56
- data/dist/chunks/_weekday_stacked-C4d17aYW.js +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e193bba835e739b1f100fc8f6dd9a6a350d05d107f30920aa4cab96511f8101
|
4
|
+
data.tar.gz: 4bad0ac7db2f0fff41ffe99e0863bce698b55dbf135e69d72da8698d74e3f5ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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;
|
@@ -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
|
@@ -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'
|