@purpurds/table 0.0.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 (142) hide show
  1. package/dist/LICENSE.txt +213 -0
  2. package/dist/cell-types/badge-cell.d.ts +8 -0
  3. package/dist/cell-types/badge-cell.d.ts.map +1 -0
  4. package/dist/cell-types/body-text-cell.d.ts +8 -0
  5. package/dist/cell-types/body-text-cell.d.ts.map +1 -0
  6. package/dist/cell-types/button-cell.d.ts +8 -0
  7. package/dist/cell-types/button-cell.d.ts.map +1 -0
  8. package/dist/cell-types/button-group-cell.d.ts +16 -0
  9. package/dist/cell-types/button-group-cell.d.ts.map +1 -0
  10. package/dist/cell-types/cta-link-cell.d.ts +8 -0
  11. package/dist/cell-types/cta-link-cell.d.ts.map +1 -0
  12. package/dist/cell-types/date-cell.d.ts +8 -0
  13. package/dist/cell-types/date-cell.d.ts.map +1 -0
  14. package/dist/cell-types/empty-cell.d.ts +4 -0
  15. package/dist/cell-types/empty-cell.d.ts.map +1 -0
  16. package/dist/cell-types/error-message-cell.d.ts +8 -0
  17. package/dist/cell-types/error-message-cell.d.ts.map +1 -0
  18. package/dist/cell-types/icon-text-cell.d.ts +8 -0
  19. package/dist/cell-types/icon-text-cell.d.ts.map +1 -0
  20. package/dist/cell-types/lead-text-cell.d.ts +8 -0
  21. package/dist/cell-types/lead-text-cell.d.ts.map +1 -0
  22. package/dist/cell-types/link-cell.d.ts +8 -0
  23. package/dist/cell-types/link-cell.d.ts.map +1 -0
  24. package/dist/cell-types/number-cell.d.ts +8 -0
  25. package/dist/cell-types/number-cell.d.ts.map +1 -0
  26. package/dist/cell-types/row-selection-cell.d.ts +8 -0
  27. package/dist/cell-types/row-selection-cell.d.ts.map +1 -0
  28. package/dist/cell-types/row-toggle-cell.d.ts +8 -0
  29. package/dist/cell-types/row-toggle-cell.d.ts.map +1 -0
  30. package/dist/cell-types/toggle-cell.d.ts +8 -0
  31. package/dist/cell-types/toggle-cell.d.ts.map +1 -0
  32. package/dist/cell-types/warning-message-cell.d.ts +8 -0
  33. package/dist/cell-types/warning-message-cell.d.ts.map +1 -0
  34. package/dist/metadata.js +17 -0
  35. package/dist/story-utils/column-def.d.ts +5 -0
  36. package/dist/story-utils/column-def.d.ts.map +1 -0
  37. package/dist/story-utils/table-data.d.ts +35 -0
  38. package/dist/story-utils/table-data.d.ts.map +1 -0
  39. package/dist/story-utils/use-fetch-table-data-hook.d.ts +11 -0
  40. package/dist/story-utils/use-fetch-table-data-hook.d.ts.map +1 -0
  41. package/dist/styles.css +1 -0
  42. package/dist/table-action-bar.d.ts +26 -0
  43. package/dist/table-action-bar.d.ts.map +1 -0
  44. package/dist/table-body.d.ts +10 -0
  45. package/dist/table-body.d.ts.map +1 -0
  46. package/dist/table-column-header-cell.d.ts +28 -0
  47. package/dist/table-column-header-cell.d.ts.map +1 -0
  48. package/dist/table-export-drawer.d.ts +17 -0
  49. package/dist/table-export-drawer.d.ts.map +1 -0
  50. package/dist/table-header.d.ts +11 -0
  51. package/dist/table-header.d.ts.map +1 -0
  52. package/dist/table-row-cell-skeleton.d.ts +14 -0
  53. package/dist/table-row-cell-skeleton.d.ts.map +1 -0
  54. package/dist/table-row-cell.d.ts +25 -0
  55. package/dist/table-row-cell.d.ts.map +1 -0
  56. package/dist/table-row.d.ts +11 -0
  57. package/dist/table-row.d.ts.map +1 -0
  58. package/dist/table-settings-drawer.d.ts +41 -0
  59. package/dist/table-settings-drawer.d.ts.map +1 -0
  60. package/dist/table-toolbar.d.ts +37 -0
  61. package/dist/table-toolbar.d.ts.map +1 -0
  62. package/dist/table.cjs.js +259 -0
  63. package/dist/table.cjs.js.map +1 -0
  64. package/dist/table.d.ts +20 -0
  65. package/dist/table.d.ts.map +1 -0
  66. package/dist/table.es.js +13585 -0
  67. package/dist/table.es.js.map +1 -0
  68. package/dist/test-utils/column-def.d.ts +6 -0
  69. package/dist/test-utils/column-def.d.ts.map +1 -0
  70. package/dist/test-utils/helpers.d.ts +138 -0
  71. package/dist/test-utils/helpers.d.ts.map +1 -0
  72. package/dist/test-utils/table-data.d.ts +33 -0
  73. package/dist/test-utils/table-data.d.ts.map +1 -0
  74. package/dist/types.d.ts +420 -0
  75. package/dist/types.d.ts.map +1 -0
  76. package/dist/use-screen-size.hook.d.ts +7 -0
  77. package/dist/use-screen-size.hook.d.ts.map +1 -0
  78. package/dist/use-truncated-hook.d.ts +10 -0
  79. package/dist/use-truncated-hook.d.ts.map +1 -0
  80. package/dist/utils/custom-functions.d.ts +9 -0
  81. package/dist/utils/custom-functions.d.ts.map +1 -0
  82. package/dist/utils/unit-conversions.d.ts +19 -0
  83. package/dist/utils/unit-conversions.d.ts.map +1 -0
  84. package/dist/utils/unit-conversions.spec.d.ts +2 -0
  85. package/dist/utils/unit-conversions.spec.d.ts.map +1 -0
  86. package/eslint.config.mjs +2 -0
  87. package/package.json +82 -0
  88. package/src/cell-types/badge-cell.tsx +25 -0
  89. package/src/cell-types/body-text-cell.tsx +54 -0
  90. package/src/cell-types/button-cell.tsx +26 -0
  91. package/src/cell-types/button-group-cell.tsx +54 -0
  92. package/src/cell-types/cta-link-cell.tsx +25 -0
  93. package/src/cell-types/date-cell.tsx +33 -0
  94. package/src/cell-types/empty-cell.tsx +6 -0
  95. package/src/cell-types/error-message-cell.tsx +30 -0
  96. package/src/cell-types/icon-text-cell.tsx +30 -0
  97. package/src/cell-types/lead-text-cell.tsx +19 -0
  98. package/src/cell-types/link-cell.tsx +58 -0
  99. package/src/cell-types/number-cell.tsx +27 -0
  100. package/src/cell-types/row-selection-cell.tsx +22 -0
  101. package/src/cell-types/row-toggle-cell.tsx +23 -0
  102. package/src/cell-types/toggle-cell.tsx +19 -0
  103. package/src/cell-types/warning-message-cell.tsx +30 -0
  104. package/src/global.d.ts +4 -0
  105. package/src/story-utils/column-def.ts +148 -0
  106. package/src/story-utils/table-data.tsx +262 -0
  107. package/src/story-utils/use-fetch-table-data-hook.tsx +30 -0
  108. package/src/table-action-bar.module.scss +106 -0
  109. package/src/table-action-bar.test.tsx +111 -0
  110. package/src/table-action-bar.tsx +104 -0
  111. package/src/table-body.tsx +25 -0
  112. package/src/table-column-header-cell.tsx +305 -0
  113. package/src/table-export-drawer.module.scss +9 -0
  114. package/src/table-export-drawer.test.tsx +75 -0
  115. package/src/table-export-drawer.tsx +59 -0
  116. package/src/table-header.tsx +35 -0
  117. package/src/table-kitchen-sink.test.tsx +1196 -0
  118. package/src/table-row-cell-skeleton.tsx +61 -0
  119. package/src/table-row-cell.test.tsx +360 -0
  120. package/src/table-row-cell.tsx +188 -0
  121. package/src/table-row.tsx +30 -0
  122. package/src/table-settings-drawer.module.scss +25 -0
  123. package/src/table-settings-drawer.test.tsx +350 -0
  124. package/src/table-settings-drawer.tsx +254 -0
  125. package/src/table-toolbar.module.scss +17 -0
  126. package/src/table-toolbar.test.tsx +95 -0
  127. package/src/table-toolbar.tsx +136 -0
  128. package/src/table.module.scss +367 -0
  129. package/src/table.stories.tsx +1246 -0
  130. package/src/table.story.css +11 -0
  131. package/src/table.test.tsx +318 -0
  132. package/src/table.tsx +501 -0
  133. package/src/test-utils/column-def.ts +152 -0
  134. package/src/test-utils/helpers.ts +234 -0
  135. package/src/test-utils/table-data.tsx +318 -0
  136. package/src/types.ts +496 -0
  137. package/src/use-screen-size.hook.ts +23 -0
  138. package/src/use-truncated-hook.tsx +74 -0
  139. package/src/utils/custom-functions.ts +52 -0
  140. package/src/utils/unit-conversions.spec.ts +92 -0
  141. package/src/utils/unit-conversions.ts +30 -0
  142. package/vitest.setup.ts +60 -0
@@ -0,0 +1,350 @@
1
+ import React from "react";
2
+ import { Column } from "@tanstack/react-table";
3
+ import { render, screen, within } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { axe } from "vitest-axe";
6
+
7
+ import { TableSettingsDrawer } from "./table-settings-drawer";
8
+ import { copy, Selectors } from "./test-utils/helpers";
9
+
10
+ const setDrawerIsOpenMock = vi.fn();
11
+ const setColumnFiltersEnabledMock = vi.fn();
12
+ const setStickyFirstColumnMock = vi.fn();
13
+ const setStickyHeadersMock = vi.fn();
14
+ const onResetSettingsMock = vi.fn();
15
+ const getAllColumnsMock = vi.fn(() => [
16
+ {
17
+ id: "column_1",
18
+ columnDef: { header: "Column 1" },
19
+ getIsVisible: vi.fn(() => true),
20
+ getCanHide: vi.fn(() => true),
21
+ toggleVisibility: vi.fn(),
22
+ },
23
+ {
24
+ id: "column_2",
25
+ columnDef: { header: "Column 2" },
26
+ getIsVisible: vi.fn(() => true),
27
+ getCanHide: vi.fn(() => true),
28
+ toggleVisibility: vi.fn(),
29
+ },
30
+ {
31
+ id: "column_3",
32
+ columnDef: { header: "Column 3" },
33
+ getIsVisible: vi.fn(() => true),
34
+ getCanHide: vi.fn(() => true),
35
+ toggleVisibility: vi.fn(),
36
+ },
37
+ ]) as unknown as () => Column<unknown, unknown>[];
38
+
39
+ describe("Data Table - Settings drawer", () => {
40
+ describe("Data Table - Settings drawer", () => {
41
+ let closeButton: HTMLButtonElement;
42
+ let container: HTMLElement;
43
+ let withinDrawerContent: ReturnType<typeof within>;
44
+
45
+ beforeEach(() => {
46
+ container = render(
47
+ <TableSettingsDrawer
48
+ isDrawerOpen={true}
49
+ columnFiltersEnabled={true}
50
+ showColumnFilters={true}
51
+ stickyFirstColumn={true}
52
+ stickyHeaders={true}
53
+ copy={copy.settingsDrawer}
54
+ setDrawerIsOpen={setDrawerIsOpenMock}
55
+ setShowColumnFiltersEnabled={setColumnFiltersEnabledMock}
56
+ setStickyFirstColumn={setStickyFirstColumnMock}
57
+ setStickyHeaders={setStickyHeadersMock}
58
+ getAllColumns={getAllColumnsMock}
59
+ onResetSettings={onResetSettingsMock}
60
+ />
61
+ ).container;
62
+
63
+ closeButton = within(screen.getByTestId(Selectors.SETTINGS_DRAWER.HEADER_ROW)).getByRole(
64
+ "button"
65
+ );
66
+
67
+ withinDrawerContent = within(screen.getByTestId(Selectors.SETTINGS_DRAWER.CONTENT));
68
+ });
69
+
70
+ it("should open the drawer", () => {
71
+ expect(screen.getByTestId(Selectors.SETTINGS_DRAWER.CONTENT)).toBeVisible();
72
+ });
73
+
74
+ it("should have drawer title", () => {
75
+ expect(withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.TITLE)).toHaveTextContent(
76
+ "Table settings"
77
+ );
78
+ });
79
+
80
+ it("should have reset settings button in drawer footer", () => {
81
+ const withinDrawerFooter = within(
82
+ withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.FOOTER)
83
+ );
84
+ expect(
85
+ withinDrawerFooter.getByTestId(Selectors.SETTINGS_DRAWER.FOOTER_RESET_SETTINGS_BUTTON)
86
+ ).toHaveTextContent("Reset settings");
87
+ });
88
+
89
+ it("should have a close drawer button", () => {
90
+ expect(closeButton).toHaveAttribute("aria-label", "Close drawer");
91
+ });
92
+
93
+ it("should be possible to close the drawer", async () => {
94
+ await userEvent.click(closeButton);
95
+ expect(screen.queryByTestId(Selectors.SETTINGS_DRAWER.CONTENT)).toBeNull();
96
+ });
97
+
98
+ it("should be accessible", async () => {
99
+ const results = await axe(container);
100
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
101
+ // @ts-ignore
102
+ expect(results).toHaveNoViolations();
103
+ });
104
+
105
+ describe("General settings", () => {
106
+ let toggleElements: HTMLElement[];
107
+ let toggleLabels: HTMLLabelElement[];
108
+ let withinGeneralSettings: ReturnType<typeof within>;
109
+
110
+ beforeEach(() => {
111
+ withinGeneralSettings = within(
112
+ withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.GENERAL_SETTINGS)
113
+ );
114
+
115
+ toggleElements = withinGeneralSettings.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE);
116
+ toggleLabels = withinGeneralSettings.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE_LABEL);
117
+ });
118
+
119
+ it("should have a a section header", () => {
120
+ expect(withinGeneralSettings.getByRole("heading")).toHaveTextContent("General settings");
121
+ });
122
+
123
+ it("should have 3 toggles", () => {
124
+ expect(toggleElements).toHaveLength(3);
125
+ });
126
+
127
+ it.each([
128
+ { label: "Show Filters", index: 0, dataState: "checked" },
129
+ { label: "Lock first column", index: 1, dataState: "checked" },
130
+ { label: "Sticky header", index: 2, dataState: "checked" },
131
+ ])("should have '%s' toggle", ({ label, index, dataState }) => {
132
+ expect(toggleElements[index]).toHaveRole("switch");
133
+ expect(toggleElements[index]).toHaveAttribute("data-state", dataState);
134
+ expect(toggleLabels[index]).toHaveTextContent(label);
135
+ });
136
+ });
137
+
138
+ describe("Visible columns", () => {
139
+ let toggleElements: HTMLElement[];
140
+ let toggleLabels: HTMLLabelElement[];
141
+ let withinVisibleColumns: ReturnType<typeof within>;
142
+
143
+ beforeEach(() => {
144
+ withinVisibleColumns = within(
145
+ withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.VISIBLE_COLUMNS)
146
+ );
147
+
148
+ toggleElements = withinVisibleColumns.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE);
149
+ toggleLabels = withinVisibleColumns.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE_LABEL);
150
+ });
151
+
152
+ it("should have a a section header", () => {
153
+ expect(withinVisibleColumns.getByRole("heading")).toHaveTextContent("Visible columns");
154
+ });
155
+
156
+ it("should have 3 toggles", () => {
157
+ expect(toggleElements).toHaveLength(3);
158
+ });
159
+
160
+ it.each([
161
+ { label: "Column 1", index: 0, dataState: "checked", disabled: false },
162
+ { label: "Column 2", index: 1, dataState: "checked", disabled: false },
163
+ { label: "Column 3", index: 2, dataState: "checked", disabled: false },
164
+ ])("should have '%s' toggle", ({ label, index, dataState }) => {
165
+ expect(toggleElements[index]).toHaveRole("switch");
166
+ expect(toggleElements[index]).toHaveAttribute("data-state", dataState);
167
+ expect(toggleLabels[index]).toHaveTextContent(label);
168
+ });
169
+
170
+ it("should be accessible", async () => {
171
+ const results = await axe(
172
+ withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.VISIBLE_COLUMNS),
173
+ {
174
+ elementRef: true,
175
+ }
176
+ );
177
+ const buttonNameViolation = results.violations.find(
178
+ (violation) => violation.id === "button-name"
179
+ );
180
+
181
+ // "Buttons must have discernible text (button-name)"
182
+ // This is rule is failing on the toggles
183
+ if (buttonNameViolation) {
184
+ buttonNameViolation.nodes = buttonNameViolation.nodes.filter(
185
+ (node) => !node.element?.matches('[role="switch"]')
186
+ );
187
+
188
+ // remove the violation if there are no more nodes
189
+ if (!buttonNameViolation.nodes.length) {
190
+ results.violations.splice(results.violations.indexOf(buttonNameViolation), 1);
191
+ }
192
+ }
193
+
194
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
195
+ // @ts-ignore
196
+ expect(results).toHaveNoViolations();
197
+ });
198
+ });
199
+ });
200
+
201
+ describe("Filters disabled", () => {
202
+ let withinDrawerContent: ReturnType<typeof within>;
203
+
204
+ beforeEach(() => {
205
+ render(
206
+ <TableSettingsDrawer
207
+ isDrawerOpen={true}
208
+ showColumnFilters={false}
209
+ columnFiltersEnabled={false}
210
+ stickyFirstColumn={true}
211
+ stickyHeaders={true}
212
+ copy={copy.settingsDrawer}
213
+ setDrawerIsOpen={setDrawerIsOpenMock}
214
+ setShowColumnFiltersEnabled={setColumnFiltersEnabledMock}
215
+ setStickyFirstColumn={setStickyFirstColumnMock}
216
+ setStickyHeaders={setStickyHeadersMock}
217
+ getAllColumns={getAllColumnsMock}
218
+ onResetSettings={onResetSettingsMock}
219
+ />
220
+ );
221
+
222
+ withinDrawerContent = within(screen.getByTestId(Selectors.SETTINGS_DRAWER.CONTENT));
223
+ });
224
+
225
+ describe("General settings", () => {
226
+ let toggleElements: HTMLElement[];
227
+ let toggleLabels: HTMLLabelElement[];
228
+ let withinGeneralSettings: ReturnType<typeof within>;
229
+
230
+ beforeEach(() => {
231
+ withinGeneralSettings = within(
232
+ withinDrawerContent.getByTestId(Selectors.SETTINGS_DRAWER.GENERAL_SETTINGS)
233
+ );
234
+
235
+ toggleElements = withinGeneralSettings.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE);
236
+ toggleLabels = withinGeneralSettings.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE_LABEL);
237
+ });
238
+
239
+ it("should have 2 toggles", () => {
240
+ expect(toggleElements).toHaveLength(2);
241
+ });
242
+
243
+ it.each([
244
+ { label: "Lock first column", index: 0, dataState: "checked" },
245
+ { label: "Sticky header", index: 1, dataState: "checked" },
246
+ ])("should have '%s' toggle", ({ label, index, dataState }) => {
247
+ expect(toggleElements[index]).toHaveRole("switch");
248
+ expect(toggleElements[index]).toHaveAttribute("data-state", dataState);
249
+ expect(toggleLabels[index]).toHaveTextContent(label);
250
+ });
251
+ });
252
+ });
253
+
254
+ describe("Row selection in settings drawer", () => {
255
+ const setRowSelectionEnabledMock = vi.fn();
256
+
257
+ describe("When row selection is enabled", () => {
258
+ let toggleElements: HTMLElement[];
259
+ let toggleLabels: HTMLLabelElement[];
260
+ let withinGeneralSettings: ReturnType<typeof within>;
261
+
262
+ beforeEach(() => {
263
+ render(
264
+ <TableSettingsDrawer
265
+ isDrawerOpen={true}
266
+ columnFiltersEnabled={true}
267
+ showColumnFilters={true}
268
+ stickyFirstColumn={true}
269
+ stickyHeaders={true}
270
+ copy={{
271
+ ...copy.settingsDrawer,
272
+ generalSettings: {
273
+ ...copy.settingsDrawer.generalSettings,
274
+ toggles: {
275
+ ...copy.settingsDrawer.generalSettings.toggles,
276
+ rowSelection: "Row selection",
277
+ },
278
+ },
279
+ }}
280
+ setDrawerIsOpen={setDrawerIsOpenMock}
281
+ setShowColumnFiltersEnabled={setColumnFiltersEnabledMock}
282
+ setStickyFirstColumn={setStickyFirstColumnMock}
283
+ setStickyHeaders={setStickyHeadersMock}
284
+ getAllColumns={getAllColumnsMock}
285
+ onResetSettings={onResetSettingsMock}
286
+ enableRowSelection={true}
287
+ rowSelectionEnabled={true}
288
+ setRowSelectionEnabled={setRowSelectionEnabledMock}
289
+ />
290
+ );
291
+
292
+ withinGeneralSettings = within(
293
+ screen.getByTestId(Selectors.SETTINGS_DRAWER.GENERAL_SETTINGS)
294
+ );
295
+ toggleElements = withinGeneralSettings.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE);
296
+ toggleLabels = withinGeneralSettings.getAllByTestId(Selectors.SETTINGS_DRAWER.TOGGLE_LABEL);
297
+ });
298
+
299
+ it("should have 4 toggles including row selection", () => {
300
+ expect(toggleElements).toHaveLength(4);
301
+ });
302
+
303
+ it("should have row selection toggle", () => {
304
+ expect(toggleLabels[3]).toHaveTextContent("Row selection");
305
+ expect(toggleElements[3]).toHaveAttribute("data-state", "checked");
306
+ });
307
+
308
+ it("should call setRowSelectionEnabled when toggled", async () => {
309
+ await userEvent.click(toggleElements[3]);
310
+ expect(setRowSelectionEnabledMock).toHaveBeenCalled();
311
+ });
312
+ });
313
+
314
+ describe("When row selection is not enabled", () => {
315
+ beforeEach(() => {
316
+ render(
317
+ <TableSettingsDrawer
318
+ isDrawerOpen={true}
319
+ columnFiltersEnabled={true}
320
+ showColumnFilters={true}
321
+ stickyFirstColumn={true}
322
+ stickyHeaders={true}
323
+ copy={copy.settingsDrawer}
324
+ setDrawerIsOpen={setDrawerIsOpenMock}
325
+ setShowColumnFiltersEnabled={setColumnFiltersEnabledMock}
326
+ setStickyFirstColumn={setStickyFirstColumnMock}
327
+ setStickyHeaders={setStickyHeadersMock}
328
+ getAllColumns={getAllColumnsMock}
329
+ onResetSettings={onResetSettingsMock}
330
+ enableRowSelection={false}
331
+ rowSelectionEnabled={true}
332
+ setRowSelectionEnabled={setRowSelectionEnabledMock}
333
+ />
334
+ );
335
+ });
336
+
337
+ it("should not show row selection toggle", () => {
338
+ const withinGeneralSettings = within(
339
+ screen.getByTestId(Selectors.SETTINGS_DRAWER.GENERAL_SETTINGS)
340
+ );
341
+ const toggleLabels = withinGeneralSettings.getAllByTestId(
342
+ Selectors.SETTINGS_DRAWER.TOGGLE_LABEL
343
+ );
344
+
345
+ expect(toggleLabels).toHaveLength(3);
346
+ expect(toggleLabels.map((label) => label.textContent)).not.toContain("Row selection");
347
+ });
348
+ });
349
+ });
350
+ });
@@ -0,0 +1,254 @@
1
+ import React from "react";
2
+ import { Button } from "@purpurds/button";
3
+ import { Drawer } from "@purpurds/drawer";
4
+ import { Heading } from "@purpurds/heading";
5
+ import { IconSync } from "@purpurds/icon/sync";
6
+ import { Toggle } from "@purpurds/toggle";
7
+ import { Column, RowData } from "@tanstack/react-table";
8
+ import c from "classnames/bind";
9
+
10
+ import styles from "./table-settings-drawer.module.scss";
11
+
12
+ export type TableSettingsDrawerCopyProps = {
13
+ title: string;
14
+ generalSettings: {
15
+ header: string;
16
+ toggles: {
17
+ showFilters: string;
18
+ lockFirstcolumn: string;
19
+ stickyHeader: string;
20
+ rowSelection?: string;
21
+ };
22
+ };
23
+ visibleColumns: {
24
+ header: string;
25
+ };
26
+ buttons: {
27
+ closeDrawer: string;
28
+ resetSettings: string;
29
+ };
30
+ };
31
+
32
+ export type TableSettingsDrawerProps<TData> = {
33
+ getAllColumns: () => Column<TData, unknown>[];
34
+ onResetSettings: () => void;
35
+ setDrawerIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
36
+ setShowColumnFiltersEnabled: React.Dispatch<React.SetStateAction<boolean>>;
37
+ setStickyFirstColumn: React.Dispatch<React.SetStateAction<boolean>>;
38
+ setStickyHeaders: React.Dispatch<React.SetStateAction<boolean>>;
39
+ columnFiltersEnabled: boolean;
40
+ copy: TableSettingsDrawerCopyProps;
41
+ isDrawerOpen: boolean;
42
+ showColumnFilters: boolean;
43
+ stickyFirstColumn: boolean;
44
+ stickyHeaders: boolean;
45
+ enableRowSelection?: boolean;
46
+ rowSelectionEnabled?: boolean;
47
+ setRowSelectionEnabled?: React.Dispatch<React.SetStateAction<boolean>>;
48
+ };
49
+
50
+ const rootClassName = "purpur-table-settings-drawer";
51
+ const cx = c.bind(styles);
52
+ const rootTestId = "purpur-table-settings-drawer";
53
+
54
+ export const TableSettingsDrawer = <TData extends RowData>({
55
+ columnFiltersEnabled,
56
+ copy,
57
+ isDrawerOpen,
58
+ showColumnFilters,
59
+ stickyFirstColumn,
60
+ stickyHeaders,
61
+ getAllColumns,
62
+ onResetSettings,
63
+ setDrawerIsOpen,
64
+ setShowColumnFiltersEnabled,
65
+ setStickyFirstColumn,
66
+ setStickyHeaders,
67
+ enableRowSelection,
68
+ rowSelectionEnabled,
69
+ setRowSelectionEnabled,
70
+ }: TableSettingsDrawerProps<TData>) => {
71
+ return (
72
+ <div id="purpur-table-settings-drawer">
73
+ <Drawer data-testid={rootTestId} open={isDrawerOpen} onOpenChange={setDrawerIsOpen}>
74
+ <Drawer.Content
75
+ data-testid={`${rootTestId}-content`}
76
+ zIndex={6}
77
+ closeButtonText={copy.buttons.closeDrawer}
78
+ title={copy.title}
79
+ footerContent={
80
+ <FooterContent text={copy.buttons.resetSettings} onClick={onResetSettings} />
81
+ }
82
+ stickyFooter
83
+ aria-describedby={undefined}
84
+ >
85
+ <div className={cx(rootClassName)}>
86
+ <GeneralSettings
87
+ setShowColumnFiltersEnabled={setShowColumnFiltersEnabled}
88
+ setStickyFirstColumn={setStickyFirstColumn}
89
+ setStickyHeaders={setStickyHeaders}
90
+ columnFiltersEnabled={columnFiltersEnabled}
91
+ showColumnFilters={showColumnFilters}
92
+ stickyFirstColumn={stickyFirstColumn}
93
+ stickyHeaders={stickyHeaders}
94
+ copy={copy.generalSettings}
95
+ enableRowSelection={enableRowSelection}
96
+ rowSelectionEnabled={rowSelectionEnabled}
97
+ setRowSelectionEnabled={setRowSelectionEnabled}
98
+ />
99
+ <VisibleColumns getAllColumns={getAllColumns} header={copy.visibleColumns.header} />
100
+ </div>
101
+ </Drawer.Content>
102
+ </Drawer>
103
+ </div>
104
+ );
105
+ };
106
+
107
+ const FooterContent = ({ text, onClick }: { text: string; onClick: () => void }) => (
108
+ <div className={cx(`${rootClassName}__button-group`)}>
109
+ <Button data-testid={`${rootTestId}-reset-button`} variant="tertiary-purple" onClick={onClick}>
110
+ <IconSync size="sm" />
111
+ {text}
112
+ </Button>
113
+ </div>
114
+ );
115
+
116
+ type GeneralSettingsProps = {
117
+ setShowColumnFiltersEnabled: React.Dispatch<React.SetStateAction<boolean>>;
118
+ setStickyHeaders: React.Dispatch<React.SetStateAction<boolean>>;
119
+ setStickyFirstColumn: React.Dispatch<React.SetStateAction<boolean>>;
120
+ columnFiltersEnabled: boolean;
121
+ stickyFirstColumn: boolean;
122
+ stickyHeaders: boolean;
123
+ showColumnFilters: boolean;
124
+ copy: {
125
+ header: string;
126
+ toggles: {
127
+ showFilters: string;
128
+ lockFirstcolumn: string;
129
+ stickyHeader: string;
130
+ rowSelection?: string;
131
+ };
132
+ };
133
+ enableRowSelection?: boolean;
134
+ rowSelectionEnabled?: boolean;
135
+ setRowSelectionEnabled?: React.Dispatch<React.SetStateAction<boolean>>;
136
+ };
137
+
138
+ const GeneralSettings = ({
139
+ setShowColumnFiltersEnabled,
140
+ setStickyFirstColumn,
141
+ setStickyHeaders,
142
+ columnFiltersEnabled,
143
+ showColumnFilters,
144
+ stickyFirstColumn,
145
+ stickyHeaders,
146
+ copy,
147
+ enableRowSelection,
148
+ rowSelectionEnabled,
149
+ setRowSelectionEnabled,
150
+ }: GeneralSettingsProps) => (
151
+ <div
152
+ data-testid={`${rootTestId}-general-settings`}
153
+ className={cx(`${rootClassName}__general-settings`)}
154
+ aria-labelledby="general-settings"
155
+ >
156
+ <Heading id="general-settings" variant="title-100" tag="h3">
157
+ {copy.header}
158
+ </Heading>
159
+ {columnFiltersEnabled && (
160
+ <ToggleWrapper
161
+ id="show-filters"
162
+ checked={showColumnFilters}
163
+ label={copy.toggles.showFilters}
164
+ onChange={setShowColumnFiltersEnabled}
165
+ />
166
+ )}
167
+ <ToggleWrapper
168
+ id="lock-first-column"
169
+ checked={stickyFirstColumn}
170
+ label={copy.toggles.lockFirstcolumn}
171
+ onChange={setStickyFirstColumn}
172
+ />
173
+ <ToggleWrapper
174
+ id="sticky-header"
175
+ checked={stickyHeaders}
176
+ label={copy.toggles.stickyHeader}
177
+ onChange={setStickyHeaders}
178
+ />
179
+ {enableRowSelection && copy.toggles.rowSelection && setRowSelectionEnabled && (
180
+ <ToggleWrapper
181
+ id="row-selection"
182
+ checked={rowSelectionEnabled || false}
183
+ label={copy.toggles.rowSelection}
184
+ onChange={setRowSelectionEnabled}
185
+ />
186
+ )}
187
+ </div>
188
+ );
189
+
190
+ const VisibleColumns = <TData extends RowData>({
191
+ header,
192
+ getAllColumns,
193
+ }: {
194
+ header: string;
195
+ getAllColumns: () => Column<TData, unknown>[];
196
+ }) => {
197
+ const allColumns = getAllColumns();
198
+ // Filter out row-selection and row-toggle columns
199
+ const filteredColumns = allColumns.filter(
200
+ (column) => column.id !== "row-selection" && column.id !== "row-toggle"
201
+ );
202
+ const visible = filteredColumns.filter((column) => column.getIsVisible());
203
+
204
+ const isDisabled = (column: Column<TData, unknown>) => {
205
+ return !column.getCanHide() || (visible.length === 1 && visible.includes(column));
206
+ };
207
+
208
+ return (
209
+ <section
210
+ data-testid={`${rootTestId}-visible-columns`}
211
+ className={cx(`${rootClassName}__visible-columns`)}
212
+ aria-labelledby="visible-columns"
213
+ >
214
+ <Heading id="visible-columns" variant="title-100" tag="h3">
215
+ {header}
216
+ </Heading>
217
+ {filteredColumns.map((column) => (
218
+ <ToggleWrapper
219
+ key={column.id}
220
+ id={column.id}
221
+ checked={column.getIsVisible()}
222
+ disabled={isDisabled(column)}
223
+ label={column.columnDef.header as string}
224
+ onChange={() => column.toggleVisibility()}
225
+ />
226
+ ))}
227
+ </section>
228
+ );
229
+ };
230
+
231
+ const ToggleWrapper = ({
232
+ id,
233
+ checked,
234
+ label,
235
+ disabled,
236
+ onChange,
237
+ }: {
238
+ id: string;
239
+ checked: boolean;
240
+ disabled?: boolean;
241
+ label: string;
242
+ onChange?: React.Dispatch<React.SetStateAction<boolean>>;
243
+ }) => (
244
+ <Toggle
245
+ id={id}
246
+ checked={checked}
247
+ label={label}
248
+ disabled={disabled}
249
+ labelPosition="left"
250
+ className={cx(`${rootClassName}__toggle`)}
251
+ onChange={onChange}
252
+ data-testid={`${rootTestId}-toggle`}
253
+ />
254
+ );
@@ -0,0 +1,17 @@
1
+ .purpur-table-toolbar {
2
+ display: flex;
3
+ justify-content: space-between;
4
+ align-items: center;
5
+ align-content: center;
6
+ column-gap: var(--purpur-spacing-100);
7
+ row-gap: var(--purpur-spacing-50);
8
+ align-self: stretch;
9
+ flex-wrap: wrap;
10
+ width: 100%;
11
+
12
+ &__content {
13
+ display: flex;
14
+ align-items: center;
15
+ gap: var(--purpur-spacing-200);
16
+ }
17
+ }