@coveord/plasma-mantine 52.26.6 → 52.27.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 +35 -34
- package/dist/.tsbuildinfo +1 -1
- package/dist/cjs/components/table/Table.d.ts.map +1 -1
- package/dist/cjs/components/table/Table.js +7 -3
- package/dist/cjs/components/table/Table.js.map +1 -1
- package/dist/cjs/components/table/Table.styles.d.ts +1 -0
- package/dist/cjs/components/table/Table.styles.d.ts.map +1 -1
- package/dist/cjs/components/table/Table.styles.js +6 -5
- package/dist/cjs/components/table/Table.styles.js.map +1 -1
- package/dist/cjs/components/table/Table.types.d.ts +8 -2
- package/dist/cjs/components/table/Table.types.d.ts.map +1 -1
- package/dist/cjs/components/table/layouts/RowLayout.js +2 -1
- package/dist/cjs/components/table/layouts/RowLayout.js.map +1 -1
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.d.ts +4 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.d.ts.map +1 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.js +99 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.js.map +1 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.styles.d.ts +10 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.styles.d.ts.map +1 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.styles.js +23 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.styles.js.map +1 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.types.d.ts +46 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.types.d.ts.map +1 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.types.js +6 -0
- package/dist/cjs/components/table/table-columns-selector/TableColumnsSelector.types.js.map +1 -0
- package/dist/esm/components/table/Table.d.ts.map +1 -1
- package/dist/esm/components/table/Table.js +6 -2
- package/dist/esm/components/table/Table.js.map +1 -1
- package/dist/esm/components/table/Table.styles.d.ts +1 -0
- package/dist/esm/components/table/Table.styles.d.ts.map +1 -1
- package/dist/esm/components/table/Table.styles.js +6 -5
- package/dist/esm/components/table/Table.styles.js.map +1 -1
- package/dist/esm/components/table/Table.types.d.ts +8 -2
- package/dist/esm/components/table/Table.types.d.ts.map +1 -1
- package/dist/esm/components/table/Table.types.js.map +1 -1
- package/dist/esm/components/table/layouts/RowLayout.js +1 -1
- package/dist/esm/components/table/layouts/RowLayout.js.map +1 -1
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.d.ts +4 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.d.ts.map +1 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.js +84 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.js.map +1 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.styles.d.ts +10 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.styles.d.ts.map +1 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.styles.js +11 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.styles.js.map +1 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.types.d.ts +46 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.types.d.ts.map +1 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.types.js +3 -0
- package/dist/esm/components/table/table-columns-selector/TableColumnsSelector.types.js.map +1 -0
- package/package.json +1 -1
- package/src/components/table/Table.styles.ts +6 -5
- package/src/components/table/Table.tsx +6 -1
- package/src/components/table/Table.types.ts +8 -1
- package/src/components/table/__tests__/TableColumnsSelector.spec.tsx +260 -0
- package/src/components/table/layouts/RowLayout.tsx +1 -1
- package/src/components/table/table-columns-selector/TableColumnsSelector.styles.ts +8 -0
- package/src/components/table/table-columns-selector/TableColumnsSelector.tsx +78 -0
- package/src/components/table/table-columns-selector/TableColumnsSelector.types.ts +46 -0
|
@@ -21,6 +21,7 @@ import {TableLayouts} from './layouts/TableLayouts';
|
|
|
21
21
|
import {TableActions} from './table-actions/TableActions';
|
|
22
22
|
import {TableAccordionColumn, TableCollapsibleColumn} from './table-column/TableCollapsibleColumn';
|
|
23
23
|
import {TableSelectableColumn} from './table-column/TableSelectableColumn';
|
|
24
|
+
import {TableColumnsSelector} from './table-columns-selector/TableColumnsSelector';
|
|
24
25
|
import {TableConsumer} from './table-consumer/TableConsumer';
|
|
25
26
|
import {TableDateRangePicker} from './table-date-range-picker/TableDateRangePicker';
|
|
26
27
|
import {TableFilter} from './table-filter/TableFilter';
|
|
@@ -91,6 +92,8 @@ export const Table: TableType = <T,>({
|
|
|
91
92
|
...options,
|
|
92
93
|
});
|
|
93
94
|
|
|
95
|
+
const getAllColumns = table.getAllFlatColumns;
|
|
96
|
+
|
|
94
97
|
const [state, setState] = useState<TableState<T>>(table.initialState as TableState<T>);
|
|
95
98
|
table.setOptions((prev) => ({
|
|
96
99
|
...prev,
|
|
@@ -144,7 +147,7 @@ export const Table: TableType = <T,>({
|
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
const Layout = layouts.find(({name}) => name === form.values.layout);
|
|
147
|
-
const hasRows = table.getRowModel()
|
|
150
|
+
const hasRows = table.getRowModel()?.rows.length > 0;
|
|
148
151
|
|
|
149
152
|
return (
|
|
150
153
|
<Box ref={outsideClickRef}>
|
|
@@ -164,6 +167,7 @@ export const Table: TableType = <T,>({
|
|
|
164
167
|
getPageCount: table.getPageCount,
|
|
165
168
|
disableRowSelection,
|
|
166
169
|
layouts,
|
|
170
|
+
getAllColumns,
|
|
167
171
|
}}
|
|
168
172
|
>
|
|
169
173
|
{consumer}
|
|
@@ -217,6 +221,7 @@ export const Table: TableType = <T,>({
|
|
|
217
221
|
);
|
|
218
222
|
};
|
|
219
223
|
|
|
224
|
+
Table.ColumnsSelector = TableColumnsSelector;
|
|
220
225
|
Table.Actions = TableActions;
|
|
221
226
|
Table.Filter = TableFilter;
|
|
222
227
|
Table.Footer = TableFooter;
|
|
@@ -2,6 +2,7 @@ import {Icon} from '@coveord/plasma-react-icons';
|
|
|
2
2
|
import {DefaultProps, Selectors} from '@mantine/core';
|
|
3
3
|
import {UseFormReturnType} from '@mantine/form';
|
|
4
4
|
import {
|
|
5
|
+
Column,
|
|
5
6
|
ColumnDef,
|
|
6
7
|
CoreOptions,
|
|
7
8
|
TableOptions,
|
|
@@ -11,10 +12,12 @@ import {
|
|
|
11
12
|
import {Dispatch, ReactElement, ReactNode, RefObject} from 'react';
|
|
12
13
|
|
|
13
14
|
import {DateRangePickerValue} from '../date-range-picker/DateRangePickerInlineCalendar';
|
|
15
|
+
import useStyles from './Table.styles';
|
|
14
16
|
import {TableLayoutProps} from './layouts/RowLayout.types'; // TODO https://coveord.atlassian.net/browse/ADUI-9182
|
|
15
17
|
import {TableLayouts} from './layouts/TableLayouts';
|
|
16
18
|
import {TableActions} from './table-actions/TableActions';
|
|
17
19
|
import {TableAccordionColumn, TableCollapsibleColumn} from './table-column/TableCollapsibleColumn';
|
|
20
|
+
import {TableColumnsSelector} from './table-columns-selector/TableColumnsSelector';
|
|
18
21
|
import {TableConsumer} from './table-consumer/TableConsumer';
|
|
19
22
|
import {TableDateRangePicker} from './table-date-range-picker/TableDateRangePicker';
|
|
20
23
|
import {TableFilter} from './table-filter/TableFilter';
|
|
@@ -25,7 +28,6 @@ import {TableLoading} from './table-loading/TableLoading';
|
|
|
25
28
|
import {TablePagination} from './table-pagination/TablePagination';
|
|
26
29
|
import {TablePerPage} from './table-per-page/TablePerPage';
|
|
27
30
|
import {TablePredicate} from './table-predicate/TablePredicate';
|
|
28
|
-
import useStyles from './Table.styles';
|
|
29
31
|
|
|
30
32
|
export type RowSelectionWithData<TData> = Record<string, TData>;
|
|
31
33
|
export interface RowSelectionState<TData> {
|
|
@@ -84,6 +86,10 @@ export type TableFormType = {
|
|
|
84
86
|
};
|
|
85
87
|
|
|
86
88
|
export type TableContextType<TData> = {
|
|
89
|
+
/**
|
|
90
|
+
* Returns all flat columns in the table.
|
|
91
|
+
*/
|
|
92
|
+
getAllColumns: () => Array<Column<TData, unknown>>;
|
|
87
93
|
/**
|
|
88
94
|
* Function to call when the table needs an update
|
|
89
95
|
*/
|
|
@@ -262,6 +268,7 @@ export interface TableProps<T> extends DefaultProps<TableStylesNames> {
|
|
|
262
268
|
|
|
263
269
|
export interface TableType {
|
|
264
270
|
<T>(props: TableProps<T>): ReactElement;
|
|
271
|
+
ColumnsSelector: typeof TableColumnsSelector;
|
|
265
272
|
Actions: typeof TableActions;
|
|
266
273
|
Filter: typeof TableFilter;
|
|
267
274
|
Footer: typeof TableFooter;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import {Box} from '@mantine/core';
|
|
2
|
+
import {ColumnDef, createColumnHelper} from '@tanstack/table-core';
|
|
3
|
+
import {render, screen, userEvent} from '@test-utils';
|
|
4
|
+
import {Table} from '../Table';
|
|
5
|
+
import {TableColumnsSelector} from '../table-columns-selector/TableColumnsSelector';
|
|
6
|
+
|
|
7
|
+
const mockData = [
|
|
8
|
+
{
|
|
9
|
+
name: 'John Doe',
|
|
10
|
+
age: 30,
|
|
11
|
+
email: 'john.doe@example.com',
|
|
12
|
+
phone: '123-456-7890',
|
|
13
|
+
body: 'coucou',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'Jane Doe',
|
|
17
|
+
age: 25,
|
|
18
|
+
email: 'jane.doe@example.com',
|
|
19
|
+
phone: '098-765-4321',
|
|
20
|
+
body: 'coucou 2',
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const columnNames: Record<string, string> = {
|
|
25
|
+
name: 'Name',
|
|
26
|
+
age: 'Age',
|
|
27
|
+
email: 'Email',
|
|
28
|
+
phone: 'Phone',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type RowData = {
|
|
32
|
+
name: string;
|
|
33
|
+
age: number;
|
|
34
|
+
email: string;
|
|
35
|
+
phone: string;
|
|
36
|
+
body: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const columnHelper = createColumnHelper<RowData>();
|
|
40
|
+
const columns: Array<ColumnDef<RowData>> = [
|
|
41
|
+
columnHelper.accessor('name', {enableSorting: false}),
|
|
42
|
+
columnHelper.accessor('age', {enableSorting: false}),
|
|
43
|
+
columnHelper.accessor('email', {enableSorting: false}),
|
|
44
|
+
columnHelper.accessor('phone', {enableSorting: false}),
|
|
45
|
+
Table.CollapsibleColumn as ColumnDef<RowData>,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
describe('TableColumnsSelector', () => {
|
|
49
|
+
it('render the edit button in the table header', () => {
|
|
50
|
+
render(
|
|
51
|
+
<Table data={mockData} columns={columns}>
|
|
52
|
+
<Table.Header>
|
|
53
|
+
<TableColumnsSelector columnNames={columnNames} />
|
|
54
|
+
</Table.Header>
|
|
55
|
+
</Table>,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(screen.getByRole('button', {name: 'Edit columns'})).toBeVisible();
|
|
59
|
+
expect(screen.queryByRole('button', {name: 'Edit columns (4)'})).not.toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('renders the custom label when defined', () => {
|
|
63
|
+
render(
|
|
64
|
+
<Table data={mockData} columns={columns}>
|
|
65
|
+
<Table.Header>
|
|
66
|
+
<TableColumnsSelector columnNames={columnNames} label="Custom label" />
|
|
67
|
+
</Table.Header>
|
|
68
|
+
</Table>,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(screen.getByRole('button', {name: 'Custom label'})).toBeVisible();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('renders the count of visible columns if showVisibleCountLabel is true', () => {
|
|
75
|
+
render(
|
|
76
|
+
<Table data={mockData} columns={columns}>
|
|
77
|
+
<Table.Header>
|
|
78
|
+
<TableColumnsSelector columnNames={columnNames} showVisibleCountLabel />
|
|
79
|
+
</Table.Header>
|
|
80
|
+
</Table>,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(screen.getByRole('button', {name: 'Edit columns (4)'})).toBeVisible();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('renders all columns in the dropdown, except the collapsible and the multiselectRow by default', async () => {
|
|
87
|
+
const user = userEvent.setup();
|
|
88
|
+
render(
|
|
89
|
+
<Table
|
|
90
|
+
data={mockData}
|
|
91
|
+
columns={columns}
|
|
92
|
+
multiRowSelectionEnabled
|
|
93
|
+
getExpandChildren={(datum) => <Box py="xs">{datum.body}</Box>}
|
|
94
|
+
>
|
|
95
|
+
<Table.Header>
|
|
96
|
+
<TableColumnsSelector columnNames={columnNames} />
|
|
97
|
+
</Table.Header>
|
|
98
|
+
</Table>,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
await user.click(screen.getByRole('button', {name: 'Edit columns'}));
|
|
102
|
+
|
|
103
|
+
// columns are rendered
|
|
104
|
+
expect(screen.getAllByRole('button', {name: /arrowheaddown/i})).toHaveLength(2);
|
|
105
|
+
expect(screen.getAllByRole('checkbox', {name: /select row/i})).toHaveLength(2);
|
|
106
|
+
|
|
107
|
+
expect(screen.getByRole('checkbox', {name: 'Name'})).toBeVisible();
|
|
108
|
+
expect(screen.getByRole('checkbox', {name: 'Age'})).toBeVisible();
|
|
109
|
+
expect(screen.getByRole('checkbox', {name: 'Email'})).toBeVisible();
|
|
110
|
+
expect(screen.getByRole('checkbox', {name: 'Phone'})).toBeVisible();
|
|
111
|
+
|
|
112
|
+
// columns are not in the dropdown
|
|
113
|
+
expect(screen.queryByRole('checkbox', {name: /collapsible/i})).not.toBeInTheDocument();
|
|
114
|
+
expect(screen.queryByRole('checkbox', {name: 'select'})).not.toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('renders all checkboxes checked by default', async () => {
|
|
118
|
+
const user = userEvent.setup();
|
|
119
|
+
render(
|
|
120
|
+
<Table data={mockData} columns={columns}>
|
|
121
|
+
<Table.Header>
|
|
122
|
+
<TableColumnsSelector columnNames={columnNames} />
|
|
123
|
+
</Table.Header>
|
|
124
|
+
</Table>,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
await user.click(screen.getByRole('button', {name: 'Edit columns'}));
|
|
128
|
+
|
|
129
|
+
expect(screen.getByRole('checkbox', {name: 'Name'})).toBeChecked();
|
|
130
|
+
expect(screen.getByRole('checkbox', {name: 'Age'})).toBeChecked();
|
|
131
|
+
expect(screen.getByRole('checkbox', {name: 'Email'})).toBeChecked();
|
|
132
|
+
expect(screen.getByRole('checkbox', {name: 'Phone'})).toBeChecked();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('does not render the checkboxes for the columns that are in the nonHideableColumns prop', async () => {
|
|
136
|
+
const user = userEvent.setup();
|
|
137
|
+
render(
|
|
138
|
+
<Table data={mockData} columns={columns}>
|
|
139
|
+
<Table.Header>
|
|
140
|
+
<TableColumnsSelector columnNames={columnNames} nonHideableColumns={['name']} />
|
|
141
|
+
</Table.Header>
|
|
142
|
+
</Table>,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
await user.click(screen.getByRole('button', {name: 'Edit columns'}));
|
|
146
|
+
|
|
147
|
+
expect(screen.queryByRole('checkbox', {name: 'Name'})).not.toBeInTheDocument();
|
|
148
|
+
expect(screen.getByRole('checkbox', {name: 'Age'})).toBeChecked();
|
|
149
|
+
expect(screen.getByRole('checkbox', {name: 'Email'})).toBeChecked();
|
|
150
|
+
expect(screen.getByRole('checkbox', {name: 'Phone'})).toBeChecked();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('renders unchecked checkboxes for the columns that are not visible in the inital state of the table', async () => {
|
|
154
|
+
const user = userEvent.setup();
|
|
155
|
+
render(
|
|
156
|
+
<Table data={mockData} columns={columns} initialState={{columnVisibility: {email: false}}}>
|
|
157
|
+
<Table.Header>
|
|
158
|
+
<TableColumnsSelector columnNames={columnNames} />
|
|
159
|
+
</Table.Header>
|
|
160
|
+
</Table>,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
await user.click(screen.getByRole('button', {name: 'Edit columns'}));
|
|
164
|
+
|
|
165
|
+
expect(screen.getByRole('checkbox', {name: 'Name'})).toBeChecked();
|
|
166
|
+
expect(screen.getByRole('checkbox', {name: 'Age'})).toBeChecked();
|
|
167
|
+
expect(screen.getByRole('checkbox', {name: 'Email'})).not.toBeChecked();
|
|
168
|
+
expect(screen.getByRole('checkbox', {name: 'Phone'})).toBeChecked();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('renders disabled checkboxes when the maxSelectableColumns is set and the maximum number of columns is checked', async () => {
|
|
172
|
+
const user = userEvent.setup();
|
|
173
|
+
render(
|
|
174
|
+
<Table data={mockData} columns={columns} initialState={{columnVisibility: {email: false}}}>
|
|
175
|
+
<Table.Header>
|
|
176
|
+
<TableColumnsSelector columnNames={columnNames} maxSelectableColumns={3} />
|
|
177
|
+
</Table.Header>
|
|
178
|
+
</Table>,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
await user.click(screen.getByRole('button', {name: 'Edit columns'}));
|
|
182
|
+
|
|
183
|
+
const nameCheckBox = screen.getByRole('checkbox', {name: /name/i});
|
|
184
|
+
const ageCheckBox = screen.getByRole('checkbox', {name: /age/i});
|
|
185
|
+
const emailCheckBox = screen.getByRole('checkbox', {name: /email/i});
|
|
186
|
+
const phoneCheckBox = screen.getByRole('checkbox', {name: /phone/i});
|
|
187
|
+
|
|
188
|
+
expect(nameCheckBox).toBeChecked();
|
|
189
|
+
expect(nameCheckBox).toBeEnabled();
|
|
190
|
+
expect(ageCheckBox).toBeChecked();
|
|
191
|
+
expect(ageCheckBox).toBeEnabled();
|
|
192
|
+
expect(emailCheckBox).not.toBeChecked();
|
|
193
|
+
expect(emailCheckBox).toBeDisabled();
|
|
194
|
+
expect(phoneCheckBox).toBeChecked();
|
|
195
|
+
expect(phoneCheckBox).toBeEnabled();
|
|
196
|
+
|
|
197
|
+
await user.click(nameCheckBox);
|
|
198
|
+
|
|
199
|
+
expect(nameCheckBox).toBeEnabled();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('renders a tooltip when the maxSelectableColumns is set and the maximum number of columns is checked and the user hover a disabled checkbox', async () => {
|
|
203
|
+
const user = userEvent.setup();
|
|
204
|
+
render(
|
|
205
|
+
<Table data={mockData} columns={columns} initialState={{columnVisibility: {email: false}}}>
|
|
206
|
+
<Table.Header>
|
|
207
|
+
<TableColumnsSelector
|
|
208
|
+
columnNames={columnNames}
|
|
209
|
+
maxSelectableColumns={3}
|
|
210
|
+
limitReachedTooltip="You can display up to 3 columns"
|
|
211
|
+
/>
|
|
212
|
+
</Table.Header>
|
|
213
|
+
</Table>,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
await user.click(screen.getByRole('button', {name: 'Edit columns'}));
|
|
217
|
+
|
|
218
|
+
const emailCheckBoxWrapper = screen.getByRole('checkbox', {name: /email/i}).parentElement;
|
|
219
|
+
|
|
220
|
+
await user.hover(emailCheckBoxWrapper);
|
|
221
|
+
|
|
222
|
+
expect(screen.getByText('You can display up to 3 columns')).toBeVisible();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe('footer', () => {
|
|
226
|
+
it('does not render the footer when maxSelectableColumns is not defined', async () => {
|
|
227
|
+
const user = userEvent.setup();
|
|
228
|
+
render(
|
|
229
|
+
<Table data={mockData} columns={columns}>
|
|
230
|
+
<Table.Header>
|
|
231
|
+
<TableColumnsSelector columnNames={columnNames} />
|
|
232
|
+
</Table.Header>
|
|
233
|
+
</Table>,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
await user.click(screen.getByRole('button', {name: 'Edit columns'}));
|
|
237
|
+
|
|
238
|
+
expect(screen.queryByText('You can display up to 3 columns')).not.toBeInTheDocument();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('renders the footer when maxSelectableColumns is defined and footer is defined', async () => {
|
|
242
|
+
const user = userEvent.setup();
|
|
243
|
+
render(
|
|
244
|
+
<Table data={mockData} columns={columns}>
|
|
245
|
+
<Table.Header>
|
|
246
|
+
<TableColumnsSelector
|
|
247
|
+
columnNames={columnNames}
|
|
248
|
+
maxSelectableColumns={3}
|
|
249
|
+
footer="You can display so many patate"
|
|
250
|
+
/>
|
|
251
|
+
</Table.Header>
|
|
252
|
+
</Table>,
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
await user.click(screen.getByRole('button', {name: 'Edit columns'}));
|
|
256
|
+
|
|
257
|
+
expect(screen.getByText('You can display so many patate')).toBeVisible();
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
@@ -50,7 +50,7 @@ const RowLayoutBody = <T,>({
|
|
|
50
50
|
cell.querySelector('button').click();
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
const rows = table.getRowModel()
|
|
53
|
+
const rows = table.getRowModel()?.rows.map((row) => {
|
|
54
54
|
const rowChildren = getExpandChildren?.(row.original) ?? null;
|
|
55
55
|
const isSelected = !!row.getIsSelected();
|
|
56
56
|
const shouldKeepSelection = keepSelection && isSelected;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {Button, Checkbox, Divider, Grid, Popover, ScrollArea, Stack, Tooltip} from '@mantine/core';
|
|
2
|
+
import {FunctionComponent} from 'react';
|
|
3
|
+
|
|
4
|
+
import {TableComponentsOrder} from '../Table.styles';
|
|
5
|
+
import {useTable} from '../TableContext';
|
|
6
|
+
import useStyles from './TableColumnsSelector.styles';
|
|
7
|
+
import {TableColumnsSelectorProps} from './TableColumnsSelector.types';
|
|
8
|
+
|
|
9
|
+
const COLUMNS_IDS_TO_EXCLUDE = ['collapsible', 'select'];
|
|
10
|
+
|
|
11
|
+
export const TableColumnsSelector: FunctionComponent<TableColumnsSelectorProps> = ({
|
|
12
|
+
classNames,
|
|
13
|
+
styles,
|
|
14
|
+
unstyled,
|
|
15
|
+
label = 'Edit columns',
|
|
16
|
+
buttonVariant = 'outline',
|
|
17
|
+
showVisibleCountLabel = false,
|
|
18
|
+
nonHideableColumns = [],
|
|
19
|
+
maxSelectableColumns,
|
|
20
|
+
footer,
|
|
21
|
+
limitReachedTooltip = 'You have reached the maximum display limit.',
|
|
22
|
+
columnNames,
|
|
23
|
+
}) => {
|
|
24
|
+
const {classes} = useStyles(null, {name: 'TableColumnsSelector', classNames, styles, unstyled});
|
|
25
|
+
const {getAllColumns} = useTable();
|
|
26
|
+
|
|
27
|
+
const columnsToExclude = [...nonHideableColumns, ...COLUMNS_IDS_TO_EXCLUDE];
|
|
28
|
+
|
|
29
|
+
const filteredColumns = getAllColumns().filter((column) => !columnsToExclude.includes(column.id));
|
|
30
|
+
|
|
31
|
+
const selectedColumnsCount = filteredColumns.filter((column) => column.getIsVisible()).length;
|
|
32
|
+
|
|
33
|
+
if (filteredColumns.length <= 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Grid.Col span="content" order={TableComponentsOrder.ColumnsSelector} py="sm" className={classes.root}>
|
|
39
|
+
<Popover withinPortal position="bottom" shadow="md">
|
|
40
|
+
<Popover.Target>
|
|
41
|
+
<Button variant={buttonVariant}>{`${label}${
|
|
42
|
+
showVisibleCountLabel ? ` (${selectedColumnsCount})` : ''
|
|
43
|
+
}`}</Button>
|
|
44
|
+
</Popover.Target>
|
|
45
|
+
<Popover.Dropdown miw={240}>
|
|
46
|
+
<ScrollArea.Autosize mah={154}>
|
|
47
|
+
<Stack>
|
|
48
|
+
{filteredColumns.map((column) => {
|
|
49
|
+
const isDisabled =
|
|
50
|
+
selectedColumnsCount >= maxSelectableColumns && !column.getIsVisible();
|
|
51
|
+
return (
|
|
52
|
+
<Tooltip label={limitReachedTooltip} disabled={!isDisabled} position="left">
|
|
53
|
+
<div>
|
|
54
|
+
<Checkbox
|
|
55
|
+
key={column.id}
|
|
56
|
+
label={columnNames?.[column.id] || column.id}
|
|
57
|
+
name={column.id}
|
|
58
|
+
checked={column.getIsVisible()}
|
|
59
|
+
disabled={isDisabled}
|
|
60
|
+
onChange={column.getToggleVisibilityHandler()}
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
</Tooltip>
|
|
64
|
+
);
|
|
65
|
+
})}
|
|
66
|
+
</Stack>
|
|
67
|
+
</ScrollArea.Autosize>
|
|
68
|
+
{maxSelectableColumns && (
|
|
69
|
+
<>
|
|
70
|
+
<Divider mb="xs" mt="sm" />
|
|
71
|
+
{footer}
|
|
72
|
+
</>
|
|
73
|
+
)}
|
|
74
|
+
</Popover.Dropdown>
|
|
75
|
+
</Popover>
|
|
76
|
+
</Grid.Col>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {DefaultProps, Selectors} from '@mantine/core';
|
|
2
|
+
import {ReactNode} from 'react';
|
|
3
|
+
import useStyles from './TableColumnsSelector.styles';
|
|
4
|
+
|
|
5
|
+
type TableColumnsSelectorStylesNames = Selectors<typeof useStyles>;
|
|
6
|
+
|
|
7
|
+
export interface TableColumnsSelectorProps extends DefaultProps<TableColumnsSelectorStylesNames> {
|
|
8
|
+
/**
|
|
9
|
+
* The label of the button
|
|
10
|
+
* @default 'Edit columns'
|
|
11
|
+
*/
|
|
12
|
+
label?: ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* The style variant of the button
|
|
15
|
+
* @default 'outline'
|
|
16
|
+
*/
|
|
17
|
+
buttonVariant?: string;
|
|
18
|
+
/**
|
|
19
|
+
* An array of column ids that the user cannot hide. This is useful for columns that are required for the table to function properly.
|
|
20
|
+
* @default []
|
|
21
|
+
*/
|
|
22
|
+
nonHideableColumns?: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Whether the count of visible columns is shown in the button label.
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
showVisibleCountLabel?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* The maximum number of columns that can be selected at the same time.
|
|
30
|
+
* If defined a footer will render with the remaining number of columns that can be selected.
|
|
31
|
+
*/
|
|
32
|
+
maxSelectableColumns?: number;
|
|
33
|
+
/**
|
|
34
|
+
* A dictionary of column ids and names to use for the checkbox labels.
|
|
35
|
+
*/
|
|
36
|
+
columnNames: Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* The content to display in the footer when maxSelectableColumns is defined.
|
|
39
|
+
*/
|
|
40
|
+
footer?: ReactNode;
|
|
41
|
+
/**
|
|
42
|
+
* The tooltip to display when the user hovers over a disabled checkbox.
|
|
43
|
+
* @default 'You have reached the maximum display limit.'
|
|
44
|
+
*/
|
|
45
|
+
limitReachedTooltip?: string;
|
|
46
|
+
}
|