@coveord/plasma-mantine 52.9.0 → 52.10.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 +33 -32
- package/dist/.tsbuildinfo +1 -1
- package/dist/cjs/components/code-editor/CodeEditor.d.ts.map +1 -1
- package/dist/cjs/components/code-editor/CodeEditor.js +4 -2
- package/dist/cjs/components/code-editor/CodeEditor.js.map +1 -1
- package/dist/cjs/components/date-range-picker/DateRangePickerPresetSelect.d.ts.map +1 -1
- package/dist/cjs/components/date-range-picker/DateRangePickerPresetSelect.js +2 -1
- package/dist/cjs/components/date-range-picker/DateRangePickerPresetSelect.js.map +1 -1
- package/dist/cjs/components/table/Table.d.ts.map +1 -1
- package/dist/cjs/components/table/Table.js +23 -12
- package/dist/cjs/components/table/Table.js.map +1 -1
- package/dist/cjs/components/table/Table.styles.d.ts.map +1 -1
- package/dist/cjs/components/table/Table.styles.js +1 -10
- package/dist/cjs/components/table/Table.styles.js.map +1 -1
- package/dist/cjs/components/table/Table.types.d.ts +14 -3
- package/dist/cjs/components/table/Table.types.d.ts.map +1 -1
- package/dist/cjs/components/table/TableFooter.js +2 -2
- package/dist/cjs/components/table/TableFooter.js.map +1 -1
- package/dist/cjs/components/table/TableHeader.js +1 -1
- package/dist/cjs/components/table/TableHeader.js.map +1 -1
- package/dist/cjs/components/table/TableLastUpdated.d.ts +24 -0
- package/dist/cjs/components/table/TableLastUpdated.d.ts.map +1 -0
- package/dist/cjs/components/table/TableLastUpdated.js +73 -0
- package/dist/cjs/components/table/TableLastUpdated.js.map +1 -0
- package/dist/cjs/components/table/TablePagination.d.ts.map +1 -1
- package/dist/cjs/components/table/TablePagination.js +1 -0
- package/dist/cjs/components/table/TablePagination.js.map +1 -1
- package/dist/cjs/components/table/TablePerPage.js +3 -1
- package/dist/cjs/components/table/TablePerPage.js.map +1 -1
- package/dist/cjs/components/table/layouts/RowLayout.d.ts.map +1 -1
- package/dist/cjs/components/table/layouts/RowLayout.js +41 -9
- package/dist/cjs/components/table/layouts/RowLayout.js.map +1 -1
- package/dist/cjs/components/table/useRowSelection.d.ts +2 -2
- package/dist/cjs/components/table/useRowSelection.d.ts.map +1 -1
- package/dist/cjs/components/table/useRowSelection.js +8 -3
- package/dist/cjs/components/table/useRowSelection.js.map +1 -1
- package/dist/esm/components/code-editor/CodeEditor.d.ts.map +1 -1
- package/dist/esm/components/code-editor/CodeEditor.js +4 -2
- package/dist/esm/components/code-editor/CodeEditor.js.map +1 -1
- package/dist/esm/components/date-range-picker/DateRangePickerPresetSelect.d.ts.map +1 -1
- package/dist/esm/components/date-range-picker/DateRangePickerPresetSelect.js +2 -1
- package/dist/esm/components/date-range-picker/DateRangePickerPresetSelect.js.map +1 -1
- package/dist/esm/components/table/Table.d.ts.map +1 -1
- package/dist/esm/components/table/Table.js +25 -14
- package/dist/esm/components/table/Table.js.map +1 -1
- package/dist/esm/components/table/Table.styles.d.ts.map +1 -1
- package/dist/esm/components/table/Table.styles.js +1 -10
- package/dist/esm/components/table/Table.styles.js.map +1 -1
- package/dist/esm/components/table/Table.types.d.ts +14 -3
- 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/TableFooter.js +2 -2
- package/dist/esm/components/table/TableFooter.js.map +1 -1
- package/dist/esm/components/table/TableHeader.js +1 -1
- package/dist/esm/components/table/TableHeader.js.map +1 -1
- package/dist/esm/components/table/TableLastUpdated.d.ts +24 -0
- package/dist/esm/components/table/TableLastUpdated.d.ts.map +1 -0
- package/dist/esm/components/table/TableLastUpdated.js +62 -0
- package/dist/esm/components/table/TableLastUpdated.js.map +1 -0
- package/dist/esm/components/table/TablePagination.d.ts.map +1 -1
- package/dist/esm/components/table/TablePagination.js +1 -0
- package/dist/esm/components/table/TablePagination.js.map +1 -1
- package/dist/esm/components/table/TablePerPage.js +3 -1
- package/dist/esm/components/table/TablePerPage.js.map +1 -1
- package/dist/esm/components/table/layouts/RowLayout.d.ts.map +1 -1
- package/dist/esm/components/table/layouts/RowLayout.js +42 -10
- package/dist/esm/components/table/layouts/RowLayout.js.map +1 -1
- package/dist/esm/components/table/useRowSelection.d.ts +2 -2
- package/dist/esm/components/table/useRowSelection.d.ts.map +1 -1
- package/dist/esm/components/table/useRowSelection.js +8 -3
- package/dist/esm/components/table/useRowSelection.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/VitestSetup.ts +12 -0
- package/src/components/code-editor/CodeEditor.tsx +4 -2
- package/src/components/code-editor/__tests__/CodeEditor.spec.tsx +1 -0
- package/src/components/date-range-picker/DateRangePickerPresetSelect.tsx +1 -0
- package/src/components/date-range-picker/__tests__/DateRangePickerInlineCalendar.spec.tsx +2 -0
- package/src/components/date-range-picker/__tests__/DateRangePickerPopoverCalendar.spec.tsx +4 -19
- package/src/components/date-range-picker/__tests__/EditableDateRangePicker.spec.tsx +3 -3
- package/src/components/modal-wizard/__tests__/ModalWizard.spec.tsx +19 -4
- package/src/components/table/Table.styles.ts +0 -9
- package/src/components/table/Table.tsx +22 -13
- package/src/components/table/Table.types.ts +14 -3
- package/src/components/table/TableFooter.tsx +1 -1
- package/src/components/table/TableHeader.tsx +1 -1
- package/src/components/table/TableLastUpdated.tsx +51 -0
- package/src/components/table/TablePagination.tsx +1 -0
- package/src/components/table/TablePerPage.tsx +3 -3
- package/src/components/table/__tests__/Table.spec.tsx +44 -5
- package/src/components/table/__tests__/TableActions.spec.tsx +4 -3
- package/src/components/table/__tests__/TableDateRangePicker.spec.tsx +26 -59
- package/src/components/table/__tests__/TableLastUpdated.spec.tsx +97 -0
- package/src/components/table/__tests__/TablePredicate.spec.tsx +7 -55
- package/src/components/table/layouts/RowLayout.tsx +45 -11
- package/src/components/table/useRowSelection.ts +13 -6
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {createStyles, DefaultProps, Group, Selectors, Text} from '@mantine/core';
|
|
2
|
+
import {useDidUpdate} from '@mantine/hooks';
|
|
3
|
+
import dayjs from 'dayjs';
|
|
4
|
+
import {FunctionComponent, useState} from 'react';
|
|
5
|
+
import {useTable} from './TableContext';
|
|
6
|
+
|
|
7
|
+
const useStyles = createStyles((theme) => ({
|
|
8
|
+
root: {
|
|
9
|
+
minHeight: '98px',
|
|
10
|
+
},
|
|
11
|
+
label: {
|
|
12
|
+
color: theme.colors.gray[6],
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
type TableLastUpdatedStylesNames = Selectors<typeof useStyles>;
|
|
17
|
+
|
|
18
|
+
interface TableLastUpdatedProps extends DefaultProps<TableLastUpdatedStylesNames> {
|
|
19
|
+
/**
|
|
20
|
+
* Label to contextualize the date
|
|
21
|
+
*
|
|
22
|
+
* @default "Last update:"
|
|
23
|
+
*/
|
|
24
|
+
label?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const TableLastUpdated: FunctionComponent<TableLastUpdatedProps & {dependencies?: never}> = ({
|
|
28
|
+
label = 'Last update:',
|
|
29
|
+
dependencies,
|
|
30
|
+
classNames,
|
|
31
|
+
styles,
|
|
32
|
+
unstyled,
|
|
33
|
+
...others
|
|
34
|
+
}) => {
|
|
35
|
+
const {classes} = useStyles(null, {name: 'TableLastUpdated', classNames, styles, unstyled});
|
|
36
|
+
const {state} = useTable();
|
|
37
|
+
const [time, setTime] = useState(new Date());
|
|
38
|
+
|
|
39
|
+
useDidUpdate(() => {
|
|
40
|
+
setTime(new Date());
|
|
41
|
+
}, [state, ...dependencies]);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Group className={classes.root} px="xl" position="right">
|
|
45
|
+
<Text size="xs" className={classes.label} {...others}>
|
|
46
|
+
{label}
|
|
47
|
+
<span role="timer">{dayjs(time).format('h:mm:ss A')}</span>
|
|
48
|
+
</Text>
|
|
49
|
+
</Group>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -32,14 +32,14 @@ export const TablePerPage: FunctionComponent<TablePerPageProps> & {DEFAULT_SIZE:
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
|
-
<Group>
|
|
36
|
-
<Text>{label}</Text>
|
|
35
|
+
<Group spacing="sm">
|
|
36
|
+
<Text fw={500}>{label}</Text>
|
|
37
37
|
<SegmentedControl
|
|
38
38
|
value={state.pagination.pageSize.toString() ?? values?.[1].toString()}
|
|
39
39
|
onChange={updatePerPage}
|
|
40
40
|
data={values.map((value) => value.toString())}
|
|
41
41
|
color="action"
|
|
42
|
-
size="
|
|
42
|
+
size="sm"
|
|
43
43
|
/>
|
|
44
44
|
</Group>
|
|
45
45
|
);
|
|
@@ -1,9 +1,10 @@
|
|
|
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
|
|
|
4
|
+
import {useState} from 'react';
|
|
4
5
|
import {Table} from '../Table';
|
|
5
|
-
import {useTable} from '../TableContext';
|
|
6
6
|
import {TableLayout} from '../Table.types';
|
|
7
|
+
import {useTable} from '../TableContext';
|
|
7
8
|
|
|
8
9
|
type RowData = {id: string; firstName: string; lastName?: string};
|
|
9
10
|
|
|
@@ -141,6 +142,44 @@ describe('Table', () => {
|
|
|
141
142
|
expect(screen.getByRole('row', {name: 'patate king', selected: false})).toBeInTheDocument();
|
|
142
143
|
});
|
|
143
144
|
|
|
145
|
+
it('does not reset row selection when clicking within one of the specified additionalRootNodes, even if it is outside the table', async () => {
|
|
146
|
+
const user = userEvent.setup();
|
|
147
|
+
|
|
148
|
+
const Fixture = () => {
|
|
149
|
+
const [cousinNode, setCousinNode] = useState<HTMLDivElement>();
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<>
|
|
153
|
+
<div key="inside" ref={setCousinNode} data-testid="table-cousin">
|
|
154
|
+
clicking inside here does not clear rows selection
|
|
155
|
+
</div>
|
|
156
|
+
<div key="outside" data-testid="outside-element">
|
|
157
|
+
clicking inside here clears rows selection
|
|
158
|
+
</div>
|
|
159
|
+
<Table
|
|
160
|
+
getRowId={({id}) => id}
|
|
161
|
+
data={[
|
|
162
|
+
{id: '🆔-1', firstName: 'John', lastName: 'Doe'},
|
|
163
|
+
{id: '🆔-2', firstName: 'Jane', lastName: 'Doe'},
|
|
164
|
+
]}
|
|
165
|
+
columns={columns}
|
|
166
|
+
additionalRootNodes={[cousinNode]}
|
|
167
|
+
/>
|
|
168
|
+
</>
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
render(<Fixture />);
|
|
172
|
+
|
|
173
|
+
const row = screen.getByRole('row', {name: /John Doe/i, selected: false});
|
|
174
|
+
expect(row).toBeInTheDocument();
|
|
175
|
+
await user.click(row);
|
|
176
|
+
expect(screen.getByRole('row', {name: /John Doe/i, selected: true})).toBeInTheDocument();
|
|
177
|
+
await user.click(screen.getByTestId('table-cousin'));
|
|
178
|
+
expect(screen.getByRole('row', {name: /John Doe/i, selected: true})).toBeInTheDocument();
|
|
179
|
+
await user.click(screen.getByTestId('outside-element'));
|
|
180
|
+
expect(screen.getByRole('row', {name: /John Doe/i, selected: false})).toBeInTheDocument();
|
|
181
|
+
});
|
|
182
|
+
|
|
144
183
|
describe('with multiple layouts', () => {
|
|
145
184
|
const layouts: TableLayout[] = [
|
|
146
185
|
{
|
|
@@ -243,13 +282,13 @@ describe('Table', () => {
|
|
|
243
282
|
onRowSelectionChange={onRowSelectionChangeSpy}
|
|
244
283
|
/>
|
|
245
284
|
);
|
|
246
|
-
await user.click(screen.getByRole('row', {name: /jane doe/i}));
|
|
285
|
+
await user.click(within(screen.getByRole('row', {name: /jane doe/i})).getByRole('checkbox'));
|
|
247
286
|
expect(onRowSelectionChangeSpy).toHaveBeenCalledTimes(1);
|
|
248
287
|
expect(onRowSelectionChangeSpy).toHaveBeenCalledWith([{id: '🆔-2', firstName: 'Jane', lastName: 'Doe'}]);
|
|
249
288
|
|
|
250
289
|
onRowSelectionChangeSpy.mockClear();
|
|
251
290
|
|
|
252
|
-
await user.click(screen.getByRole('row', {name: /john smith/i}));
|
|
291
|
+
await user.click(within(screen.getByRole('row', {name: /john smith/i})).getByRole('checkbox'));
|
|
253
292
|
expect(onRowSelectionChangeSpy).toHaveBeenCalledTimes(1);
|
|
254
293
|
expect(onRowSelectionChangeSpy).toHaveBeenCalledWith([
|
|
255
294
|
{id: '🆔-2', firstName: 'Jane', lastName: 'Doe'},
|
|
@@ -284,7 +323,7 @@ describe('Table', () => {
|
|
|
284
323
|
|
|
285
324
|
expect(row).toBeInTheDocument();
|
|
286
325
|
|
|
287
|
-
await user.click(row);
|
|
326
|
+
await user.click(within(row).getByRole('checkbox'));
|
|
288
327
|
|
|
289
328
|
expect(screen.getByRole('row', {name: /patate king/i, selected: true})).toBeInTheDocument();
|
|
290
329
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ColumnDef, createColumnHelper} from '@tanstack/table-core';
|
|
2
|
-
import {render, screen, userEvent} from '@test-utils';
|
|
2
|
+
import {render, screen, userEvent, within} from '@test-utils';
|
|
3
3
|
|
|
4
4
|
import {Button} from '../../button';
|
|
5
5
|
import {Table} from '../Table';
|
|
@@ -57,6 +57,7 @@ describe('Table.Actions', () => {
|
|
|
57
57
|
const renderSpy = vi.fn().mockImplementation(() => <div />);
|
|
58
58
|
render(
|
|
59
59
|
<Table<RowData>
|
|
60
|
+
getRowId={(row) => row.name}
|
|
60
61
|
data={[{name: 'fruit'}, {name: 'vegetable'}, {name: 'bread'}]}
|
|
61
62
|
columns={columns}
|
|
62
63
|
multiRowSelectionEnabled
|
|
@@ -66,8 +67,8 @@ describe('Table.Actions', () => {
|
|
|
66
67
|
</Table.Header>
|
|
67
68
|
</Table>
|
|
68
69
|
);
|
|
69
|
-
await user.click(screen.getByRole('
|
|
70
|
-
await user.click(screen.getByRole('
|
|
70
|
+
await user.click(within(screen.getByRole('row', {name: /fruit/})).getByRole('checkbox'));
|
|
71
|
+
await user.click(within(screen.getByRole('row', {name: /vegetable/})).getByRole('checkbox'));
|
|
71
72
|
expect(renderSpy).toHaveBeenCalledWith([{name: 'fruit'}, {name: 'vegetable'}]);
|
|
72
73
|
});
|
|
73
74
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {ColumnDef, createColumnHelper} from '@tanstack/table-core';
|
|
2
|
-
import {render, screen, userEvent, waitFor} from '@test-utils';
|
|
3
|
-
import {act} from 'react-dom/test-utils';
|
|
2
|
+
import {render, screen, userEvent, waitFor, within} from '@test-utils';
|
|
4
3
|
|
|
5
4
|
import {Table} from '../Table';
|
|
6
5
|
|
|
@@ -20,62 +19,32 @@ const basicTableWithDateRangePicker = (
|
|
|
20
19
|
</Table>
|
|
21
20
|
);
|
|
22
21
|
|
|
23
|
-
// Since we're mocking the date and the animations are timer based we're mocking useReduceMotion to disable all the animations
|
|
24
|
-
// I tried wrapping the components in <MantineProvider theme={{components: {Transition: {defaultProps: {duration: 0}}}}}>
|
|
25
|
-
// but the animation was still happening. :(
|
|
26
|
-
vi.mock('@mantine/hooks', async () => {
|
|
27
|
-
const actual = await vi.importActual('@mantine/hooks');
|
|
28
|
-
return {
|
|
29
|
-
...actual,
|
|
30
|
-
useReduceMotion: () => true,
|
|
31
|
-
};
|
|
32
|
-
});
|
|
33
|
-
|
|
34
22
|
describe('Table.DateRangePicker', () => {
|
|
35
|
-
beforeEach(() => {
|
|
36
|
-
vi.useFakeTimers().setSystemTime(new Date(2022, 0, 15));
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
vi.useRealTimers();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
23
|
it('displays the initial dates', async () => {
|
|
44
24
|
render(basicTableWithDateRangePicker);
|
|
45
25
|
|
|
46
|
-
|
|
47
|
-
expect(screen.getByText('Jan 01, 2022 - Jan 07, 2022')).toBeVisible();
|
|
48
|
-
});
|
|
26
|
+
expect(screen.getByText('Jan 01, 2022 - Jan 07, 2022')).toBeVisible();
|
|
49
27
|
});
|
|
50
28
|
|
|
51
29
|
it('opens the dialog when clicking on the calendar button', async () => {
|
|
52
|
-
|
|
53
|
-
vi.useRealTimers();
|
|
54
|
-
const user = userEvent.setup({delay: null});
|
|
30
|
+
const user = userEvent.setup();
|
|
55
31
|
render(basicTableWithDateRangePicker);
|
|
56
32
|
|
|
57
|
-
await screen.
|
|
58
|
-
|
|
59
|
-
await user.click(screen.getByRole('button', {name: 'calendar'}));
|
|
60
|
-
});
|
|
61
|
-
expect(screen.queryByRole('dialog')).toBeVisible();
|
|
33
|
+
await user.click(screen.getByRole('button', {name: 'calendar'}));
|
|
34
|
+
expect(screen.getByRole('dialog', {name: 'calendar'})).toBeVisible();
|
|
62
35
|
});
|
|
63
36
|
|
|
64
37
|
it('closes the dialog when clicking back on the calendar button', async () => {
|
|
65
|
-
|
|
66
|
-
vi.useRealTimers();
|
|
67
|
-
const user = userEvent.setup({delay: null});
|
|
38
|
+
const user = userEvent.setup();
|
|
68
39
|
render(basicTableWithDateRangePicker);
|
|
69
40
|
|
|
70
|
-
await screen.
|
|
71
|
-
await
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
76
|
-
});
|
|
41
|
+
await user.click(screen.getByRole('button', {name: 'calendar'}));
|
|
42
|
+
await user.click(screen.getByRole('button', {name: 'calendar'}));
|
|
43
|
+
expect(screen.queryByRole('dialog', {name: 'calendar'})).not.toBeInTheDocument();
|
|
44
|
+
}, 10000);
|
|
77
45
|
|
|
78
46
|
it('displays the selected date range in the table', async () => {
|
|
47
|
+
vi.useFakeTimers().setSystemTime(new Date(2022, 0, 15));
|
|
79
48
|
const user = userEvent.setup({delay: null});
|
|
80
49
|
const onChange = vi.fn();
|
|
81
50
|
render(
|
|
@@ -85,35 +54,33 @@ describe('Table.DateRangePicker', () => {
|
|
|
85
54
|
onChange={onChange}
|
|
86
55
|
initialState={{dateRange: [new Date(2022, 0, 1), new Date(2022, 0, 7)]}}
|
|
87
56
|
>
|
|
88
|
-
<Table.Header>
|
|
57
|
+
<Table.Header data-testid="table-header">
|
|
89
58
|
<Table.DateRangePicker
|
|
90
59
|
presets={{preset: {label: 'Preset', range: [new Date(2022, 0, 8), new Date(2022, 0, 14)]}}}
|
|
91
60
|
/>
|
|
92
61
|
</Table.Header>
|
|
93
62
|
</Table>
|
|
94
63
|
);
|
|
64
|
+
const tableHeader = screen.getByTestId('table-header');
|
|
95
65
|
|
|
96
|
-
|
|
97
|
-
await
|
|
98
|
-
|
|
99
|
-
await user.click(screen.getByRole('button', {name: 'calendar'}));
|
|
66
|
+
expect(within(tableHeader).getByText('Jan 01, 2022 - Jan 07, 2022')).toBeInTheDocument();
|
|
67
|
+
await user.click(within(tableHeader).getByRole('button', {name: 'calendar'}));
|
|
100
68
|
|
|
101
|
-
await screen.findByRole('dialog');
|
|
69
|
+
const calendar = await screen.findByRole('dialog', {name: 'calendar'});
|
|
102
70
|
|
|
103
71
|
// select a preset
|
|
104
|
-
await user.click(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
})
|
|
108
|
-
);
|
|
109
|
-
await user.click(screen.getByRole('option', {name: 'Preset'}));
|
|
110
|
-
|
|
111
|
-
await user.click(screen.getByRole('button', {name: 'Apply'}));
|
|
112
|
-
vi.advanceTimersByTime(500);
|
|
72
|
+
await user.click(within(calendar).getByRole('searchbox', {name: 'Date range'}));
|
|
73
|
+
await user.click(within(calendar).getByRole('option', {name: 'Preset'}));
|
|
74
|
+
await user.click(within(calendar).getByRole('button', {name: 'Apply'}));
|
|
113
75
|
|
|
114
|
-
await waitFor(() =>
|
|
76
|
+
await waitFor(() => {
|
|
77
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
78
|
+
});
|
|
115
79
|
expect(onChange).toHaveBeenCalledWith(
|
|
116
80
|
expect.objectContaining({dateRange: [new Date(2022, 0, 8), new Date(2022, 0, 14)]})
|
|
117
81
|
);
|
|
118
|
-
|
|
82
|
+
expect(within(tableHeader).getByText('Jan 08, 2022 - Jan 14, 2022')).toBeInTheDocument();
|
|
83
|
+
|
|
84
|
+
vi.useRealTimers();
|
|
85
|
+
}, 20000);
|
|
119
86
|
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {ColumnDef, createColumnHelper} from '@tanstack/table-core';
|
|
2
|
+
import {render, screen, userEvent} from '@test-utils';
|
|
3
|
+
import {useState} from 'react';
|
|
4
|
+
|
|
5
|
+
import {Table} from '../Table';
|
|
6
|
+
|
|
7
|
+
type RowData = {name: string};
|
|
8
|
+
|
|
9
|
+
const columnHelper = createColumnHelper<RowData>();
|
|
10
|
+
const columns: Array<ColumnDef<RowData>> = [columnHelper.accessor('name', {enableSorting: false})];
|
|
11
|
+
|
|
12
|
+
describe('Table.LastUpdated', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.useFakeTimers().setSystemTime(new Date(2022, 0, 15, 13, 5, 50));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.useRealTimers();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('displays the label and time', () => {
|
|
22
|
+
const {rerender} = render(
|
|
23
|
+
<Table data={[{name: 'fruit'}]} columns={columns}>
|
|
24
|
+
<Table.LastUpdated />
|
|
25
|
+
</Table>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
expect(screen.getByText('Last update:')).toBeVisible();
|
|
29
|
+
expect(screen.getByRole('timer')).toHaveTextContent('1:05:50 PM');
|
|
30
|
+
|
|
31
|
+
rerender(
|
|
32
|
+
<Table data={[{name: 'fruit'}]} columns={columns}>
|
|
33
|
+
<Table.LastUpdated label="CUSTOM label:" />
|
|
34
|
+
</Table>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(screen.queryByText('Last update:')).not.toBeInTheDocument();
|
|
38
|
+
expect(screen.getByText('CUSTOM label:')).toBeVisible();
|
|
39
|
+
expect(screen.getByRole('timer')).toHaveTextContent('1:05:50 PM');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('updates the time when a dependency changes', async () => {
|
|
43
|
+
const user = userEvent.setup({delay: null});
|
|
44
|
+
|
|
45
|
+
// Using a fixture to have a button that will trigger a change of a dependency
|
|
46
|
+
const Fixture = () => {
|
|
47
|
+
const [isClicked, setIsClicked] = useState(false);
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<button onClick={() => setIsClicked(true)}>Click me</button>
|
|
51
|
+
<Table data={[{name: 'fruit'}]} columns={columns}>
|
|
52
|
+
<Table.LastUpdated dependencies={[isClicked]} />
|
|
53
|
+
</Table>
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
render(<Fixture />);
|
|
58
|
+
|
|
59
|
+
expect(screen.getByText('Last update:')).toBeVisible();
|
|
60
|
+
expect(screen.getByRole('timer')).toHaveTextContent('1:05:50 PM');
|
|
61
|
+
|
|
62
|
+
vi.setSystemTime(new Date(2022, 0, 15, 14, 11, 22));
|
|
63
|
+
|
|
64
|
+
// the date changed but the dependency didn't change yet, so the timer is still the same
|
|
65
|
+
expect(screen.getByRole('timer')).toHaveTextContent('1:05:50 PM');
|
|
66
|
+
|
|
67
|
+
// When we click on the button the isClicked switch from false to true which triggers an update
|
|
68
|
+
await user.click(screen.getByRole('button', {name: 'Click me'}));
|
|
69
|
+
expect(screen.getByRole('timer')).toHaveTextContent('2:11:22 PM');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('updates the time when the data changes', async () => {
|
|
73
|
+
const user = userEvent.setup({delay: null});
|
|
74
|
+
const Fixture = () => {
|
|
75
|
+
const [data, setData] = useState([{name: 'fruit'}]);
|
|
76
|
+
return (
|
|
77
|
+
<>
|
|
78
|
+
<button onClick={() => setData([{name: 'vegetable'}])}>Click me</button>
|
|
79
|
+
<Table data={data} columns={columns}>
|
|
80
|
+
<Table.LastUpdated />
|
|
81
|
+
</Table>
|
|
82
|
+
</>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
render(<Fixture />);
|
|
86
|
+
|
|
87
|
+
expect(screen.getByRole('timer')).toHaveTextContent('1:05:50 PM');
|
|
88
|
+
|
|
89
|
+
vi.setSystemTime(new Date(2022, 0, 15, 14, 11, 22));
|
|
90
|
+
|
|
91
|
+
expect(screen.getByRole('timer')).toHaveTextContent('1:05:50 PM');
|
|
92
|
+
|
|
93
|
+
await user.click(screen.getByRole('button', {name: 'Click me'}));
|
|
94
|
+
|
|
95
|
+
expect(screen.getByRole('timer')).toHaveTextContent('2:11:22 PM');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ColumnDef, createColumnHelper} from '@tanstack/table-core';
|
|
2
|
-
import {
|
|
2
|
+
import {render, screen, userEvent, waitFor} from '@test-utils';
|
|
3
3
|
|
|
4
4
|
import {Table} from '../Table';
|
|
5
5
|
|
|
@@ -9,40 +9,6 @@ const columnHelper = createColumnHelper<RowData>();
|
|
|
9
9
|
const columns: Array<ColumnDef<RowData>> = [columnHelper.accessor('name', {enableSorting: false})];
|
|
10
10
|
|
|
11
11
|
describe('Table.Predicate', () => {
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
vi.useFakeTimers();
|
|
14
|
-
});
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
vi.useRealTimers();
|
|
17
|
-
});
|
|
18
|
-
it('displays the intial value', async () => {
|
|
19
|
-
render(
|
|
20
|
-
<Table
|
|
21
|
-
data={[{name: 'fruit'}, {name: 'vegetable'}]}
|
|
22
|
-
columns={columns}
|
|
23
|
-
initialState={{predicates: {rank: 'second'}}}
|
|
24
|
-
>
|
|
25
|
-
<Table.Header>
|
|
26
|
-
<Table.Predicate
|
|
27
|
-
id="rank"
|
|
28
|
-
data={[
|
|
29
|
-
{value: 'first', label: 'First'},
|
|
30
|
-
{value: 'second', label: 'Second'},
|
|
31
|
-
]}
|
|
32
|
-
/>
|
|
33
|
-
</Table.Header>
|
|
34
|
-
</Table>
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
await waitFor(() => {
|
|
38
|
-
expect(
|
|
39
|
-
screen.getByRole('searchbox', {
|
|
40
|
-
name: 'rank',
|
|
41
|
-
})
|
|
42
|
-
).toHaveValue('Second');
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
12
|
it('calls onMount with the initial value', async () => {
|
|
47
13
|
const onMount = vi.fn();
|
|
48
14
|
render(
|
|
@@ -65,26 +31,17 @@ describe('Table.Predicate', () => {
|
|
|
65
31
|
);
|
|
66
32
|
|
|
67
33
|
await waitFor(() => {
|
|
68
|
-
expect(
|
|
69
|
-
screen.getByRole('searchbox', {
|
|
70
|
-
name: 'rank',
|
|
71
|
-
})
|
|
72
|
-
).toHaveValue('Second');
|
|
34
|
+
expect(screen.getByRole('searchbox', {name: 'rank'})).toHaveValue('Second');
|
|
73
35
|
});
|
|
74
36
|
expect(onMount).toHaveBeenCalledWith(expect.objectContaining({predicates: {rank: 'second'}}));
|
|
75
37
|
});
|
|
76
38
|
|
|
77
39
|
it('calls onChange when changing the predicate', async () => {
|
|
78
|
-
const user = userEvent.setup(
|
|
40
|
+
const user = userEvent.setup();
|
|
79
41
|
const onChange = vi.fn();
|
|
80
42
|
render(
|
|
81
|
-
<Table
|
|
82
|
-
data=
|
|
83
|
-
columns={columns}
|
|
84
|
-
onChange={onChange}
|
|
85
|
-
initialState={{predicates: {rank: 'second'}}}
|
|
86
|
-
>
|
|
87
|
-
<Table.Header>
|
|
43
|
+
<Table data={[{name: 'fruit'}, {name: 'vegetable'}]} columns={columns} onChange={onChange}>
|
|
44
|
+
<Table.Header data-testid="table-header">
|
|
88
45
|
<Table.Predicate
|
|
89
46
|
id="rank"
|
|
90
47
|
data={[
|
|
@@ -96,18 +53,13 @@ describe('Table.Predicate', () => {
|
|
|
96
53
|
</Table>
|
|
97
54
|
);
|
|
98
55
|
|
|
99
|
-
expect(screen.getByRole('searchbox', {name: 'rank'})).toHaveValue('Second');
|
|
100
|
-
|
|
101
56
|
await user.click(screen.getByRole('searchbox', {name: 'rank'}));
|
|
102
|
-
|
|
103
57
|
await user.click(screen.getByRole('option', {name: 'First'}));
|
|
104
58
|
|
|
105
59
|
expect(screen.getByRole('searchbox', {name: 'rank'})).toHaveValue('First');
|
|
106
|
-
|
|
107
|
-
|
|
60
|
+
await waitFor(() => {
|
|
61
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
108
62
|
});
|
|
109
|
-
|
|
110
|
-
expect(onChange).toHaveBeenCalledTimes(1);
|
|
111
63
|
expect(onChange).toHaveBeenCalledWith(expect.objectContaining({predicates: {rank: 'first'}}));
|
|
112
64
|
});
|
|
113
65
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {ListSize16Px} from '@coveord/plasma-react-icons';
|
|
2
|
-
import {Box, Collapse, createStyles} from '@mantine/core';
|
|
2
|
+
import {Box, Collapse, createStyles, rem} from '@mantine/core';
|
|
3
3
|
import {flexRender} from '@tanstack/react-table';
|
|
4
4
|
import {defaultColumnSizing} from '@tanstack/table-core';
|
|
5
|
-
import {Fragment} from 'react';
|
|
5
|
+
import {Fragment, type MouseEvent} from 'react';
|
|
6
6
|
import {TableLayout, TableLayoutProps} from '../Table.types';
|
|
7
7
|
import {TableCollapsibleColumn} from '../TableCollapsibleColumn';
|
|
8
8
|
import {useTable} from '../TableContext';
|
|
9
9
|
import {TableLoading} from '../TableLoading';
|
|
10
|
+
import {TableSelectableColumn} from '../TableSelectableColumn';
|
|
10
11
|
import {Th} from '../Th';
|
|
11
12
|
|
|
12
13
|
interface TableStylesParams {
|
|
@@ -16,13 +17,12 @@ interface TableStylesParams {
|
|
|
16
17
|
|
|
17
18
|
const useStyles = createStyles<string, TableStylesParams>((theme, {multiRowSelectionEnabled, disableRowSelection}) => {
|
|
18
19
|
const rowBackgroundColor =
|
|
19
|
-
theme.colorScheme === 'dark'
|
|
20
|
-
|
|
21
|
-
: theme.colors[theme.primaryColor][0];
|
|
20
|
+
theme.colorScheme === 'dark' ? theme.fn.rgba(theme.colors[theme.primaryColor][7], 0.2) : theme.colors.gray[1];
|
|
21
|
+
const border = `${rem(1)} solid ${theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[3]}`;
|
|
22
22
|
return {
|
|
23
23
|
headerColumns: {
|
|
24
24
|
'& th:first-of-type > *': {
|
|
25
|
-
paddingLeft:
|
|
25
|
+
paddingLeft: '40px',
|
|
26
26
|
},
|
|
27
27
|
|
|
28
28
|
'& input[type=checkbox]': {
|
|
@@ -60,10 +60,26 @@ const useStyles = createStyles<string, TableStylesParams>((theme, {multiRowSelec
|
|
|
60
60
|
},
|
|
61
61
|
|
|
62
62
|
row: {
|
|
63
|
+
'& td:first-of-type': {
|
|
64
|
+
paddingLeft: '40px',
|
|
65
|
+
},
|
|
63
66
|
'&:hover': {
|
|
64
67
|
backgroundColor: rowBackgroundColor,
|
|
65
68
|
},
|
|
66
69
|
},
|
|
70
|
+
|
|
71
|
+
cell: {
|
|
72
|
+
verticalAlign: 'middle',
|
|
73
|
+
// We must use height instead of minHeight here, otherwise it doesn't apply
|
|
74
|
+
height: '56px',
|
|
75
|
+
padding: `${theme.spacing.xs} ${theme.spacing.sm}`,
|
|
76
|
+
borderBottom: border,
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
collapsible: {
|
|
80
|
+
backgroundColor: rowBackgroundColor,
|
|
81
|
+
borderBottom: border,
|
|
82
|
+
},
|
|
67
83
|
};
|
|
68
84
|
});
|
|
69
85
|
|
|
@@ -84,14 +100,27 @@ const RowLayoutBody = <T,>({table, doubleClickAction, getExpandChildren, loading
|
|
|
84
100
|
const {multiRowSelectionEnabled, disableRowSelection} = useTable();
|
|
85
101
|
const {classes, cx} = useStyles({disableRowSelection, multiRowSelectionEnabled});
|
|
86
102
|
|
|
103
|
+
const toggleCollapsible = (el: HTMLTableRowElement) => {
|
|
104
|
+
const cell = el.children[el.children.length - 1] as HTMLTableCellElement;
|
|
105
|
+
cell.querySelector('button').click();
|
|
106
|
+
};
|
|
107
|
+
|
|
87
108
|
const rows = table.getRowModel().rows.map((row) => {
|
|
88
109
|
const rowChildren = getExpandChildren?.(row.original) ?? null;
|
|
89
110
|
const isSelected = !!row.getIsSelected();
|
|
111
|
+
const onClick = (event: MouseEvent<HTMLTableRowElement>) => {
|
|
112
|
+
if (rowChildren) {
|
|
113
|
+
toggleCollapsible(event.currentTarget);
|
|
114
|
+
}
|
|
115
|
+
if (!disableRowSelection && !multiRowSelectionEnabled) {
|
|
116
|
+
row.toggleSelected();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
90
119
|
|
|
91
120
|
return (
|
|
92
121
|
<Fragment key={row.id}>
|
|
93
122
|
<tr
|
|
94
|
-
onClick={
|
|
123
|
+
onClick={onClick}
|
|
95
124
|
onDoubleClick={() => doubleClickAction?.(row.original)}
|
|
96
125
|
className={cx(classes.row, {
|
|
97
126
|
[classes.rowSelected]: isSelected,
|
|
@@ -102,13 +131,20 @@ const RowLayoutBody = <T,>({table, doubleClickAction, getExpandChildren, loading
|
|
|
102
131
|
{row.getVisibleCells().map((cell) => {
|
|
103
132
|
const size = cell.column.getSize();
|
|
104
133
|
const width = size !== defaultColumnSizing.size ? size : undefined;
|
|
134
|
+
const onCollapsibleCellClick = (event: MouseEvent<HTMLTableCellElement>) => {
|
|
135
|
+
if (cell.column.id === TableSelectableColumn.id && !disableRowSelection) {
|
|
136
|
+
event.stopPropagation();
|
|
137
|
+
row.getToggleSelectedHandler();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
105
140
|
return (
|
|
106
141
|
<td
|
|
107
142
|
key={cell.id}
|
|
108
143
|
style={{width}}
|
|
109
|
-
className={cx({
|
|
144
|
+
className={cx(classes.cell, {
|
|
110
145
|
[classes.rowCollapsibleButtonCell]: cell.column.id === TableCollapsibleColumn.id,
|
|
111
146
|
})}
|
|
147
|
+
onClick={onCollapsibleCellClick}
|
|
112
148
|
>
|
|
113
149
|
<TableLoading visible={loading}>
|
|
114
150
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
@@ -123,12 +159,10 @@ const RowLayoutBody = <T,>({table, doubleClickAction, getExpandChildren, loading
|
|
|
123
159
|
colSpan={table.getAllColumns().length}
|
|
124
160
|
style={{
|
|
125
161
|
padding: 0,
|
|
126
|
-
borderTop: row.getIsExpanded() ? undefined : 'none',
|
|
127
|
-
borderBottom: row.getIsExpanded() ? undefined : 'none',
|
|
128
162
|
}}
|
|
129
163
|
>
|
|
130
164
|
<Collapse in={row.getIsExpanded()}>
|
|
131
|
-
<Box px="sm" py="xs">
|
|
165
|
+
<Box className={classes.collapsible} px="sm" py="xs">
|
|
132
166
|
{rowChildren}
|
|
133
167
|
</Box>
|
|
134
168
|
</Collapse>
|