@coveord/plasma-mantine 52.5.0 → 52.7.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.
Files changed (223) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/.turbo/turbo-test.log +31 -30
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/cjs/components/blank-slate/BlankSlate.js.map +1 -1
  5. package/dist/cjs/components/button/Button.js.map +1 -1
  6. package/dist/cjs/components/button/ButtonWithDisabledTooltip.js.map +1 -1
  7. package/dist/cjs/components/code-editor/CodeEditor.js.map +1 -1
  8. package/dist/cjs/components/code-editor/languages/xml.js.map +1 -1
  9. package/dist/cjs/components/collection/Collection.js.map +1 -1
  10. package/dist/cjs/components/collection/Collection.styles.js.map +1 -1
  11. package/dist/cjs/components/collection/CollectionItem.js.map +1 -1
  12. package/dist/cjs/components/copyToClipboard/CopyToClipboard.js.map +1 -1
  13. package/dist/cjs/components/date-range-picker/DateRangePickerInlineCalendar.js +5 -5
  14. package/dist/cjs/components/date-range-picker/DateRangePickerInlineCalendar.js.map +1 -1
  15. package/dist/cjs/components/date-range-picker/DateRangePickerPopoverCalendar.js.map +1 -1
  16. package/dist/cjs/components/date-range-picker/DateRangePickerPresetSelect.js.map +1 -1
  17. package/dist/cjs/components/date-range-picker/EditableDateRangePicker.js.map +1 -1
  18. package/dist/cjs/components/header/Header.d.ts.map +1 -1
  19. package/dist/cjs/components/header/Header.js +3 -0
  20. package/dist/cjs/components/header/Header.js.map +1 -1
  21. package/dist/cjs/components/header/__tests__/__snapshots__/Header.spec.tsx.snap +1 -1
  22. package/dist/cjs/components/inline-confirm/InlineConfirm.js.map +1 -1
  23. package/dist/cjs/components/inline-confirm/InlineConfirmButton.js.map +1 -1
  24. package/dist/cjs/components/inline-confirm/InlineConfirmContext.js.map +1 -1
  25. package/dist/cjs/components/inline-confirm/InlineConfirmMenuItem.js.map +1 -1
  26. package/dist/cjs/components/inline-confirm/InlineConfirmPrompt.js.map +1 -1
  27. package/dist/cjs/components/inline-confirm/useInlineConfirm.js.map +1 -1
  28. package/dist/cjs/components/menu/Menu.js.map +1 -1
  29. package/dist/cjs/components/modal-wizard/ModalWizard.js.map +1 -1
  30. package/dist/cjs/components/modal-wizard/ModalWizardStep.js.map +1 -1
  31. package/dist/cjs/components/prompt/Prompt.js.map +1 -1
  32. package/dist/cjs/components/prompt/PromptFooter.js.map +1 -1
  33. package/dist/cjs/components/sticky-footer/StickyFooter.js.map +1 -1
  34. package/dist/cjs/components/table/Table.d.ts.map +1 -1
  35. package/dist/cjs/components/table/Table.js +33 -84
  36. package/dist/cjs/components/table/Table.js.map +1 -1
  37. package/dist/cjs/components/table/Table.styles.d.ts +2 -4
  38. package/dist/cjs/components/table/Table.styles.d.ts.map +1 -1
  39. package/dist/cjs/components/table/Table.styles.js +7 -25
  40. package/dist/cjs/components/table/Table.styles.js.map +1 -1
  41. package/dist/cjs/components/table/Table.types.d.ts +68 -2
  42. package/dist/cjs/components/table/Table.types.d.ts.map +1 -1
  43. package/dist/cjs/components/table/TableActions.d.ts +12 -2
  44. package/dist/cjs/components/table/TableActions.d.ts.map +1 -1
  45. package/dist/cjs/components/table/TableActions.js +29 -7
  46. package/dist/cjs/components/table/TableActions.js.map +1 -1
  47. package/dist/cjs/components/table/TableCollapsibleColumn.js.map +1 -1
  48. package/dist/cjs/components/table/TableContext.js.map +1 -1
  49. package/dist/cjs/components/table/TableDateRangePicker.d.ts +12 -1
  50. package/dist/cjs/components/table/TableDateRangePicker.d.ts.map +1 -1
  51. package/dist/cjs/components/table/TableDateRangePicker.js +38 -12
  52. package/dist/cjs/components/table/TableDateRangePicker.js.map +1 -1
  53. package/dist/cjs/components/table/TableFilter.d.ts +1 -0
  54. package/dist/cjs/components/table/TableFilter.d.ts.map +1 -1
  55. package/dist/cjs/components/table/TableFilter.js +3 -1
  56. package/dist/cjs/components/table/TableFilter.js.map +1 -1
  57. package/dist/cjs/components/table/TableFooter.js.map +1 -1
  58. package/dist/cjs/components/table/TableHeader.d.ts.map +1 -1
  59. package/dist/cjs/components/table/TableHeader.js +5 -2
  60. package/dist/cjs/components/table/TableHeader.js.map +1 -1
  61. package/dist/cjs/components/table/TableLayoutControl.d.ts +3 -0
  62. package/dist/cjs/components/table/TableLayoutControl.d.ts.map +1 -0
  63. package/dist/cjs/components/table/TableLayoutControl.js +50 -0
  64. package/dist/cjs/components/table/TableLayoutControl.js.map +1 -0
  65. package/dist/cjs/components/table/TableLoading.d.ts +4 -0
  66. package/dist/cjs/components/table/TableLoading.d.ts.map +1 -0
  67. package/dist/cjs/components/table/TableLoading.js +28 -0
  68. package/dist/cjs/components/table/TableLoading.js.map +1 -0
  69. package/dist/cjs/components/table/TablePagination.js.map +1 -1
  70. package/dist/cjs/components/table/TablePerPage.js.map +1 -1
  71. package/dist/cjs/components/table/TablePredicate.d.ts +12 -2
  72. package/dist/cjs/components/table/TablePredicate.d.ts.map +1 -1
  73. package/dist/cjs/components/table/TablePredicate.js +29 -4
  74. package/dist/cjs/components/table/TablePredicate.js.map +1 -1
  75. package/dist/cjs/components/table/TableSelectableColumn.js.map +1 -1
  76. package/dist/cjs/components/table/Th.d.ts.map +1 -1
  77. package/dist/cjs/components/table/Th.js +2 -1
  78. package/dist/cjs/components/table/Th.js.map +1 -1
  79. package/dist/cjs/components/table/index.d.ts +1 -0
  80. package/dist/cjs/components/table/index.d.ts.map +1 -1
  81. package/dist/cjs/components/table/index.js +12 -3
  82. package/dist/cjs/components/table/index.js.map +1 -1
  83. package/dist/cjs/components/table/layouts/RowLayout.d.ts +3 -0
  84. package/dist/cjs/components/table/layouts/RowLayout.d.ts.map +1 -0
  85. package/dist/cjs/components/table/layouts/RowLayout.js +156 -0
  86. package/dist/cjs/components/table/layouts/RowLayout.js.map +1 -0
  87. package/dist/cjs/components/table/layouts/TableLayouts.d.ts +4 -0
  88. package/dist/cjs/components/table/layouts/TableLayouts.d.ts.map +1 -0
  89. package/dist/cjs/components/table/layouts/TableLayouts.js +16 -0
  90. package/dist/cjs/components/table/layouts/TableLayouts.js.map +1 -0
  91. package/dist/cjs/components/table/useRowSelection.js.map +1 -1
  92. package/dist/cjs/form/FormProvider.js.map +1 -1
  93. package/dist/cjs/form/useForm.js.map +1 -1
  94. package/dist/cjs/hooks/useControlledList.js.map +1 -1
  95. package/dist/cjs/hooks/useParentHeight.js.map +1 -1
  96. package/dist/cjs/index.js.map +1 -1
  97. package/dist/cjs/theme/PlasmaColors.js.map +1 -1
  98. package/dist/cjs/theme/Plasmantine.js.map +1 -1
  99. package/dist/cjs/theme/Theme.js +12 -10
  100. package/dist/cjs/theme/Theme.js.map +1 -1
  101. package/dist/cjs/utils/createPolymorphicComponent.js.map +1 -1
  102. package/dist/cjs/utils/overrideComponent.js.map +1 -1
  103. package/dist/esm/components/blank-slate/BlankSlate.js.map +1 -1
  104. package/dist/esm/components/button/Button.js.map +1 -1
  105. package/dist/esm/components/button/ButtonWithDisabledTooltip.js.map +1 -1
  106. package/dist/esm/components/code-editor/CodeEditor.js.map +1 -1
  107. package/dist/esm/components/code-editor/languages/xml.js.map +1 -1
  108. package/dist/esm/components/collection/Collection.js.map +1 -1
  109. package/dist/esm/components/collection/Collection.styles.js.map +1 -1
  110. package/dist/esm/components/collection/CollectionItem.js.map +1 -1
  111. package/dist/esm/components/copyToClipboard/CopyToClipboard.js.map +1 -1
  112. package/dist/esm/components/date-range-picker/DateRangePickerInlineCalendar.js +5 -5
  113. package/dist/esm/components/date-range-picker/DateRangePickerInlineCalendar.js.map +1 -1
  114. package/dist/esm/components/date-range-picker/DateRangePickerPopoverCalendar.js.map +1 -1
  115. package/dist/esm/components/date-range-picker/DateRangePickerPresetSelect.js.map +1 -1
  116. package/dist/esm/components/date-range-picker/EditableDateRangePicker.js.map +1 -1
  117. package/dist/esm/components/header/Header.d.ts.map +1 -1
  118. package/dist/esm/components/header/Header.js +3 -0
  119. package/dist/esm/components/header/Header.js.map +1 -1
  120. package/dist/esm/components/header/__tests__/__snapshots__/Header.spec.tsx.snap +1 -1
  121. package/dist/esm/components/inline-confirm/InlineConfirm.js.map +1 -1
  122. package/dist/esm/components/inline-confirm/InlineConfirmButton.js.map +1 -1
  123. package/dist/esm/components/inline-confirm/InlineConfirmContext.js.map +1 -1
  124. package/dist/esm/components/inline-confirm/InlineConfirmMenuItem.js.map +1 -1
  125. package/dist/esm/components/inline-confirm/InlineConfirmPrompt.js.map +1 -1
  126. package/dist/esm/components/inline-confirm/useInlineConfirm.js.map +1 -1
  127. package/dist/esm/components/menu/Menu.js.map +1 -1
  128. package/dist/esm/components/modal-wizard/ModalWizard.js.map +1 -1
  129. package/dist/esm/components/modal-wizard/ModalWizardStep.js.map +1 -1
  130. package/dist/esm/components/prompt/Prompt.js.map +1 -1
  131. package/dist/esm/components/prompt/PromptFooter.js.map +1 -1
  132. package/dist/esm/components/sticky-footer/StickyFooter.js.map +1 -1
  133. package/dist/esm/components/table/Table.d.ts.map +1 -1
  134. package/dist/esm/components/table/Table.js +36 -87
  135. package/dist/esm/components/table/Table.js.map +1 -1
  136. package/dist/esm/components/table/Table.styles.d.ts +2 -4
  137. package/dist/esm/components/table/Table.styles.d.ts.map +1 -1
  138. package/dist/esm/components/table/Table.styles.js +7 -25
  139. package/dist/esm/components/table/Table.styles.js.map +1 -1
  140. package/dist/esm/components/table/Table.types.d.ts +68 -2
  141. package/dist/esm/components/table/Table.types.d.ts.map +1 -1
  142. package/dist/esm/components/table/Table.types.js.map +1 -1
  143. package/dist/esm/components/table/TableActions.d.ts +12 -2
  144. package/dist/esm/components/table/TableActions.d.ts.map +1 -1
  145. package/dist/esm/components/table/TableActions.js +30 -8
  146. package/dist/esm/components/table/TableActions.js.map +1 -1
  147. package/dist/esm/components/table/TableCollapsibleColumn.js.map +1 -1
  148. package/dist/esm/components/table/TableContext.js.map +1 -1
  149. package/dist/esm/components/table/TableDateRangePicker.d.ts +12 -1
  150. package/dist/esm/components/table/TableDateRangePicker.d.ts.map +1 -1
  151. package/dist/esm/components/table/TableDateRangePicker.js +39 -13
  152. package/dist/esm/components/table/TableDateRangePicker.js.map +1 -1
  153. package/dist/esm/components/table/TableFilter.d.ts +1 -0
  154. package/dist/esm/components/table/TableFilter.d.ts.map +1 -1
  155. package/dist/esm/components/table/TableFilter.js +3 -1
  156. package/dist/esm/components/table/TableFilter.js.map +1 -1
  157. package/dist/esm/components/table/TableFooter.js.map +1 -1
  158. package/dist/esm/components/table/TableHeader.d.ts.map +1 -1
  159. package/dist/esm/components/table/TableHeader.js +5 -2
  160. package/dist/esm/components/table/TableHeader.js.map +1 -1
  161. package/dist/esm/components/table/TableLayoutControl.d.ts +3 -0
  162. package/dist/esm/components/table/TableLayoutControl.d.ts.map +1 -0
  163. package/dist/esm/components/table/TableLayoutControl.js +40 -0
  164. package/dist/esm/components/table/TableLayoutControl.js.map +1 -0
  165. package/dist/esm/components/table/TableLoading.d.ts +4 -0
  166. package/dist/esm/components/table/TableLoading.d.ts.map +1 -0
  167. package/dist/esm/components/table/TableLoading.js +18 -0
  168. package/dist/esm/components/table/TableLoading.js.map +1 -0
  169. package/dist/esm/components/table/TablePagination.js.map +1 -1
  170. package/dist/esm/components/table/TablePerPage.js.map +1 -1
  171. package/dist/esm/components/table/TablePredicate.d.ts +12 -2
  172. package/dist/esm/components/table/TablePredicate.d.ts.map +1 -1
  173. package/dist/esm/components/table/TablePredicate.js +30 -5
  174. package/dist/esm/components/table/TablePredicate.js.map +1 -1
  175. package/dist/esm/components/table/TableSelectableColumn.js.map +1 -1
  176. package/dist/esm/components/table/Th.d.ts.map +1 -1
  177. package/dist/esm/components/table/Th.js +2 -1
  178. package/dist/esm/components/table/Th.js.map +1 -1
  179. package/dist/esm/components/table/index.d.ts +1 -0
  180. package/dist/esm/components/table/index.d.ts.map +1 -1
  181. package/dist/esm/components/table/index.js +1 -0
  182. package/dist/esm/components/table/index.js.map +1 -1
  183. package/dist/esm/components/table/layouts/RowLayout.d.ts +3 -0
  184. package/dist/esm/components/table/layouts/RowLayout.d.ts.map +1 -0
  185. package/dist/esm/components/table/layouts/RowLayout.js +146 -0
  186. package/dist/esm/components/table/layouts/RowLayout.js.map +1 -0
  187. package/dist/esm/components/table/layouts/TableLayouts.d.ts +4 -0
  188. package/dist/esm/components/table/layouts/TableLayouts.d.ts.map +1 -0
  189. package/dist/esm/components/table/layouts/TableLayouts.js +6 -0
  190. package/dist/esm/components/table/layouts/TableLayouts.js.map +1 -0
  191. package/dist/esm/components/table/useRowSelection.js.map +1 -1
  192. package/dist/esm/form/FormProvider.js.map +1 -1
  193. package/dist/esm/form/useForm.js.map +1 -1
  194. package/dist/esm/hooks/useControlledList.js.map +1 -1
  195. package/dist/esm/hooks/useParentHeight.js.map +1 -1
  196. package/dist/esm/theme/PlasmaColors.js.map +1 -1
  197. package/dist/esm/theme/Theme.js +12 -10
  198. package/dist/esm/theme/Theme.js.map +1 -1
  199. package/dist/esm/utils/overrideComponent.js.map +1 -1
  200. package/package.json +10 -10
  201. package/src/__tests__/VitestSetup.ts +7 -5
  202. package/src/components/date-range-picker/DateRangePickerInlineCalendar.tsx +3 -3
  203. package/src/components/header/Header.tsx +5 -1
  204. package/src/components/header/__tests__/Header.spec.tsx +1 -1
  205. package/src/components/header/__tests__/__snapshots__/Header.spec.tsx.snap +1 -1
  206. package/src/components/table/Table.styles.ts +33 -63
  207. package/src/components/table/Table.tsx +42 -83
  208. package/src/components/table/Table.types.ts +71 -2
  209. package/src/components/table/TableActions.tsx +22 -6
  210. package/src/components/table/TableDateRangePicker.tsx +34 -12
  211. package/src/components/table/TableFilter.tsx +4 -3
  212. package/src/components/table/TableHeader.tsx +10 -2
  213. package/src/components/table/TableLayoutControl.tsx +29 -0
  214. package/src/components/table/TableLoading.tsx +8 -0
  215. package/src/components/table/TablePredicate.tsx +24 -7
  216. package/src/components/table/Th.tsx +1 -0
  217. package/src/components/table/__tests__/Table.spec.tsx +140 -316
  218. package/src/components/table/__tests__/TableDateRangePicker.spec.tsx +41 -14
  219. package/src/components/table/index.ts +1 -0
  220. package/src/components/table/layouts/RowLayout.tsx +150 -0
  221. package/src/components/table/layouts/TableLayouts.tsx +5 -0
  222. package/src/components/table/layouts/__tests__/RowLayout.spec.tsx +296 -0
  223. package/src/theme/Theme.tsx +10 -10
@@ -1,10 +1,9 @@
1
1
  import {ColumnDef, createColumnHelper} from '@tanstack/table-core';
2
- import {render, screen, userEvent, waitFor, within} from '@test-utils';
3
- import {FunctionComponent} from 'react';
2
+ import {render, screen, userEvent, waitFor} from '@test-utils';
4
3
 
5
4
  import {Table} from '../Table';
6
- import {TableProps} from '../Table.types';
7
5
  import {useTable} from '../TableContext';
6
+ import {TableLayout} from '../Table.types';
8
7
 
9
8
  type RowData = {id: string; firstName: string; lastName?: string};
10
9
 
@@ -14,127 +13,44 @@ const columns: Array<ColumnDef<RowData>> = [
14
13
  columnHelper.accessor('lastName', {enableSorting: false}),
15
14
  ];
16
15
 
17
- describe('Table', () => {
18
- it('renders the data', () => {
19
- render(
20
- <Table
21
- getRowId={({id}) => id}
22
- data={[{id: '🆔', firstName: 'first', lastName: 'last'}]}
23
- columns={columns}
24
- />
25
- );
26
-
27
- expect(screen.getByRole('columnheader', {name: 'firstName'})).toBeVisible();
28
- expect(screen.getByRole('columnheader', {name: 'lastName'})).toBeVisible();
29
-
30
- expect(
31
- screen.getByRole('cell', {
32
- name: /first/i,
33
- })
34
- ).toBeVisible();
35
- expect(
36
- screen.getByRole('cell', {
37
- name: /last/i,
38
- })
39
- ).toBeVisible();
40
- });
41
-
42
- it('formats the data', () => {
43
- const customColumns: Array<ColumnDef<RowData>> = [
44
- columnHelper.accessor('firstName', {
45
- header: () => 'First Name',
46
- cell: (info) => info.getValue().toUpperCase(),
47
- enableSorting: false,
48
- }),
49
- columnHelper.accessor('lastName', {
50
- header: () => 'Last Name',
51
- cell: (info) => info.getValue().toUpperCase(),
52
- enableSorting: false,
53
- }),
54
- ];
55
- render(<Table data={[{id: '🆔', firstName: 'first', lastName: 'last'}]} columns={customColumns} />);
56
-
57
- expect(screen.getByRole('columnheader', {name: 'First Name'})).toBeVisible();
58
- expect(screen.getByRole('columnheader', {name: 'Last Name'})).toBeVisible();
59
-
60
- expect(
61
- screen.getByRole('cell', {
62
- name: 'FIRST',
63
- })
64
- ).toBeVisible();
65
- expect(
66
- screen.getByRole('cell', {
67
- name: 'LAST',
68
- })
69
- ).toBeVisible();
70
- });
71
-
72
- it('renders the noDataChildren when the table is empty', () => {
73
- const Fixture: FunctionComponent = () => <button>Hello</button>;
74
- render(<Table data={[]} noDataChildren={<Fixture />} columns={columns} />);
16
+ const EmptyState = () => {
17
+ const {isFiltered} = useTable();
18
+ return isFiltered ? <span data-testid="filtered-empty-state" /> : <span data-testid="empty-state" />;
19
+ };
75
20
 
76
- expect(screen.getByRole('button', {name: 'Hello'})).toBeVisible();
77
- });
78
-
79
- it('hides the footer and header when the table is empty and not filtered', () => {
80
- const NoData = () => <span data-testid="empty-state" />;
81
- const customColumns: Array<ColumnDef<RowData>> = [
82
- columnHelper.accessor('firstName', {
83
- header: () => 'First Name',
84
- cell: (info) => info.getValue().toUpperCase(),
85
- enableSorting: false,
86
- }),
87
- columnHelper.accessor('lastName', {
88
- header: () => 'Last Name',
89
- cell: (info) => info.getValue().toUpperCase(),
90
- enableSorting: false,
91
- }),
92
- ];
93
- render(
94
- <Table data={[]} columns={customColumns} noDataChildren={<NoData />}>
95
- <Table.Header data-testid="table-header">header</Table.Header>
96
- <Table.Footer data-testid="table-footer">footer</Table.Footer>
97
- </Table>
98
- );
21
+ describe('Table', () => {
22
+ describe('when it has no data', () => {
23
+ it('hides the footer and header if the table is not filtered', () => {
24
+ render(
25
+ <Table data={[]} columns={columns} noDataChildren={<EmptyState />}>
26
+ <Table.Header data-testid="table-header">header</Table.Header>
27
+ <Table.Footer data-testid="table-footer">footer</Table.Footer>
28
+ </Table>
29
+ );
99
30
 
100
- expect(screen.queryByTestId('table-header')).not.toBeInTheDocument();
101
- expect(screen.queryByTestId('table-footer')).not.toBeInTheDocument();
102
- expect(screen.getByTestId('empty-state')).toBeInTheDocument();
103
- });
31
+ expect(screen.queryByTestId('table-header')).not.toBeInTheDocument();
32
+ expect(screen.queryByTestId('table-footer')).not.toBeInTheDocument();
33
+ expect(screen.getByTestId('empty-state')).toBeVisible();
34
+ });
104
35
 
105
- it('does not hide the footer and header when the table is empty and filtered', () => {
106
- const NoData = () => {
107
- const {isFiltered} = useTable();
108
- return isFiltered ? <span data-testid="filtered-empty-state" /> : <span data-testid="empty-state" />;
109
- };
110
- const customColumns: Array<ColumnDef<RowData>> = [
111
- columnHelper.accessor('firstName', {
112
- header: () => 'First Name',
113
- cell: (info) => info.getValue().toUpperCase(),
114
- enableSorting: false,
115
- }),
116
- columnHelper.accessor('lastName', {
117
- header: () => 'Last Name',
118
- cell: (info) => info.getValue().toUpperCase(),
119
- enableSorting: false,
120
- }),
121
- ];
122
- render(
123
- <Table
124
- data={[]}
125
- columns={customColumns}
126
- noDataChildren={<NoData />}
127
- initialState={{globalFilter: 'something'}}
128
- >
129
- <Table.Header data-testid="table-header">header</Table.Header>
130
- <Table.Footer data-testid="table-footer">footer</Table.Footer>
131
- </Table>
132
- );
36
+ it('does not hide the footer and header if the table is filtered', () => {
37
+ render(
38
+ <Table
39
+ data={[]}
40
+ columns={columns}
41
+ noDataChildren={<EmptyState />}
42
+ initialState={{globalFilter: 'something'}}
43
+ >
44
+ <Table.Header data-testid="table-header">header</Table.Header>
45
+ <Table.Footer data-testid="table-footer">footer</Table.Footer>
46
+ </Table>
47
+ );
133
48
 
134
- expect(screen.getByTestId('table-header')).toBeInTheDocument();
135
- expect(screen.getByTestId('table-footer')).toBeInTheDocument();
136
- expect(screen.getByTestId('filtered-empty-state')).toBeInTheDocument();
137
- expect(screen.queryByTestId('empty-state')).not.toBeInTheDocument();
49
+ expect(screen.getByTestId('table-header')).toBeVisible();
50
+ expect(screen.getByTestId('table-footer')).toBeVisible();
51
+ expect(screen.getByTestId('filtered-empty-state')).toBeVisible();
52
+ expect(screen.queryByTestId('empty-state')).not.toBeInTheDocument();
53
+ });
138
54
  });
139
55
 
140
56
  it('updates the table when a component in Table.Consumer triggers a change', async () => {
@@ -162,176 +78,42 @@ describe('Table', () => {
162
78
  });
163
79
  });
164
80
 
165
- describe('shows a loading animation', () => {
166
- const doRender = (props: Omit<TableProps<RowData>, 'columns'>) => {
167
- const NoData = () => {
168
- const {isFiltered, clearFilters} = useTable();
169
- return isFiltered ? (
170
- <button data-testid="filtered-empty-state" onClick={clearFilters}></button>
171
- ) : (
172
- <span data-testid="empty-state" />
173
- );
174
- };
175
-
176
- const customColumns: Array<ColumnDef<RowData>> = [
177
- columnHelper.accessor('firstName', {
178
- header: () => 'First Name',
179
- cell: (info) => info.getValue().toUpperCase(),
180
- enableSorting: false,
181
- }),
182
- columnHelper.accessor('lastName', {
183
- header: () => 'Last Name',
184
- cell: (info) => info.getValue().toUpperCase(),
185
- enableSorting: false,
186
- }),
187
- ];
81
+ describe('when it is loading', () => {
82
+ it('shows a loading animation over the no data children (filtered)', () => {
188
83
  render(
189
- <Table columns={customColumns} noDataChildren={<NoData />} {...props}>
84
+ <Table
85
+ loading
86
+ data={[]}
87
+ columns={columns}
88
+ noDataChildren={<EmptyState />}
89
+ initialState={{globalFilter: 'something'}}
90
+ >
190
91
  <Table.Header>
191
92
  <Table.Filter data-testid="table-filter" />
192
93
  </Table.Header>
193
94
  </Table>
194
95
  );
195
- };
196
-
197
- it('when the table filtered, empty and loading', () => {
198
- doRender({loading: true, data: [], initialState: {globalFilter: 'something'}});
199
96
  expect(screen.getByTestId('filtered-empty-state').parentElement).toHaveClass(
200
97
  'mantine-Skeleton-root mantine-Skeleton-visible'
201
98
  );
202
99
  });
203
100
 
204
- it('when the table not filtered, empty and loading', () => {
205
- doRender({data: [], loading: true});
101
+ it('shows a loading animation over the no data children (unfiltered)', () => {
102
+ render(
103
+ <Table loading data={[]} columns={columns} noDataChildren={<EmptyState />}>
104
+ <Table.Header>
105
+ <Table.Filter data-testid="table-filter" />
106
+ </Table.Header>
107
+ </Table>
108
+ );
206
109
  expect(screen.getByTestId('empty-state').parentElement).toHaveClass(
207
110
  'mantine-Skeleton-root mantine-Skeleton-visible'
208
111
  );
209
112
  });
210
113
  });
211
114
 
212
- it('opens the collapsible rows when the user click on the toggle', async () => {
213
- const user = userEvent.setup({delay: null});
214
- const Fixture: FunctionComponent<{row: RowData}> = ({row}) => <div>Collapsible content: {row.lastName}</div>;
215
- const customColumns: Array<ColumnDef<RowData>> = [
216
- columnHelper.accessor('firstName', {
217
- enableSorting: false,
218
- }),
219
- Table.CollapsibleColumn as ColumnDef<RowData>,
220
- ];
221
- render(
222
- <Table
223
- getRowId={({id}) => id}
224
- data={[{id: '🆔', firstName: 'first', lastName: 'last'}]}
225
- getExpandChildren={(row: RowData) => <Fixture row={row} />}
226
- columns={customColumns}
227
- />
228
- );
229
-
230
- // wait for the collapsible icon to show
231
- await screen.findByRole('button', {name: 'arrowHeadDown'});
232
-
233
- expect(screen.queryByText('Collapsible content: last')).not.toBeVisible();
234
-
235
- await user.click(screen.getByRole('button', {name: 'arrowHeadDown'}));
236
- await waitFor(() => {
237
- expect(screen.queryByText('Collapsible content: last')).toBeVisible();
238
- });
239
- });
240
-
241
- it('renders the collapsible button only for rows that can be expanded', async () => {
242
- const Fixture: FunctionComponent<{row: RowData}> = ({row}) => (
243
- <div>
244
- Collapsible content: {row.firstName} {row.lastName}
245
- </div>
246
- );
247
- const customColumns: Array<ColumnDef<RowData>> = [
248
- columnHelper.accessor('firstName', {
249
- enableSorting: false,
250
- }),
251
- Table.CollapsibleColumn as ColumnDef<RowData>,
252
- ];
253
- render(
254
- <Table
255
- getRowId={({id}) => id}
256
- data={[
257
- {id: '🆔-1', firstName: 'Luke', lastName: 'Skywalker'},
258
- {id: '🆔-2', firstName: 'Lea', lastName: 'Skywalker'},
259
- {id: '🆔-3', firstName: 'Han', lastName: 'Solo'},
260
- ]}
261
- getExpandChildren={(row: RowData) => (row.lastName === 'Skywalker' ? <Fixture row={row} /> : null)}
262
- columns={customColumns}
263
- />
264
- );
265
-
266
- // wait for the collapsible icon to show
267
- await screen.findAllByRole('button', {name: 'arrowHeadDown'});
268
-
269
- const allRows = screen.getAllByRole('button', {name: 'arrowHeadDown'});
270
- expect(allRows).toHaveLength(2);
271
- });
272
-
273
- it('closes the opened collapsible when using the accordion column and the user expand a different row', async () => {
274
- const user = userEvent.setup({delay: null});
275
- const Fixture: FunctionComponent<{row: RowData}> = ({row}) => <div>Collapsible content: {row.lastName}</div>;
276
- const customColumns: Array<ColumnDef<RowData>> = [
277
- columnHelper.accessor('firstName', {
278
- enableSorting: false,
279
- }),
280
- Table.AccordionColumn as ColumnDef<RowData>,
281
- ];
282
- render(
283
- <Table
284
- getRowId={({id}) => id}
285
- data={[
286
- {id: '🆔-1', firstName: 'Jack', lastName: 'Russel'},
287
- {id: '🆔-2', firstName: 'Golden', lastName: 'Retriever'},
288
- ]}
289
- getExpandChildren={(row: RowData) => <Fixture row={row} />}
290
- columns={customColumns}
291
- />
292
- );
293
-
294
- // wait for the collapsible icon to show
295
- await screen.findAllByRole('button', {name: 'arrowHeadDown'});
296
-
297
- expect(screen.queryByText('Collapsible content: Russel')).not.toBeVisible();
298
- expect(screen.queryByText('Collapsible content: Retriever')).not.toBeVisible();
299
-
300
- await user.click(within(screen.getAllByRole('row')[1]).getByRole('button', {name: 'arrowHeadDown'}));
301
- await waitFor(() => {
302
- expect(screen.queryByText('Collapsible content: Russel')).toBeVisible();
303
- });
304
- expect(screen.queryByText('Collapsible content: Retriever')).not.toBeVisible();
305
-
306
- await user.click(within(screen.getAllByRole('row')[3]).getByRole('button', {name: 'arrowHeadDown'}));
307
-
308
- await waitFor(() => {
309
- expect(screen.queryByText('Collapsible content: Retriever')).toBeVisible();
310
- });
311
- expect(screen.queryByText('Collapsible content: Russel')).not.toBeVisible();
312
- });
313
-
314
- it('calls an action when user double clicks on a row', async () => {
315
- const user = userEvent.setup();
316
- const doubleClickSpy = vi.fn();
317
- render(
318
- <Table<RowData>
319
- getRowId={({id}) => id}
320
- data={[
321
- {id: '🆔-1', firstName: 'Mario'},
322
- {id: '🆔-2', firstName: 'Luigi'},
323
- ]}
324
- columns={columns}
325
- doubleClickAction={doubleClickSpy}
326
- ></Table>
327
- );
328
- await user.dblClick(screen.getByRole('cell', {name: 'Mario'}));
329
- expect(doubleClickSpy).toHaveBeenCalledTimes(1);
330
- expect(doubleClickSpy).toHaveBeenCalledWith({id: '🆔-1', firstName: 'Mario'});
331
- });
332
-
333
115
  it('reset row selection when user click outside the table', async () => {
334
- const user = userEvent.setup({delay: null});
116
+ const user = userEvent.setup();
335
117
  render(
336
118
  <div>
337
119
  <div>I'm a header</div>
@@ -359,73 +141,96 @@ describe('Table', () => {
359
141
  expect(screen.getByRole('row', {name: 'patate king', selected: false})).toBeInTheDocument();
360
142
  });
361
143
 
362
- describe('when multi row selection is enabled', () => {
363
- it('displays a checkbox as the first cell of each row', () => {
144
+ describe('with multiple layouts', () => {
145
+ const layouts: TableLayout[] = [
146
+ {
147
+ name: 'Layout 1',
148
+ Header: () => <tr data-testid="layout1-header" />,
149
+ Body: () => <tr data-testid="layout1-body" />,
150
+ },
151
+ {
152
+ name: 'Layout 2',
153
+ Header: () => <tr data-testid="layout2-header" />,
154
+ Body: () => <tr data-testid="layout2-body" />,
155
+ },
156
+ ];
157
+
158
+ it('handles switching layout', async () => {
159
+ const user = userEvent.setup();
364
160
  render(
365
161
  <Table
366
162
  getRowId={({id}) => id}
367
- data={[
368
- {id: '🆔-1', firstName: 'John', lastName: 'Smith'},
369
- {id: '🆔-2', firstName: 'Jane', lastName: 'Doe'},
370
- ]}
163
+ data={[{id: '🆔', firstName: 'first', lastName: 'last'}]}
371
164
  columns={columns}
372
- multiRowSelectionEnabled
373
- />
165
+ layouts={layouts}
166
+ >
167
+ <Table.Header data-testid="table-header" />
168
+ </Table>
374
169
  );
375
-
376
- expect(screen.getByRole('columnheader', {name: /select all from this page/i})).toBeInTheDocument();
377
-
378
- const rows = screen.getAllByRole('row');
379
- rows.forEach((row) => {
380
- expect(within(row).getByRole('checkbox', {name: /select/i})).toBeInTheDocument();
381
- });
170
+ expect(screen.getByRole('radio', {name: /layout 1/i})).toBeChecked();
171
+ expect(screen.getByTestId('layout1-header')).toBeInTheDocument();
172
+ expect(screen.getByTestId('layout1-body')).toBeInTheDocument();
173
+ expect(screen.getByRole('radio', {name: /layout 2/i})).not.toBeChecked();
174
+ expect(screen.queryByTestId('layout2-header')).not.toBeInTheDocument();
175
+ expect(screen.queryByTestId('layout2-body')).not.toBeInTheDocument();
176
+
177
+ await user.click(screen.getByRole('radio', {name: /layout 2/i}));
178
+
179
+ expect(screen.getByRole('radio', {name: /layout 2/i})).toBeChecked();
180
+ expect(screen.getByTestId('layout2-header')).toBeInTheDocument();
181
+ expect(screen.getByTestId('layout2-body')).toBeInTheDocument();
182
+ expect(screen.getByRole('radio', {name: /layout 1/i})).not.toBeChecked();
183
+ expect(screen.queryByTestId('layout1-header')).not.toBeInTheDocument();
184
+ expect(screen.queryByTestId('layout1-body')).not.toBeInTheDocument();
382
185
  });
383
186
 
384
- it('selects the rows specified in the initial state on mount', () => {
187
+ it('does not refetch data when switching layout', async () => {
188
+ const user = userEvent.setup();
189
+ const fetchDataSpy = vi.fn();
385
190
  render(
386
191
  <Table
387
192
  getRowId={({id}) => id}
388
- data={[
389
- {id: '🆔-1', firstName: 'John', lastName: 'Smith'},
390
- {id: '🆔-2', firstName: 'Jane', lastName: 'Doe'},
391
- ]}
193
+ data={[{id: '🆔', firstName: 'first', lastName: 'last'}]}
392
194
  columns={columns}
393
- multiRowSelectionEnabled
394
- initialState={{
395
- rowSelection: {'🆔-2': {id: '🆔-2', firstName: 'Jane', lastName: 'Doe'}},
396
- }}
397
- />
195
+ onMount={fetchDataSpy}
196
+ onChange={fetchDataSpy}
197
+ layouts={layouts}
198
+ >
199
+ <Table.Header data-testid="table-header" />
200
+ </Table>
398
201
  );
399
202
 
400
- expect(screen.getByRole('row', {name: /jane doe/i, selected: true})).toBeInTheDocument();
203
+ await user.click(screen.getByRole('radio', {name: /layout 2/i}));
204
+ await user.click(screen.getByRole('radio', {name: /layout 1/i}));
205
+ expect(fetchDataSpy).toHaveBeenCalledTimes(1);
401
206
  });
402
207
 
403
- it('selects all rows of the current page when clicking on the checkbox that is in the column header', async () => {
404
- const user = userEvent.setup({delay: null});
208
+ it('renders the layout specified in the initialState', () => {
405
209
  render(
406
210
  <Table
407
211
  getRowId={({id}) => id}
408
- data={[
409
- {id: '🆔-1', firstName: 'John', lastName: 'Smith'},
410
- {id: '🆔-2', firstName: 'Jane', lastName: 'Doe'},
411
- ]}
212
+ data={[{id: '🆔', firstName: 'first', lastName: 'last'}]}
412
213
  columns={columns}
413
- multiRowSelectionEnabled
414
- />
214
+ layouts={layouts}
215
+ initialState={{layout: 'Layout 2'}}
216
+ >
217
+ <Table.Header data-testid="table-header" />
218
+ </Table>
415
219
  );
416
220
 
417
- const selectAll = screen.getByRole('checkbox', {name: /select all from this page/i});
418
- await user.click(selectAll);
419
-
420
- expect(screen.getAllByRole('row', {selected: true})).toHaveLength(2);
421
- await user.click(selectAll);
422
-
423
- expect(screen.queryAllByRole('row', {selected: true})).toEqual([]);
221
+ expect(screen.getByRole('radio', {name: /layout 2/i})).toBeChecked();
222
+ expect(screen.getByTestId('layout2-header')).toBeInTheDocument();
223
+ expect(screen.getByTestId('layout2-body')).toBeInTheDocument();
224
+ expect(screen.getByRole('radio', {name: /layout 1/i})).not.toBeChecked();
225
+ expect(screen.queryByTestId('layout1-header')).not.toBeInTheDocument();
226
+ expect(screen.queryByTestId('layout1-body')).not.toBeInTheDocument();
424
227
  });
228
+ });
425
229
 
230
+ describe('when multi row selection is enabled', () => {
426
231
  it('calls the onRowSelectionChange prop when the row selection changes', async () => {
427
232
  const onRowSelectionChangeSpy = vi.fn();
428
- const user = userEvent.setup({delay: null});
233
+ const user = userEvent.setup();
429
234
  render(
430
235
  <Table
431
236
  getRowId={({id}) => id}
@@ -459,7 +264,7 @@ describe('Table', () => {
459
264
  });
460
265
 
461
266
  it('does not clear the row selection when clicking outside the table', async () => {
462
- const user = userEvent.setup({delay: null});
267
+ const user = userEvent.setup();
463
268
  render(
464
269
  <div>
465
270
  <div>I'm a header</div>
@@ -489,7 +294,7 @@ describe('Table', () => {
489
294
  });
490
295
 
491
296
  it('unselects all the selected rows when clicking on the the unselect button from the table header', async () => {
492
- const user = userEvent.setup({delay: null});
297
+ const user = userEvent.setup();
493
298
  render(
494
299
  <Table
495
300
  getRowId={({id}) => id}
@@ -508,5 +313,24 @@ describe('Table', () => {
508
313
  await user.click(screen.getByRole('button', {name: /2 selected/i}));
509
314
  expect(screen.queryAllByRole('row', {selected: true})).toEqual([]);
510
315
  });
316
+
317
+ it('does not display number of selected rows if disableRowSelection is true', async () => {
318
+ render(
319
+ <Table
320
+ getRowId={({id}) => id}
321
+ data={[
322
+ {id: '🆔-1', firstName: 'John', lastName: 'Smith'},
323
+ {id: '🆔-2', firstName: 'Jane', lastName: 'Doe'},
324
+ ]}
325
+ columns={columns}
326
+ multiRowSelectionEnabled
327
+ initialState={{
328
+ rowSelection: {'🆔-2': {id: '🆔-2', firstName: 'Jane', lastName: 'Doe'}},
329
+ }}
330
+ />
331
+ );
332
+
333
+ expect(screen.queryByRole('button', {name: /1 selected/i})).not.toBeInTheDocument();
334
+ });
511
335
  });
512
336
  });
@@ -1,5 +1,6 @@
1
1
  import {ColumnDef, createColumnHelper} from '@tanstack/table-core';
2
2
  import {render, screen, userEvent, waitFor} from '@test-utils';
3
+ import {act} from 'react-dom/test-utils';
3
4
 
4
5
  import {Table} from '../Table';
5
6
 
@@ -7,6 +8,17 @@ type RowData = {name: string};
7
8
 
8
9
  const columnHelper = createColumnHelper<RowData>();
9
10
  const columns: Array<ColumnDef<RowData>> = [columnHelper.accessor('name', {enableSorting: false})];
11
+ const basicTableWithDateRangePicker = (
12
+ <Table
13
+ data={[{name: 'fruit'}, {name: 'vegetable'}]}
14
+ columns={columns}
15
+ initialState={{dateRange: [new Date(2022, 0, 1), new Date(2022, 0, 7)]}}
16
+ >
17
+ <Table.Header>
18
+ <Table.DateRangePicker />
19
+ </Table.Header>
20
+ </Table>
21
+ );
10
22
 
11
23
  // Since we're mocking the date and the animations are timer based we're mocking useReduceMotion to disable all the animations
12
24
  // I tried wrapping the components in <MantineProvider theme={{components: {Transition: {defaultProps: {duration: 0}}}}}>
@@ -20,8 +32,6 @@ vi.mock('@mantine/hooks', async () => {
20
32
  });
21
33
 
22
34
  describe('Table.DateRangePicker', () => {
23
- // vi.setTimeout(15000);
24
-
25
35
  beforeEach(() => {
26
36
  vi.useFakeTimers().setSystemTime(new Date(2022, 0, 15));
27
37
  });
@@ -30,24 +40,41 @@ describe('Table.DateRangePicker', () => {
30
40
  vi.useRealTimers();
31
41
  });
32
42
 
33
- it('displays the intial dates', async () => {
34
- render(
35
- <Table
36
- data={[{name: 'fruit'}, {name: 'vegetable'}]}
37
- columns={columns}
38
- initialState={{dateRange: [new Date(2022, 0, 1), new Date(2022, 0, 7)]}}
39
- >
40
- <Table.Header>
41
- <Table.DateRangePicker />
42
- </Table.Header>
43
- </Table>
44
- );
43
+ it('displays the initial dates', async () => {
44
+ render(basicTableWithDateRangePicker);
45
45
 
46
46
  await waitFor(() => {
47
47
  expect(screen.getByText('Jan 01, 2022 - Jan 07, 2022')).toBeVisible();
48
48
  });
49
49
  });
50
50
 
51
+ it('opens the dialog when clicking on the calendar button', async () => {
52
+ // Otherwise, css transition is not triggered in Mantine component
53
+ vi.useRealTimers();
54
+ const user = userEvent.setup({delay: null});
55
+ render(basicTableWithDateRangePicker);
56
+
57
+ await screen.findByRole('button', {name: 'calendar'});
58
+ await act(async () => {
59
+ await user.click(screen.getByRole('button', {name: 'calendar'}));
60
+ });
61
+ expect(screen.queryByRole('dialog')).toBeVisible();
62
+ });
63
+
64
+ it('closes the dialog when clicking back on the calendar button', async () => {
65
+ // Otherwise, css transition is not triggered in Mantine component
66
+ vi.useRealTimers();
67
+ const user = userEvent.setup({delay: null});
68
+ render(basicTableWithDateRangePicker);
69
+
70
+ await screen.findByRole('button', {name: 'calendar'});
71
+ await act(async () => {
72
+ await user.click(screen.getByRole('button', {name: 'calendar'}));
73
+ await user.click(screen.getByRole('button', {name: 'calendar'}));
74
+ });
75
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
76
+ });
77
+
51
78
  it('displays the selected date range in the table', async () => {
52
79
  const user = userEvent.setup({delay: null});
53
80
  const onChange = vi.fn();
@@ -1,3 +1,4 @@
1
1
  export * from './Table';
2
2
  export {useTable} from './TableContext';
3
3
  export {type onTableChangeEvent, type InitialTableState, type TableState, type TableProps} from './Table.types';
4
+ export {TableLayouts} from './layouts/TableLayouts';