@coveord/plasma-mantine 52.9.0 → 52.10.1

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.
Files changed (112) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/.turbo/turbo-test.log +33 -32
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/cjs/components/code-editor/CodeEditor.d.ts.map +1 -1
  5. package/dist/cjs/components/code-editor/CodeEditor.js +8 -3
  6. package/dist/cjs/components/code-editor/CodeEditor.js.map +1 -1
  7. package/dist/cjs/components/collection/Collection.js +5 -3
  8. package/dist/cjs/components/collection/Collection.js.map +1 -1
  9. package/dist/cjs/components/date-range-picker/DateRangePickerPresetSelect.d.ts.map +1 -1
  10. package/dist/cjs/components/date-range-picker/DateRangePickerPresetSelect.js +2 -1
  11. package/dist/cjs/components/date-range-picker/DateRangePickerPresetSelect.js.map +1 -1
  12. package/dist/cjs/components/date-range-picker/EditableDateRangePicker.js +2 -2
  13. package/dist/cjs/components/date-range-picker/EditableDateRangePicker.js.map +1 -1
  14. package/dist/cjs/components/modal-wizard/ModalWizard.js +2 -2
  15. package/dist/cjs/components/modal-wizard/ModalWizard.js.map +1 -1
  16. package/dist/cjs/components/table/Table.d.ts.map +1 -1
  17. package/dist/cjs/components/table/Table.js +25 -13
  18. package/dist/cjs/components/table/Table.js.map +1 -1
  19. package/dist/cjs/components/table/Table.styles.d.ts.map +1 -1
  20. package/dist/cjs/components/table/Table.styles.js +1 -10
  21. package/dist/cjs/components/table/Table.styles.js.map +1 -1
  22. package/dist/cjs/components/table/Table.types.d.ts +14 -3
  23. package/dist/cjs/components/table/Table.types.d.ts.map +1 -1
  24. package/dist/cjs/components/table/TableFooter.js +2 -2
  25. package/dist/cjs/components/table/TableFooter.js.map +1 -1
  26. package/dist/cjs/components/table/TableHeader.js +1 -1
  27. package/dist/cjs/components/table/TableHeader.js.map +1 -1
  28. package/dist/cjs/components/table/TableLastUpdated.d.ts +24 -0
  29. package/dist/cjs/components/table/TableLastUpdated.d.ts.map +1 -0
  30. package/dist/cjs/components/table/TableLastUpdated.js +73 -0
  31. package/dist/cjs/components/table/TableLastUpdated.js.map +1 -0
  32. package/dist/cjs/components/table/TablePagination.d.ts.map +1 -1
  33. package/dist/cjs/components/table/TablePagination.js +1 -0
  34. package/dist/cjs/components/table/TablePagination.js.map +1 -1
  35. package/dist/cjs/components/table/TablePerPage.js +5 -3
  36. package/dist/cjs/components/table/TablePerPage.js.map +1 -1
  37. package/dist/cjs/components/table/layouts/RowLayout.d.ts.map +1 -1
  38. package/dist/cjs/components/table/layouts/RowLayout.js +41 -9
  39. package/dist/cjs/components/table/layouts/RowLayout.js.map +1 -1
  40. package/dist/cjs/components/table/useRowSelection.d.ts +2 -2
  41. package/dist/cjs/components/table/useRowSelection.d.ts.map +1 -1
  42. package/dist/cjs/components/table/useRowSelection.js +8 -3
  43. package/dist/cjs/components/table/useRowSelection.js.map +1 -1
  44. package/dist/cjs/theme/Theme.js +1 -1
  45. package/dist/cjs/theme/Theme.js.map +1 -1
  46. package/dist/esm/components/code-editor/CodeEditor.d.ts.map +1 -1
  47. package/dist/esm/components/code-editor/CodeEditor.js +8 -3
  48. package/dist/esm/components/code-editor/CodeEditor.js.map +1 -1
  49. package/dist/esm/components/collection/Collection.js +5 -3
  50. package/dist/esm/components/collection/Collection.js.map +1 -1
  51. package/dist/esm/components/date-range-picker/DateRangePickerPresetSelect.d.ts.map +1 -1
  52. package/dist/esm/components/date-range-picker/DateRangePickerPresetSelect.js +2 -1
  53. package/dist/esm/components/date-range-picker/DateRangePickerPresetSelect.js.map +1 -1
  54. package/dist/esm/components/date-range-picker/EditableDateRangePicker.js +2 -2
  55. package/dist/esm/components/date-range-picker/EditableDateRangePicker.js.map +1 -1
  56. package/dist/esm/components/modal-wizard/ModalWizard.js +2 -2
  57. package/dist/esm/components/modal-wizard/ModalWizard.js.map +1 -1
  58. package/dist/esm/components/table/Table.d.ts.map +1 -1
  59. package/dist/esm/components/table/Table.js +27 -15
  60. package/dist/esm/components/table/Table.js.map +1 -1
  61. package/dist/esm/components/table/Table.styles.d.ts.map +1 -1
  62. package/dist/esm/components/table/Table.styles.js +1 -10
  63. package/dist/esm/components/table/Table.styles.js.map +1 -1
  64. package/dist/esm/components/table/Table.types.d.ts +14 -3
  65. package/dist/esm/components/table/Table.types.d.ts.map +1 -1
  66. package/dist/esm/components/table/Table.types.js.map +1 -1
  67. package/dist/esm/components/table/TableFooter.js +2 -2
  68. package/dist/esm/components/table/TableFooter.js.map +1 -1
  69. package/dist/esm/components/table/TableHeader.js +1 -1
  70. package/dist/esm/components/table/TableHeader.js.map +1 -1
  71. package/dist/esm/components/table/TableLastUpdated.d.ts +24 -0
  72. package/dist/esm/components/table/TableLastUpdated.d.ts.map +1 -0
  73. package/dist/esm/components/table/TableLastUpdated.js +62 -0
  74. package/dist/esm/components/table/TableLastUpdated.js.map +1 -0
  75. package/dist/esm/components/table/TablePagination.d.ts.map +1 -1
  76. package/dist/esm/components/table/TablePagination.js +1 -0
  77. package/dist/esm/components/table/TablePagination.js.map +1 -1
  78. package/dist/esm/components/table/TablePerPage.js +5 -3
  79. package/dist/esm/components/table/TablePerPage.js.map +1 -1
  80. package/dist/esm/components/table/layouts/RowLayout.d.ts.map +1 -1
  81. package/dist/esm/components/table/layouts/RowLayout.js +42 -10
  82. package/dist/esm/components/table/layouts/RowLayout.js.map +1 -1
  83. package/dist/esm/components/table/useRowSelection.d.ts +2 -2
  84. package/dist/esm/components/table/useRowSelection.d.ts.map +1 -1
  85. package/dist/esm/components/table/useRowSelection.js +8 -3
  86. package/dist/esm/components/table/useRowSelection.js.map +1 -1
  87. package/dist/esm/theme/Theme.js +1 -1
  88. package/dist/esm/theme/Theme.js.map +1 -1
  89. package/package.json +17 -17
  90. package/src/__tests__/VitestSetup.ts +12 -0
  91. package/src/components/code-editor/CodeEditor.tsx +5 -3
  92. package/src/components/code-editor/__tests__/CodeEditor.spec.tsx +1 -0
  93. package/src/components/date-range-picker/DateRangePickerPresetSelect.tsx +1 -0
  94. package/src/components/date-range-picker/__tests__/DateRangePickerInlineCalendar.spec.tsx +2 -0
  95. package/src/components/date-range-picker/__tests__/DateRangePickerPopoverCalendar.spec.tsx +4 -19
  96. package/src/components/date-range-picker/__tests__/EditableDateRangePicker.spec.tsx +3 -3
  97. package/src/components/modal-wizard/__tests__/ModalWizard.spec.tsx +19 -4
  98. package/src/components/table/Table.styles.ts +0 -9
  99. package/src/components/table/Table.tsx +22 -13
  100. package/src/components/table/Table.types.ts +14 -3
  101. package/src/components/table/TableFooter.tsx +1 -1
  102. package/src/components/table/TableHeader.tsx +1 -1
  103. package/src/components/table/TableLastUpdated.tsx +51 -0
  104. package/src/components/table/TablePagination.tsx +1 -0
  105. package/src/components/table/TablePerPage.tsx +3 -3
  106. package/src/components/table/__tests__/Table.spec.tsx +44 -5
  107. package/src/components/table/__tests__/TableActions.spec.tsx +4 -3
  108. package/src/components/table/__tests__/TableDateRangePicker.spec.tsx +26 -59
  109. package/src/components/table/__tests__/TableLastUpdated.spec.tsx +97 -0
  110. package/src/components/table/__tests__/TablePredicate.spec.tsx +7 -55
  111. package/src/components/table/layouts/RowLayout.tsx +45 -11
  112. package/src/components/table/useRowSelection.ts +13 -6
@@ -1,8 +1,8 @@
1
- import {DateRangePickerValue} from '@mantine/dates';
2
1
  import {render, screen, userEvent} from '@test-utils';
3
2
  import {useState} from 'react';
4
3
 
5
4
  import {EditableDateRangePicker} from '../EditableDateRangePicker';
5
+ import {DateRangePickerValue} from '../DateRangePickerInlineCalendar';
6
6
 
7
7
  describe('EditableDateRangePicker', () => {
8
8
  it('renders an input for the start and an input for the end', () => {
@@ -19,7 +19,7 @@ describe('EditableDateRangePicker', () => {
19
19
  });
20
20
 
21
21
  it('updates when editing values', async () => {
22
- const user = userEvent.setup({delay: null});
22
+ const user = userEvent.setup();
23
23
  const Fixture = () => {
24
24
  const [value, setValue] = useState<DateRangePickerValue>([null, null]);
25
25
  return (
@@ -44,5 +44,5 @@ describe('EditableDateRangePicker', () => {
44
44
  await user.type(endInput, 'Jan 14, 2022');
45
45
 
46
46
  expect(screen.getByTestId('json')).toHaveTextContent('["2022-01-08T00:00:00.000Z","2022-01-14T23:59:59.999Z"]');
47
- });
47
+ }, 10000);
48
48
  });
@@ -8,24 +8,28 @@ describe('ModalWizard', () => {
8
8
 
9
9
  const modelSteps = [
10
10
  {
11
+ id: 'step1',
11
12
  docLink: 'https://google.com',
12
13
  title: (currentStep: string) => 'Current Step is: ' + currentStep,
13
14
  validateStep: () => ({isValid: true}),
14
15
  element: <div> Slide 1</div>,
15
16
  },
16
17
  {
18
+ id: 'step2',
17
19
  docLink: 'https://google.com',
18
20
  title: (currentStep: string) => 'Current Step is: ' + currentStep,
19
21
  validateStep: () => ({isValid: true}),
20
22
  element: <div> Slide 2</div>,
21
23
  },
22
24
  {
25
+ id: 'step3',
23
26
  docLink: 'https://google.com',
24
27
  title: (currentStep: string) => 'Current Step is: ' + currentStep,
25
28
  validateStep: () => ({isValid: false}),
26
29
  element: <div> Slide 3</div>,
27
30
  },
28
31
  {
32
+ id: 'step4',
29
33
  docLink: 'https://google.com',
30
34
  title: (currentStep: string) => 'Current Step is: ' + currentStep,
31
35
  validateStep: () => ({isValid: false}),
@@ -46,13 +50,14 @@ describe('ModalWizard', () => {
46
50
  onNext={onNextSpy}
47
51
  onPrevious={onPreviousSpy}
48
52
  >
49
- {modelSteps.map((model_item) => (
53
+ {modelSteps.map((step) => (
50
54
  <ModalWizard.Step
51
- docLink={model_item.docLink}
55
+ key={step.id}
56
+ docLink={step.docLink}
52
57
  title={(currentStep) => 'Current Step is: ' + currentStep}
53
- validateStep={model_item.validateStep}
58
+ validateStep={step.validateStep}
54
59
  >
55
- {model_item.element}
60
+ {step.element}
56
61
  </ModalWizard.Step>
57
62
  ))}
58
63
  </ModalWizard>
@@ -165,6 +170,7 @@ describe('ModalWizard', () => {
165
170
 
166
171
  const modelSteps = [
167
172
  {
173
+ id: 'step1',
168
174
  docLink: 'https://google.com',
169
175
  title: (currentStep: string) => 'Current Step is: ' + currentStep,
170
176
  validateStep: () => ({isValid: true}),
@@ -179,6 +185,7 @@ describe('ModalWizard', () => {
179
185
  <ModalWizard isDirty={isDirty} onClose={onClose} opened={true}>
180
186
  {modelSteps.map((model_item) => (
181
187
  <ModalWizard.Step
188
+ key={model_item.id}
182
189
  docLink={model_item.docLink}
183
190
  title={(currentStep) => 'Current Step is: ' + currentStep}
184
191
  validateStep={model_item.validateStep}
@@ -201,6 +208,7 @@ describe('ModalWizard', () => {
201
208
  const user = userEvent.setup();
202
209
  const modelSteps = [
203
210
  {
211
+ id: 'step1',
204
212
  docLink: 'https://google.com',
205
213
  title: (currentStep: string) => 'Current Step is: ' + currentStep,
206
214
  validateStep: () => ({isValid: true}),
@@ -215,6 +223,7 @@ describe('ModalWizard', () => {
215
223
  <ModalWizard isDirty={isDirty} onClose={onClose} opened={true}>
216
224
  {modelSteps.map((model_item) => (
217
225
  <ModalWizard.Step
226
+ key={model_item.id}
218
227
  docLink={model_item.docLink}
219
228
  title={(currentStep) => 'Current Step is: ' + currentStep}
220
229
  validateStep={model_item.validateStep}
@@ -237,6 +246,7 @@ describe('ModalWizard', () => {
237
246
  const user = userEvent.setup();
238
247
  const modelSteps = [
239
248
  {
249
+ id: 'step1',
240
250
  docLink: 'https://google.com',
241
251
  title: (currentStep: string) => 'Current Step is: ' + currentStep,
242
252
  validateStep: () => ({isValid: true}),
@@ -252,6 +262,7 @@ describe('ModalWizard', () => {
252
262
  <ModalWizard isDirty={isDirty} onClose={onClose} onFinish={onFinish} opened={true}>
253
263
  {modelSteps.map((model_item) => (
254
264
  <ModalWizard.Step
265
+ key={model_item.id}
255
266
  docLink={model_item.docLink}
256
267
  title={(currentStep) => 'Current Step is: ' + currentStep}
257
268
  validateStep={model_item.validateStep}
@@ -274,6 +285,7 @@ describe('ModalWizard', () => {
274
285
  const user = userEvent.setup();
275
286
  const modelSteps = [
276
287
  {
288
+ id: 'step1',
277
289
  docLink: 'https://google.com',
278
290
  title: (currentStep: string) => 'Current Step is: ' + currentStep,
279
291
  validateStep: () => ({isValid: true}),
@@ -289,6 +301,7 @@ describe('ModalWizard', () => {
289
301
  <ModalWizard isDirty={isDirty} onClose={onClose} handleDirtyState={handleDirtyState} opened={true}>
290
302
  {modelSteps.map((model_item) => (
291
303
  <ModalWizard.Step
304
+ key={model_item.id}
292
305
  docLink={model_item.docLink}
293
306
  title={(currentStep) => 'Current Step is: ' + currentStep}
294
307
  validateStep={model_item.validateStep}
@@ -313,6 +326,7 @@ describe('ModalWizard', () => {
313
326
  const user = userEvent.setup();
314
327
  const modelSteps = [
315
328
  {
329
+ id: 'step1',
316
330
  docLink: 'https://google.com',
317
331
  title: (currentStep: string) => 'Current Step is: ' + currentStep,
318
332
  validateStep: () => ({isValid: true}),
@@ -328,6 +342,7 @@ describe('ModalWizard', () => {
328
342
  <ModalWizard isDirty={isDirty} onClose={onClose} handleDirtyState={handleDirtyState} opened={true}>
329
343
  {modelSteps.map((model_item) => (
330
344
  <ModalWizard.Step
345
+ key={model_item.id}
331
346
  docLink={model_item.docLink}
332
347
  title={(currentStep) => 'Current Step is: ' + currentStep}
333
348
  validateStep={model_item.validateStep}
@@ -3,15 +3,6 @@ import {createStyles} from '@mantine/core';
3
3
  const useStyles = createStyles<string>((theme) => ({
4
4
  table: {
5
5
  width: '100%',
6
- '& thead tr th': {
7
- borderBottom: 'none',
8
- },
9
- '& td:first-of-type': {
10
- paddingLeft: theme.spacing.xl,
11
- },
12
- '& tbody td': {
13
- verticalAlign: 'top',
14
- },
15
6
  },
16
7
 
17
8
  header: {
@@ -1,11 +1,12 @@
1
- import {Box, Center, Loader, Table as MantineTable} from '@mantine/core';
1
+ import {Box, Center, Loader} from '@mantine/core';
2
2
  import {useForm} from '@mantine/form';
3
3
  import {useDidUpdate} from '@mantine/hooks';
4
- import {ColumnDef, Row, TableState as TanstackTableState, getCoreRowModel, useReactTable} from '@tanstack/react-table';
4
+ import {ColumnDef, getCoreRowModel, Row, TableState as TanstackTableState, useReactTable} from '@tanstack/react-table';
5
5
  import debounce from 'lodash.debounce';
6
6
  import defaultsDeep from 'lodash.defaultsdeep';
7
- import {Children, Dispatch, ReactElement, useCallback, useEffect, useState} from 'react';
7
+ import {Children, cloneElement, Dispatch, ReactElement, useCallback, useEffect, useState} from 'react';
8
8
 
9
+ import {TableLayouts} from './layouts/TableLayouts';
9
10
  import useStyles from './Table.styles';
10
11
  import {TableFormType, TableProps, TableState, TableType} from './Table.types';
11
12
  import {TableActions} from './TableActions';
@@ -16,13 +17,13 @@ import {TableDateRangePicker} from './TableDateRangePicker';
16
17
  import {TableFilter} from './TableFilter';
17
18
  import {TableFooter} from './TableFooter';
18
19
  import {TableHeader} from './TableHeader';
20
+ import {TableLastUpdated} from './TableLastUpdated';
21
+ import {TableLoading} from './TableLoading';
19
22
  import {TablePagination} from './TablePagination';
20
23
  import {TablePerPage} from './TablePerPage';
21
24
  import {TablePredicate} from './TablePredicate';
22
25
  import {TableSelectableColumn} from './TableSelectableColumn';
23
26
  import {useRowSelection} from './useRowSelection';
24
- import {TableLoading} from './TableLoading';
25
- import {TableLayouts} from './layouts/TableLayouts';
26
27
 
27
28
  export const Table: TableType = <T,>({
28
29
  data,
@@ -40,12 +41,14 @@ export const Table: TableType = <T,>({
40
41
  multiRowSelectionEnabled,
41
42
  disableRowSelection,
42
43
  onRowSelectionChange,
44
+ additionalRootNodes,
43
45
  options = {},
44
46
  }: TableProps<T>) => {
45
47
  const convertedChildren = Children.toArray(children) as ReactElement[];
46
48
  const header = convertedChildren.find((child) => child.type === TableHeader);
47
49
  const footer = convertedChildren.find((child) => child.type === TableFooter);
48
50
  const consumer = convertedChildren.find((child) => child.type === TableConsumer);
51
+ const lastUpdated = convertedChildren.find((child) => child.type === TableLastUpdated);
49
52
 
50
53
  const {predicates, dateRange, ...initialStateWithoutForm} = initialState;
51
54
  const form = useForm<TableFormType>({
@@ -58,7 +61,10 @@ export const Table: TableType = <T,>({
58
61
  const {classes} = useStyles();
59
62
 
60
63
  const table = useReactTable({
61
- initialState: defaultsDeep(initialStateWithoutForm, {pagination: {pageSize: TablePerPage.DEFAULT_SIZE}}),
64
+ initialState: defaultsDeep(initialStateWithoutForm, {
65
+ pagination: {pageSize: TablePerPage.DEFAULT_SIZE},
66
+ globalFilter: '',
67
+ }),
62
68
  data,
63
69
  columns: multiRowSelectionEnabled ? [TableSelectableColumn as ColumnDef<T>].concat(columns) : columns,
64
70
  getCoreRowModel: getCoreRowModel(),
@@ -79,6 +85,7 @@ export const Table: TableType = <T,>({
79
85
  const {clearSelection, getSelectedRow, getSelectedRows, outsideClickRef} = useRowSelection(table, {
80
86
  multiRowSelectionEnabled,
81
87
  onRowSelectionChange,
88
+ additionalRootNodes,
82
89
  });
83
90
  const isFiltered =
84
91
  !!state.globalFilter ||
@@ -149,15 +156,11 @@ export const Table: TableType = <T,>({
149
156
  noDataChildren
150
157
  ) : (
151
158
  <>
152
- <MantineTable className={classes.table} horizontalSpacing="sm" verticalSpacing="xs" pb="sm">
159
+ <Box component="table" className={classes.table} pb="sm">
153
160
  <thead className={classes.header}>
154
161
  {!!header ? (
155
162
  <tr>
156
- <th
157
- // need to use inline style because Mantine define style on `.mantine-{id} thead tr th`
158
- style={{padding: 0, fontWeight: 'unset'}}
159
- colSpan={table.getAllColumns().length}
160
- >
163
+ <th style={{padding: 0}} colSpan={table.getAllColumns().length}>
161
164
  {header}
162
165
  </th>
163
166
  </tr>
@@ -185,8 +188,13 @@ export const Table: TableType = <T,>({
185
188
  </tr>
186
189
  )}
187
190
  </tbody>
188
- </MantineTable>
191
+ </Box>
189
192
  {footer}
193
+ {lastUpdated
194
+ ? cloneElement(lastUpdated, {
195
+ dependencies: [data, ...(lastUpdated.props.dependencies ?? [])],
196
+ })
197
+ : null}
190
198
  </>
191
199
  )}
192
200
  </TableContext.Provider>
@@ -198,6 +206,7 @@ Table.Actions = TableActions;
198
206
  Table.Filter = TableFilter;
199
207
  Table.Footer = TableFooter;
200
208
  Table.Header = TableHeader;
209
+ Table.LastUpdated = TableLastUpdated;
201
210
  Table.Pagination = TablePagination;
202
211
  Table.Predicate = TablePredicate;
203
212
  Table.PerPage = TablePerPage;
@@ -1,16 +1,17 @@
1
+ import {Icon} from '@coveord/plasma-react-icons';
1
2
  import {UseFormReturnType} from '@mantine/form';
2
3
  import {
3
4
  ColumnDef,
4
5
  CoreOptions,
6
+ InitialTableState as TanstackInitialTableState,
5
7
  Table,
6
8
  TableOptions,
7
- InitialTableState as TanstackInitialTableState,
8
9
  TableState as TanstackTableState,
9
10
  } from '@tanstack/table-core';
10
11
  import {Dispatch, ReactElement, ReactNode, RefObject} from 'react';
11
12
 
12
- import {Icon} from '@coveord/plasma-react-icons';
13
13
  import {DateRangePickerValue} from '../date-range-picker/DateRangePickerInlineCalendar';
14
+ import {TableLayouts} from './layouts/TableLayouts';
14
15
  import {TableActions} from './TableActions';
15
16
  import {TableAccordionColumn, TableCollapsibleColumn} from './TableCollapsibleColumn';
16
17
  import {TableConsumer} from './TableConsumer';
@@ -18,11 +19,11 @@ import {TableDateRangePicker} from './TableDateRangePicker';
18
19
  import {TableFilter} from './TableFilter';
19
20
  import {TableFooter} from './TableFooter';
20
21
  import {TableHeader} from './TableHeader';
22
+ import {TableLastUpdated} from './TableLastUpdated';
21
23
  import {TableLoading} from './TableLoading';
22
24
  import {TablePagination} from './TablePagination';
23
25
  import {TablePerPage} from './TablePerPage';
24
26
  import {TablePredicate} from './TablePredicate';
25
- import {TableLayouts} from './layouts/TableLayouts';
26
27
 
27
28
  export type RowSelectionWithData<TData> = Record<string, TData>;
28
29
  export interface RowSelectionState<TData> {
@@ -245,6 +246,15 @@ export interface TableProps<T> {
245
246
  * @default false
246
247
  */
247
248
  disableRowSelection?: boolean;
249
+ /**
250
+ * Nodes that are considered inside the table.
251
+ *
252
+ * Rows normally get unselected when clicking outside the table, but sometimes it has difficulties guessing what is inside or outside, for example when using modals.
253
+ * You can use this prop to force the table to consider some nodes to be inside the table.
254
+ *
255
+ * @see https://mantine.dev/hooks/use-click-outside/#multiple-nodes
256
+ */
257
+ additionalRootNodes?: HTMLElement[];
248
258
  /**
249
259
  * Additional options that can be passed to the table
250
260
  */
@@ -268,6 +278,7 @@ export interface TableType {
268
278
  Filter: typeof TableFilter;
269
279
  Footer: typeof TableFooter;
270
280
  Header: typeof TableHeader;
281
+ LastUpdated: typeof TableLastUpdated;
271
282
  Pagination: typeof TablePagination;
272
283
  PerPage: typeof TablePerPage;
273
284
  Predicate: typeof TablePredicate;
@@ -5,7 +5,7 @@ interface TableFooterProps extends DefaultProps {
5
5
  children?: ReactNode;
6
6
  }
7
7
  export const TableFooter: FunctionComponent<TableFooterProps> = ({children, ...others}) => (
8
- <Group position="apart" px="md" py="sm" {...others}>
8
+ <Group position="apart" px="xl" py="md" {...others}>
9
9
  {children}
10
10
  </Group>
11
11
  );
@@ -15,7 +15,7 @@ const useStyles = createStyles((theme) => ({
15
15
  borderBottom: `1px solid ${theme.colors.gray[3]}`,
16
16
  },
17
17
  multiSelectInfo: {
18
- justifySelf: 'flex-start',
18
+ display: 'flex',
19
19
  },
20
20
  }));
21
21
 
@@ -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
+ };
@@ -29,6 +29,7 @@ export const TablePagination: FunctionComponent<TablePaginationProps> = ({totalP
29
29
  total={total}
30
30
  boundaries={0}
31
31
  size="md"
32
+ spacing="xs"
32
33
  getControlProps={(control) => {
33
34
  switch (control) {
34
35
  case 'previous':
@@ -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="md"
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('cell', {name: 'fruit'}));
70
- await user.click(screen.getByRole('cell', {name: 'vegetable'}));
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
  });