@coveord/plasma-mantine 48.22.5 → 48.23.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.
- package/.turbo/turbo-build.log +3 -3
- package/.turbo/turbo-test.log +10 -10
- package/dist/.tsbuildinfo +1 -1
- package/dist/cjs/components/code-editor/CodeEditor.js +1 -3
- package/dist/cjs/components/code-editor/CodeEditor.js.map +1 -1
- package/dist/cjs/components/collection/Collection.js +15 -17
- package/dist/cjs/components/collection/Collection.js.map +1 -1
- package/dist/cjs/components/table/Table.js +48 -45
- package/dist/cjs/components/table/Table.js.map +1 -1
- package/dist/cjs/components/table/TableActions.js +4 -4
- package/dist/cjs/components/table/TableActions.js.map +1 -1
- package/dist/cjs/components/table/TableCollapsibleColumn.js +1 -1
- package/dist/cjs/components/table/TableCollapsibleColumn.js.map +1 -1
- package/dist/cjs/components/table/TableContext.js.map +1 -1
- package/dist/cjs/components/table/TableFilter.js +4 -0
- package/dist/cjs/components/table/TableFilter.js.map +1 -1
- package/dist/cjs/components/table/TableHeader.js +36 -4
- package/dist/cjs/components/table/TableHeader.js.map +1 -1
- package/dist/cjs/components/table/TableSelectableColumn.js +46 -0
- package/dist/cjs/components/table/TableSelectableColumn.js.map +1 -0
- package/dist/cjs/components/table/Th.js +5 -15
- package/dist/cjs/components/table/Th.js.map +1 -1
- package/dist/cjs/components/table/useRowSelection.js +58 -0
- package/dist/cjs/components/table/useRowSelection.js.map +1 -0
- package/dist/cjs/theme/Theme.js +15 -10
- package/dist/cjs/theme/Theme.js.map +1 -1
- package/dist/definitions/components/code-editor/CodeEditor.d.ts.map +1 -1
- package/dist/definitions/components/collection/Collection.d.ts.map +1 -1
- package/dist/definitions/components/table/Table.d.ts +12 -2
- package/dist/definitions/components/table/Table.d.ts.map +1 -1
- package/dist/definitions/components/table/TableActions.d.ts +3 -3
- package/dist/definitions/components/table/TableActions.d.ts.map +1 -1
- package/dist/definitions/components/table/TableCollapsibleColumn.d.ts +1 -1
- package/dist/definitions/components/table/TableContext.d.ts +6 -1
- package/dist/definitions/components/table/TableContext.d.ts.map +1 -1
- package/dist/definitions/components/table/TableFilter.d.ts.map +1 -1
- package/dist/definitions/components/table/TableHeader.d.ts.map +1 -1
- package/dist/definitions/components/table/TableSelectableColumn.d.ts +6 -0
- package/dist/definitions/components/table/TableSelectableColumn.d.ts.map +1 -0
- package/dist/definitions/components/table/Th.d.ts.map +1 -1
- package/dist/definitions/components/table/useRowSelection.d.ts +7 -0
- package/dist/definitions/components/table/useRowSelection.d.ts.map +1 -0
- package/dist/definitions/components/table/useTable.d.ts +2 -0
- package/dist/definitions/components/table/useTable.d.ts.map +1 -1
- package/dist/definitions/theme/Theme.d.ts.map +1 -1
- package/dist/esm/components/code-editor/CodeEditor.js +1 -3
- package/dist/esm/components/code-editor/CodeEditor.js.map +1 -1
- package/dist/esm/components/collection/Collection.js +15 -17
- package/dist/esm/components/collection/Collection.js.map +1 -1
- package/dist/esm/components/table/Table.js +48 -45
- package/dist/esm/components/table/Table.js.map +1 -1
- package/dist/esm/components/table/TableActions.js +4 -4
- package/dist/esm/components/table/TableActions.js.map +1 -1
- package/dist/esm/components/table/TableCollapsibleColumn.js +2 -2
- package/dist/esm/components/table/TableCollapsibleColumn.js.map +1 -1
- package/dist/esm/components/table/TableContext.js.map +1 -1
- package/dist/esm/components/table/TableFilter.js +4 -0
- package/dist/esm/components/table/TableFilter.js.map +1 -1
- package/dist/esm/components/table/TableHeader.js +38 -6
- package/dist/esm/components/table/TableHeader.js.map +1 -1
- package/dist/esm/components/table/TableSelectableColumn.js +38 -0
- package/dist/esm/components/table/TableSelectableColumn.js.map +1 -0
- package/dist/esm/components/table/Th.js +5 -15
- package/dist/esm/components/table/Th.js.map +1 -1
- package/dist/esm/components/table/useRowSelection.js +48 -0
- package/dist/esm/components/table/useRowSelection.js.map +1 -0
- package/dist/esm/theme/Theme.js +15 -10
- package/dist/esm/theme/Theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/code-editor/CodeEditor.tsx +1 -3
- package/src/components/collection/Collection.tsx +8 -10
- package/src/components/table/Table.tsx +91 -62
- package/src/components/table/TableActions.tsx +7 -7
- package/src/components/table/TableCollapsibleColumn.tsx +2 -2
- package/src/components/table/TableContext.tsx +6 -1
- package/src/components/table/TableFilter.tsx +7 -1
- package/src/components/table/TableHeader.tsx +24 -4
- package/src/components/table/TableSelectableColumn.tsx +33 -0
- package/src/components/table/Th.tsx +6 -19
- package/src/components/table/__tests__/Table.spec.tsx +100 -7
- package/src/components/table/__tests__/TableActions.spec.tsx +21 -0
- package/src/components/table/__tests__/TableFilter.spec.tsx +48 -1
- package/src/components/table/useRowSelection.ts +45 -0
- package/src/theme/Theme.tsx +14 -7
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
TableState,
|
|
11
11
|
useReactTable,
|
|
12
12
|
} from '@tanstack/react-table';
|
|
13
|
-
import {InitialTableState} from '@tanstack/table-core';
|
|
13
|
+
import {CoreOptions, InitialTableState} from '@tanstack/table-core';
|
|
14
14
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
15
15
|
import {Children, Fragment, ReactElement, ReactNode, useCallback, useEffect, useState} from 'react';
|
|
16
16
|
|
|
@@ -24,58 +24,74 @@ import {TableHeader} from './TableHeader';
|
|
|
24
24
|
import {TablePagination} from './TablePagination';
|
|
25
25
|
import {TablePerPage} from './TablePerPage';
|
|
26
26
|
import {TablePredicate} from './TablePredicate';
|
|
27
|
+
import {TableSelectableColumn} from './TableSelectableColumn';
|
|
27
28
|
import {Th} from './Th';
|
|
29
|
+
import {useRowSelection} from './useRowSelection';
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
interface TableStylesParams {
|
|
32
|
+
hasHeader: boolean;
|
|
33
|
+
multiRowSelectionEnabled: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const useStyles = createStyles<string, TableStylesParams>((theme, {hasHeader, multiRowSelectionEnabled}) => {
|
|
37
|
+
const rowBackgroundColor =
|
|
38
|
+
theme.colorScheme === 'dark'
|
|
39
|
+
? theme.fn.rgba(theme.colors[theme.primaryColor][7], 0.2)
|
|
40
|
+
: theme.colors[theme.primaryColor][0];
|
|
41
|
+
return {
|
|
42
|
+
table: {
|
|
43
|
+
width: '100%',
|
|
44
|
+
'& td:first-of-type, th:first-of-type > *': {
|
|
45
|
+
paddingLeft: theme.spacing.xl,
|
|
46
|
+
},
|
|
34
47
|
},
|
|
35
|
-
},
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
header: {
|
|
50
|
+
position: 'sticky',
|
|
51
|
+
top: hasHeader ? 69 : 0,
|
|
52
|
+
backgroundColor: theme.colorScheme === 'dark' ? theme.black : theme.white,
|
|
53
|
+
transition: 'box-shadow 150ms ease',
|
|
54
|
+
zIndex: 12, // skeleton is 11
|
|
55
|
+
|
|
56
|
+
'&::after': {
|
|
57
|
+
content: '""',
|
|
58
|
+
position: 'absolute',
|
|
59
|
+
left: 0,
|
|
60
|
+
right: 0,
|
|
61
|
+
bottom: 0,
|
|
62
|
+
borderBottom: `1px solid ${theme.colors.gray[2]}`,
|
|
63
|
+
},
|
|
45
64
|
},
|
|
46
|
-
|
|
47
|
-
|
|
65
|
+
|
|
66
|
+
rowSelected: {
|
|
67
|
+
backgroundColor: multiRowSelectionEnabled ? undefined : rowBackgroundColor,
|
|
48
68
|
},
|
|
49
69
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
position: 'absolute',
|
|
53
|
-
left: 0,
|
|
54
|
-
right: 0,
|
|
55
|
-
bottom: 0,
|
|
56
|
-
borderBottom: `1px solid ${theme.colors.gray[2]}`,
|
|
70
|
+
rowSelectionCheckboxCell: {
|
|
71
|
+
verticalAlign: 'middle',
|
|
57
72
|
},
|
|
58
|
-
},
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
rowCollapsibleButtonCell: {
|
|
75
|
+
textAlign: 'right',
|
|
76
|
+
},
|
|
63
77
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
? theme.fn.rgba(theme.colors[theme.primaryColor][7], 0.2)
|
|
69
|
-
: theme.colors[theme.primaryColor][0],
|
|
78
|
+
row: {
|
|
79
|
+
'&:hover': {
|
|
80
|
+
backgroundColor: rowBackgroundColor,
|
|
81
|
+
},
|
|
70
82
|
},
|
|
71
|
-
}
|
|
72
|
-
})
|
|
83
|
+
};
|
|
84
|
+
});
|
|
73
85
|
|
|
74
86
|
interface TableProps<T> {
|
|
75
87
|
/**
|
|
76
88
|
* Data to display in the table
|
|
77
89
|
*/
|
|
78
90
|
data: T[];
|
|
91
|
+
/**
|
|
92
|
+
* Defines how each row is uniquely identified. It is highly recommended that you specify this prop to an ID that makes sense.
|
|
93
|
+
*/
|
|
94
|
+
getRowId?: CoreOptions<T>['getRowId'];
|
|
79
95
|
/**
|
|
80
96
|
* Columns to display in the table.
|
|
81
97
|
*
|
|
@@ -131,6 +147,12 @@ interface TableProps<T> {
|
|
|
131
147
|
* Action passed when user double clicks on a row
|
|
132
148
|
*/
|
|
133
149
|
doubleClickAction?: (datum: T) => void;
|
|
150
|
+
/**
|
|
151
|
+
* Whether the user can select multiple rows in order to perform actions in bulk
|
|
152
|
+
*
|
|
153
|
+
* @default false
|
|
154
|
+
*/
|
|
155
|
+
multiRowSelectionEnabled?: boolean;
|
|
134
156
|
}
|
|
135
157
|
|
|
136
158
|
interface TableType {
|
|
@@ -142,12 +164,13 @@ interface TableType {
|
|
|
142
164
|
Pagination: typeof TablePagination;
|
|
143
165
|
PerPage: typeof TablePerPage;
|
|
144
166
|
Predicate: typeof TablePredicate;
|
|
145
|
-
CollapsibleColumn: typeof TableCollapsibleColumn;
|
|
146
167
|
DateRangePicker: typeof TableDateRangePicker;
|
|
168
|
+
CollapsibleColumn: typeof TableCollapsibleColumn;
|
|
147
169
|
}
|
|
148
170
|
|
|
149
171
|
export const Table: TableType = <T,>({
|
|
150
172
|
data,
|
|
173
|
+
getRowId,
|
|
151
174
|
noDataChildren,
|
|
152
175
|
getExpandChildren,
|
|
153
176
|
initialState = {},
|
|
@@ -157,6 +180,7 @@ export const Table: TableType = <T,>({
|
|
|
157
180
|
children,
|
|
158
181
|
loading = false,
|
|
159
182
|
doubleClickAction,
|
|
183
|
+
multiRowSelectionEnabled,
|
|
160
184
|
}: TableProps<T>) => {
|
|
161
185
|
const convertedChildren = Children.toArray(children) as ReactElement[];
|
|
162
186
|
const header = convertedChildren.find((child) => child.type === TableHeader);
|
|
@@ -166,15 +190,16 @@ export const Table: TableType = <T,>({
|
|
|
166
190
|
const form = useForm<TableFormType>({
|
|
167
191
|
initialValues: {predicates: initialState?.predicates ?? {}, dateRange: initialState?.dateRange ?? [null, null]},
|
|
168
192
|
});
|
|
169
|
-
|
|
170
|
-
const {cx, classes} = useStyles({hasHeader: !!header});
|
|
193
|
+
const {cx, classes} = useStyles({hasHeader: !!header, multiRowSelectionEnabled});
|
|
171
194
|
|
|
172
195
|
const table = useReactTable({
|
|
173
196
|
initialState: defaultsDeep(initialStateWithoutForm, {pagination: {pageSize: TablePerPage.DEFAULT_SIZE}}),
|
|
174
197
|
data,
|
|
175
|
-
columns,
|
|
198
|
+
columns: multiRowSelectionEnabled ? [TableSelectableColumn as ColumnDef<T>].concat(columns) : columns,
|
|
176
199
|
getCoreRowModel: getCoreRowModel(),
|
|
177
200
|
manualPagination: true,
|
|
201
|
+
enableMultiRowSelection: !!multiRowSelectionEnabled,
|
|
202
|
+
getRowId,
|
|
178
203
|
getRowCanExpand: (row: Row<T>) => !!getExpandChildren?.(row.original) ?? false,
|
|
179
204
|
});
|
|
180
205
|
const [state, setState] = useState<TableState>(table.initialState);
|
|
@@ -183,6 +208,7 @@ export const Table: TableType = <T,>({
|
|
|
183
208
|
state,
|
|
184
209
|
onStateChange: setState,
|
|
185
210
|
}));
|
|
211
|
+
const {clearSelection, getSelectedRow, getSelectedRows} = useRowSelection(table);
|
|
186
212
|
|
|
187
213
|
const triggerChange = () => onChange?.({...state, ...form.values});
|
|
188
214
|
|
|
@@ -190,13 +216,11 @@ export const Table: TableType = <T,>({
|
|
|
190
216
|
onMount?.({...state, ...form.values});
|
|
191
217
|
}, []);
|
|
192
218
|
|
|
193
|
-
const outsideClickRef = useClickOutside(() => {
|
|
194
|
-
table.resetRowSelection(true);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
219
|
useDidUpdate(() => {
|
|
198
220
|
triggerChange();
|
|
199
|
-
|
|
221
|
+
if (!multiRowSelectionEnabled) {
|
|
222
|
+
clearSelection();
|
|
223
|
+
}
|
|
200
224
|
}, [state.globalFilter, state.sorting, state.pagination, form.values]);
|
|
201
225
|
|
|
202
226
|
const clearFilters = useCallback(() => {
|
|
@@ -204,14 +228,11 @@ export const Table: TableType = <T,>({
|
|
|
204
228
|
setState((prevState) => ({...prevState, globalFilter: ''}));
|
|
205
229
|
}, []);
|
|
206
230
|
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
() => table.getSelectedRowModel().flatRows?.[0]?.original ?? null,
|
|
213
|
-
[state.rowSelection]
|
|
214
|
-
);
|
|
231
|
+
const outsideClickRef = useClickOutside(() => {
|
|
232
|
+
if (!multiRowSelectionEnabled) {
|
|
233
|
+
clearSelection();
|
|
234
|
+
}
|
|
235
|
+
});
|
|
215
236
|
|
|
216
237
|
if (!data) {
|
|
217
238
|
return (
|
|
@@ -221,25 +242,29 @@ export const Table: TableType = <T,>({
|
|
|
221
242
|
);
|
|
222
243
|
}
|
|
223
244
|
|
|
224
|
-
const toggleRowSelection = (row: Row<T>) => {
|
|
225
|
-
table.setRowSelection(() => ({[row.id]: !row.getIsSelected()}));
|
|
226
|
-
};
|
|
227
|
-
|
|
228
245
|
const rows = table.getRowModel().rows.map((row) => {
|
|
229
246
|
const rowChildren = getExpandChildren?.(row.original) ?? null;
|
|
230
247
|
|
|
231
248
|
return (
|
|
232
249
|
<Fragment key={row.id}>
|
|
233
250
|
<tr
|
|
234
|
-
onClick={() =>
|
|
251
|
+
onClick={() => row.toggleSelected()}
|
|
235
252
|
onDoubleClick={() => doubleClickAction?.(row.original)}
|
|
236
253
|
className={cx(classes.row, {[classes.rowSelected]: row.getIsSelected()})}
|
|
254
|
+
aria-selected={row.getIsSelected()}
|
|
237
255
|
>
|
|
238
256
|
{row.getVisibleCells().map((cell) => {
|
|
239
257
|
const size = cell.column.getSize();
|
|
240
258
|
const width = size !== defaultColumnSizing.size ? size : undefined;
|
|
241
259
|
return (
|
|
242
|
-
<td
|
|
260
|
+
<td
|
|
261
|
+
key={cell.id}
|
|
262
|
+
style={{width}}
|
|
263
|
+
className={cx({
|
|
264
|
+
[classes.rowSelectionCheckboxCell]: cell.column.id === TableSelectableColumn.id,
|
|
265
|
+
[classes.rowCollapsibleButtonCell]: cell.column.id === TableCollapsibleColumn.id,
|
|
266
|
+
})}
|
|
267
|
+
>
|
|
243
268
|
<Skeleton visible={loading} sx={!loading ? {borderRadius: 0} : undefined}>
|
|
244
269
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
245
270
|
</Skeleton>
|
|
@@ -251,10 +276,12 @@ export const Table: TableType = <T,>({
|
|
|
251
276
|
<tr>
|
|
252
277
|
<td
|
|
253
278
|
colSpan={columns.length + 1}
|
|
254
|
-
style={{padding: 0,
|
|
279
|
+
style={{padding: 0, borderTop: row.getIsExpanded() ? undefined : 'none'}}
|
|
255
280
|
>
|
|
256
|
-
<Collapse in={row.getIsExpanded()}
|
|
257
|
-
|
|
281
|
+
<Collapse in={row.getIsExpanded()}>
|
|
282
|
+
<Box px="sm" py="xs">
|
|
283
|
+
{rowChildren}
|
|
284
|
+
</Box>
|
|
258
285
|
</Collapse>
|
|
259
286
|
</td>
|
|
260
287
|
</tr>
|
|
@@ -272,9 +299,11 @@ export const Table: TableType = <T,>({
|
|
|
272
299
|
setState,
|
|
273
300
|
clearFilters,
|
|
274
301
|
getSelectedRow,
|
|
302
|
+
getSelectedRows,
|
|
275
303
|
clearSelection,
|
|
276
304
|
form,
|
|
277
305
|
containerRef: outsideClickRef,
|
|
306
|
+
multiRowSelectionEnabled,
|
|
278
307
|
}}
|
|
279
308
|
>
|
|
280
309
|
{header}
|
|
@@ -3,9 +3,9 @@ import {useTable} from './useTable';
|
|
|
3
3
|
|
|
4
4
|
interface TableActionsProps<T> {
|
|
5
5
|
/**
|
|
6
|
-
* Function that return components for the selected row
|
|
6
|
+
* Function that return components for the selected row or selected rows when multi row selection is enabled
|
|
7
7
|
*
|
|
8
|
-
* @param datum the data of the selected row
|
|
8
|
+
* @param datum the data of the selected row(s)
|
|
9
9
|
* @example
|
|
10
10
|
* <Table.Actions<MyType>>
|
|
11
11
|
* {(datum: MyType) => (
|
|
@@ -21,16 +21,16 @@ interface TableActionsProps<T> {
|
|
|
21
21
|
* )}
|
|
22
22
|
* </Table.Actions>
|
|
23
23
|
*/
|
|
24
|
-
children: (datum: T) => ReactNode;
|
|
24
|
+
children: ((datum: T) => ReactNode) | ((data: T[]) => ReactNode);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const TableActions = <T,>({children}: TableActionsProps<T>): ReactElement => {
|
|
28
|
-
const {
|
|
29
|
-
const
|
|
28
|
+
const {getSelectedRows, multiRowSelectionEnabled} = useTable();
|
|
29
|
+
const selectedRows = getSelectedRows();
|
|
30
30
|
|
|
31
|
-
if (
|
|
31
|
+
if (selectedRows.length <= 0) {
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
return <>{children(
|
|
35
|
+
return <>{children(multiRowSelectionEnabled ? selectedRows : selectedRows[0])}</>;
|
|
36
36
|
};
|
|
@@ -4,12 +4,12 @@ import {ColumnDef} from '@tanstack/table-core';
|
|
|
4
4
|
import {MouseEvent as ReactMouseEvent} from 'react';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Generic column to use when your table
|
|
7
|
+
* Generic column to use when your table needs collapsible rows
|
|
8
8
|
*/
|
|
9
9
|
export const TableCollapsibleColumn: ColumnDef<unknown> = {
|
|
10
10
|
id: 'collapsible',
|
|
11
|
-
header: '',
|
|
12
11
|
enableSorting: false,
|
|
12
|
+
header: '',
|
|
13
13
|
cell: (info) => {
|
|
14
14
|
const handler = info.row.getToggleExpandedHandler();
|
|
15
15
|
const onClick = (e: ReactMouseEvent<HTMLButtonElement>) => {
|
|
@@ -40,9 +40,13 @@ type TableContextType = {
|
|
|
40
40
|
*/
|
|
41
41
|
clearFilters: () => void;
|
|
42
42
|
/**
|
|
43
|
-
* Function that returns the selected row if any
|
|
43
|
+
* Function that returns the selected row if any.
|
|
44
44
|
*/
|
|
45
45
|
getSelectedRow: () => any | null;
|
|
46
|
+
/**
|
|
47
|
+
* Function that returns an array of the selected rows. Most useful when multi row selection is enabled.
|
|
48
|
+
*/
|
|
49
|
+
getSelectedRows: () => any[];
|
|
46
50
|
/**
|
|
47
51
|
* Function that clear the selected row
|
|
48
52
|
*/
|
|
@@ -55,6 +59,7 @@ type TableContextType = {
|
|
|
55
59
|
* Table container ref
|
|
56
60
|
*/
|
|
57
61
|
containerRef: RefObject<HTMLDivElement>;
|
|
62
|
+
multiRowSelectionEnabled: boolean;
|
|
58
63
|
};
|
|
59
64
|
|
|
60
65
|
export const TableContext = createContext<TableContextType | null>(null);
|
|
@@ -35,7 +35,13 @@ export const TableFilter: FunctionComponent<TableFilterProps> = ({
|
|
|
35
35
|
|
|
36
36
|
const handleSearchChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
37
37
|
const {value} = event.currentTarget;
|
|
38
|
-
setState((prevState: TableState) => ({
|
|
38
|
+
setState((prevState: TableState) => ({
|
|
39
|
+
...prevState,
|
|
40
|
+
pagination: prevState.pagination
|
|
41
|
+
? {pageIndex: 0, pageSize: prevState.pagination.pageSize}
|
|
42
|
+
: prevState.pagination,
|
|
43
|
+
globalFilter: value,
|
|
44
|
+
}));
|
|
39
45
|
};
|
|
40
46
|
|
|
41
47
|
return (
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {CrossSize16Px} from '@coveord/plasma-react-icons';
|
|
2
|
+
import {Button, createStyles, DefaultProps, Group, Selectors, Space, Tooltip} from '@mantine/core';
|
|
2
3
|
import {FunctionComponent, ReactNode} from 'react';
|
|
3
4
|
|
|
5
|
+
import {useTable} from './useTable';
|
|
6
|
+
|
|
4
7
|
const useStyles = createStyles((theme) => ({
|
|
5
8
|
root: {
|
|
6
9
|
position: 'sticky',
|
|
7
10
|
top: 0,
|
|
8
11
|
zIndex: 13, // skeleton is 11
|
|
9
12
|
backgroundColor: theme.colors.gray[1],
|
|
10
|
-
borderBottom: `1px solid ${theme.colors.gray[
|
|
13
|
+
borderBottom: `1px solid ${theme.colors.gray[3]}`,
|
|
11
14
|
},
|
|
12
15
|
}));
|
|
13
16
|
|
|
@@ -23,9 +26,26 @@ export const TableHeader: FunctionComponent<TableHeaderProps> = ({
|
|
|
23
26
|
children,
|
|
24
27
|
...others
|
|
25
28
|
}) => {
|
|
29
|
+
const {getSelectedRows, multiRowSelectionEnabled, clearSelection} = useTable();
|
|
26
30
|
const {classes} = useStyles(null, {name: 'TableHeader', classNames, styles, unstyled});
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
const selectedRows = getSelectedRows();
|
|
32
|
+
return multiRowSelectionEnabled ? (
|
|
33
|
+
<Group position="apart" className={classes.root}>
|
|
34
|
+
{selectedRows.length > 0 ? (
|
|
35
|
+
<Tooltip label="Unselect all">
|
|
36
|
+
<Button onClick={clearSelection} ml="lg" variant="subtle" leftIcon={<CrossSize16Px height={16} />}>
|
|
37
|
+
{selectedRows.length} selected
|
|
38
|
+
</Button>
|
|
39
|
+
</Tooltip>
|
|
40
|
+
) : (
|
|
41
|
+
<Space />
|
|
42
|
+
)}
|
|
43
|
+
<Group position="right" spacing="lg" px="md" py="sm" {...others}>
|
|
44
|
+
{children}
|
|
45
|
+
</Group>
|
|
46
|
+
</Group>
|
|
47
|
+
) : (
|
|
48
|
+
<Group position="right" spacing="lg" px="md" py="sm" className={classes.root} {...others}>
|
|
29
49
|
{children}
|
|
30
50
|
</Group>
|
|
31
51
|
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {Checkbox, Tooltip} from '@mantine/core';
|
|
2
|
+
import {ColumnDef} from '@tanstack/table-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generic column to use when your table needs multi selection of rows
|
|
6
|
+
*/
|
|
7
|
+
export const TableSelectableColumn: ColumnDef<unknown> = {
|
|
8
|
+
id: 'select',
|
|
9
|
+
enableSorting: false,
|
|
10
|
+
header: ({table}) => {
|
|
11
|
+
const label = table.getIsAllRowsSelected() ? 'Unselect all from this page' : 'Select all from this page';
|
|
12
|
+
return (
|
|
13
|
+
<Tooltip label={label}>
|
|
14
|
+
<Checkbox
|
|
15
|
+
checked={table.getIsAllPageRowsSelected()}
|
|
16
|
+
indeterminate={table.getIsSomePageRowsSelected()}
|
|
17
|
+
onChange={table.getToggleAllPageRowsSelectedHandler()}
|
|
18
|
+
sx={{display: 'flex'}}
|
|
19
|
+
aria-label={label}
|
|
20
|
+
/>
|
|
21
|
+
</Tooltip>
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
cell: ({row}) => (
|
|
25
|
+
<Checkbox
|
|
26
|
+
checked={row.getIsSelected()}
|
|
27
|
+
indeterminate={row.getIsSomeSelected()}
|
|
28
|
+
onChange={row.getToggleSelectedHandler()}
|
|
29
|
+
sx={{display: 'flex'}}
|
|
30
|
+
aria-label="Select row"
|
|
31
|
+
/>
|
|
32
|
+
),
|
|
33
|
+
};
|
|
@@ -4,27 +4,16 @@ import {defaultColumnSizing, flexRender, Header} from '@tanstack/react-table';
|
|
|
4
4
|
|
|
5
5
|
const useStyles = createStyles((theme) => ({
|
|
6
6
|
th: {
|
|
7
|
-
padding: '0 !important',
|
|
8
7
|
fontWeight: '400 !important' as any,
|
|
8
|
+
padding: '0 !important',
|
|
9
9
|
color: theme.black + '!important',
|
|
10
|
-
|
|
11
|
-
padding: '8px 16px',
|
|
12
|
-
div: {
|
|
13
|
-
padding: '0px !important',
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
div: {
|
|
17
|
-
padding: '8px 16px',
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
noSort: {
|
|
22
|
-
padding: `${theme.spacing.xs}px ${theme.spacing.md}px`,
|
|
10
|
+
verticalAlign: 'middle',
|
|
23
11
|
},
|
|
24
12
|
|
|
25
13
|
control: {
|
|
26
14
|
width: '100%',
|
|
27
|
-
padding: `${theme.spacing.xs}px ${theme.spacing.
|
|
15
|
+
padding: `${theme.spacing.xs}px ${theme.spacing.sm}px`,
|
|
16
|
+
whiteSpace: 'nowrap',
|
|
28
17
|
|
|
29
18
|
'&:hover': {
|
|
30
19
|
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.gray[6] : theme.colors.gray[2],
|
|
@@ -58,9 +47,7 @@ export const Th = <T,>({header}: ThProps<T>) => {
|
|
|
58
47
|
if (!header.column.getCanSort()) {
|
|
59
48
|
return (
|
|
60
49
|
<th className={classes.th} style={{width}}>
|
|
61
|
-
<Text
|
|
62
|
-
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
63
|
-
</Text>
|
|
50
|
+
<Text size="xs">{flexRender(header.column.columnDef.header, header.getContext())}</Text>
|
|
64
51
|
</th>
|
|
65
52
|
);
|
|
66
53
|
}
|
|
@@ -72,7 +59,7 @@ export const Th = <T,>({header}: ThProps<T>) => {
|
|
|
72
59
|
return (
|
|
73
60
|
<th className={classes.th} style={{width}} aria-sort={sortingOrder ? SortingLabels[sortingOrder] : 'none'}>
|
|
74
61
|
<UnstyledButton onClick={onSort} className={classes.control}>
|
|
75
|
-
<Group position="apart">
|
|
62
|
+
<Group position="apart" noWrap>
|
|
76
63
|
<Text size="xs">{flexRender(header.column.columnDef.header, header.getContext())}</Text>
|
|
77
64
|
<Center sx={(theme) => ({color: sortingOrder ? theme.colors.action[8] : undefined})}>
|
|
78
65
|
<Icon height={14} />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ColumnDef, createColumnHelper} from '@tanstack/table-core';
|
|
2
|
-
import {render, screen, userEvent, waitFor} from '@test-utils';
|
|
2
|
+
import {render, screen, userEvent, waitFor, within} from '@test-utils';
|
|
3
3
|
import {FunctionComponent} from 'react';
|
|
4
4
|
|
|
5
5
|
import {Table} from '../Table';
|
|
@@ -75,7 +75,7 @@ describe('Table', () => {
|
|
|
75
75
|
columnHelper.accessor('firstName', {
|
|
76
76
|
enableSorting: false,
|
|
77
77
|
}),
|
|
78
|
-
Table.CollapsibleColumn
|
|
78
|
+
Table.CollapsibleColumn as ColumnDef<RowData>,
|
|
79
79
|
];
|
|
80
80
|
render(
|
|
81
81
|
<Table
|
|
@@ -106,7 +106,7 @@ describe('Table', () => {
|
|
|
106
106
|
columnHelper.accessor('firstName', {
|
|
107
107
|
enableSorting: false,
|
|
108
108
|
}),
|
|
109
|
-
Table.CollapsibleColumn
|
|
109
|
+
Table.CollapsibleColumn as ColumnDef<RowData>,
|
|
110
110
|
];
|
|
111
111
|
render(
|
|
112
112
|
<Table
|
|
@@ -157,16 +157,109 @@ describe('Table', () => {
|
|
|
157
157
|
</div>
|
|
158
158
|
);
|
|
159
159
|
|
|
160
|
-
const row = screen.getByRole('row', {name: 'patate king'});
|
|
160
|
+
const row = screen.getByRole('row', {name: 'patate king', selected: false});
|
|
161
161
|
|
|
162
|
-
expect(row).
|
|
162
|
+
expect(row).toBeInTheDocument();
|
|
163
163
|
|
|
164
164
|
await user.click(row);
|
|
165
165
|
|
|
166
|
-
expect(
|
|
166
|
+
expect(screen.getByRole('row', {name: 'patate king', selected: true})).toBeInTheDocument();
|
|
167
167
|
|
|
168
168
|
await user.click(screen.getByText(/i'm a header/i));
|
|
169
169
|
|
|
170
|
-
expect(
|
|
170
|
+
expect(screen.getByRole('row', {name: 'patate king', selected: false})).toBeInTheDocument();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('when multi row selection is enabled', () => {
|
|
174
|
+
it('displays a checkbox as the first cell of each row', () => {
|
|
175
|
+
render(
|
|
176
|
+
<Table
|
|
177
|
+
data={[
|
|
178
|
+
{firstName: 'John', lastName: 'Smith'},
|
|
179
|
+
{firstName: 'Jane', lastName: 'Doe'},
|
|
180
|
+
]}
|
|
181
|
+
columns={columns}
|
|
182
|
+
multiRowSelectionEnabled
|
|
183
|
+
/>
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(screen.getByRole('columnheader', {name: /select all from this page/i})).toBeInTheDocument();
|
|
187
|
+
|
|
188
|
+
const rows = screen.getAllByRole('row');
|
|
189
|
+
rows.forEach((row) => {
|
|
190
|
+
expect(within(row).getByRole('checkbox', {name: /select/i})).toBeInTheDocument();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('selects all rows of the current page when clicking on the checkbox that is in the column header', async () => {
|
|
195
|
+
const user = userEvent.setup({delay: null});
|
|
196
|
+
render(
|
|
197
|
+
<Table
|
|
198
|
+
data={[
|
|
199
|
+
{firstName: 'John', lastName: 'Smith'},
|
|
200
|
+
{firstName: 'Jane', lastName: 'Doe'},
|
|
201
|
+
]}
|
|
202
|
+
columns={columns}
|
|
203
|
+
multiRowSelectionEnabled
|
|
204
|
+
/>
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const selectAll = screen.getByRole('checkbox', {name: /select all from this page/i});
|
|
208
|
+
await user.click(selectAll);
|
|
209
|
+
|
|
210
|
+
expect(screen.getAllByRole('row', {selected: true})).toHaveLength(2);
|
|
211
|
+
await user.click(selectAll);
|
|
212
|
+
|
|
213
|
+
expect(screen.queryAllByRole('row', {selected: true})).toEqual([]);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('does not clear the row selection when clicking outside the table', async () => {
|
|
217
|
+
const user = userEvent.setup({delay: null});
|
|
218
|
+
render(
|
|
219
|
+
<div>
|
|
220
|
+
<div>I'm a header</div>
|
|
221
|
+
<Table
|
|
222
|
+
data={[
|
|
223
|
+
{firstName: 'first', lastName: 'last'},
|
|
224
|
+
{firstName: 'patate', lastName: 'king'},
|
|
225
|
+
]}
|
|
226
|
+
columns={columns}
|
|
227
|
+
multiRowSelectionEnabled
|
|
228
|
+
/>
|
|
229
|
+
</div>
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const row = screen.getByRole('row', {name: /patate king/i, selected: false});
|
|
233
|
+
|
|
234
|
+
expect(row).toBeInTheDocument();
|
|
235
|
+
|
|
236
|
+
await user.click(row);
|
|
237
|
+
|
|
238
|
+
expect(screen.getByRole('row', {name: /patate king/i, selected: true})).toBeInTheDocument();
|
|
239
|
+
|
|
240
|
+
await user.click(screen.getByText(/i'm a header/i));
|
|
241
|
+
|
|
242
|
+
expect(screen.getByRole('row', {name: /patate king/i, selected: true})).toBeInTheDocument();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('unselects all the selected rows when clicking on the the unselect button from the table header', async () => {
|
|
246
|
+
const user = userEvent.setup({delay: null});
|
|
247
|
+
render(
|
|
248
|
+
<Table
|
|
249
|
+
data={[
|
|
250
|
+
{firstName: 'John', lastName: 'Smith'},
|
|
251
|
+
{firstName: 'Jane', lastName: 'Doe'},
|
|
252
|
+
]}
|
|
253
|
+
columns={columns}
|
|
254
|
+
multiRowSelectionEnabled
|
|
255
|
+
>
|
|
256
|
+
<Table.Header />
|
|
257
|
+
</Table>
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
await user.click(screen.getByRole('checkbox', {name: /select all/i}));
|
|
261
|
+
await user.click(screen.getByRole('button', {name: /2 selected/i}));
|
|
262
|
+
expect(screen.queryAllByRole('row', {selected: true})).toEqual([]);
|
|
263
|
+
});
|
|
171
264
|
});
|
|
172
265
|
});
|
|
@@ -34,4 +34,25 @@ describe('Table.Actions', () => {
|
|
|
34
34
|
expect(screen.queryByRole('button', {name: 'Eat fruit'})).not.toBeInTheDocument();
|
|
35
35
|
expect(screen.getByRole('button', {name: 'Eat vegetable'})).toBeVisible();
|
|
36
36
|
});
|
|
37
|
+
|
|
38
|
+
describe('when multi row selection is enabled', () => {
|
|
39
|
+
it('passes down an array of selected rows', async () => {
|
|
40
|
+
const user = userEvent.setup({delay: null});
|
|
41
|
+
const renderSpy = jest.fn().mockImplementation(() => <div />);
|
|
42
|
+
render(
|
|
43
|
+
<Table<RowData>
|
|
44
|
+
data={[{name: 'fruit'}, {name: 'vegetable'}, {name: 'bread'}]}
|
|
45
|
+
columns={columns}
|
|
46
|
+
multiRowSelectionEnabled
|
|
47
|
+
>
|
|
48
|
+
<Table.Header>
|
|
49
|
+
<Table.Actions>{renderSpy}</Table.Actions>
|
|
50
|
+
</Table.Header>
|
|
51
|
+
</Table>
|
|
52
|
+
);
|
|
53
|
+
await user.click(screen.getByRole('cell', {name: 'fruit'}));
|
|
54
|
+
await user.click(screen.getByRole('cell', {name: 'vegetable'}));
|
|
55
|
+
expect(renderSpy).toHaveBeenCalledWith([{name: 'fruit'}, {name: 'vegetable'}]);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
37
58
|
});
|