@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,11 @@
1
+ div.table-transition {
2
+ transition: max-width var(--purpur-motion-duration-300) var(--purpur-motion-easing-ease-in);
3
+ }
4
+
5
+ div.escape-grid {
6
+ max-width: 100%;
7
+ }
8
+
9
+ .table-max-height {
10
+ max-height: 100dvh;
11
+ }
@@ -0,0 +1,318 @@
1
+ import React from "react";
2
+ import { IllustrativeIconSearchQuestionDuocolor } from "@purpurds/illustrative-icon/search-question-duocolor";
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 { Table } from "./table";
8
+ import { createColumnDefSmall } from "./test-utils/column-def.ts";
9
+ import { copy, getTableBody, getTableHead, Selectors } from "./test-utils/helpers.ts";
10
+ import { TableDataSmall, tableDataSmall } from "./test-utils/table-data.tsx";
11
+
12
+ describe("Data Table", () => {
13
+ let table: HTMLTableElement;
14
+ let tableRows: HTMLTableRowElement[];
15
+ let tableHeaders: HTMLTableCellElement[];
16
+ let container: HTMLElement;
17
+
18
+ describe("Base Table", () => {
19
+ beforeEach(() => {
20
+ container = render(
21
+ <Table columns={createColumnDefSmall()} data={tableDataSmall} />
22
+ ).container;
23
+
24
+ table = screen.getByRole("table");
25
+ tableHeaders = within(getTableHead(table)).getAllByRole("columnheader");
26
+
27
+ tableRows = within(getTableBody(table)).getAllByRole("row");
28
+ });
29
+
30
+ it("should have 10 headers", () => {
31
+ const expectedHeaders = ["ID", "Name", "Link", "Age"];
32
+
33
+ expect(tableHeaders).toHaveLength(expectedHeaders.length);
34
+
35
+ expectedHeaders.forEach((expectedHeader, index) => {
36
+ const withinHeader = within(tableHeaders[index]);
37
+ if (expectedHeader === "checkbox") {
38
+ expect(withinHeader.getByRole("checkbox")).toBeDefined();
39
+ } else {
40
+ const paragraph = withinHeader.getByRole("paragraph");
41
+ expect(paragraph.textContent?.trim()).toBe(expectedHeader);
42
+ }
43
+ });
44
+ });
45
+
46
+ it("should have 11 rows of data", () => {
47
+ expect(tableRows).toHaveLength(11);
48
+ });
49
+
50
+ it("should have data in first row", () => {
51
+ const tableRow1 = within(getTableBody(table)).getAllByRole("row")[0];
52
+
53
+ const tableRowCells = within(tableRow1).getAllByRole("cell");
54
+
55
+ const expectedCellsTextContent = ["12345", "Name 1", "Link 2", "40"];
56
+
57
+ tableRowCells.forEach((tableRowCell, index) => {
58
+ expect(tableRowCell).toHaveTextContent(expectedCellsTextContent[index]);
59
+ });
60
+ });
61
+
62
+ it("should not have pagination", () => {
63
+ expect(screen.queryByTestId(Selectors.PAGINATION.ROOT)).not.toBeInTheDocument();
64
+ });
65
+
66
+ it("should not have row selection", () => {
67
+ tableRows.forEach((tableRow) => {
68
+ const checkbox = within(tableRow).queryByRole("checkbox");
69
+ expect(checkbox).not.toBeInTheDocument();
70
+ });
71
+ });
72
+
73
+ it("should not have table header filters enabled", () => {
74
+ const withinTableHead = within(getTableHead(table));
75
+ expect(withinTableHead.queryByRole("combobox")).not.toBeInTheDocument();
76
+ expect(withinTableHead.queryByRole("search")).not.toBeInTheDocument();
77
+ });
78
+
79
+ it("should not have sorting enabled", () => {
80
+ const withinTableHead = within(getTableHead(table));
81
+ expect(withinTableHead.queryByRole("button")).not.toBeInTheDocument();
82
+ });
83
+
84
+ it("should not have tool bar", () => {
85
+ expect(screen.queryByTestId(Selectors.TOOLBAR.ROOT)).not.toBeInTheDocument();
86
+ });
87
+
88
+ it("should be accessible", async () => {
89
+ const results = await axe(container);
90
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
91
+ // @ts-ignore
92
+ expect(results).toHaveNoViolations();
93
+ });
94
+
95
+ it("should be accessible", async () => {
96
+ const results = await axe(container);
97
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
98
+ // @ts-ignore
99
+ expect(results).toHaveNoViolations();
100
+ });
101
+ });
102
+
103
+ describe("Empty table", () => {
104
+ beforeEach(() => {
105
+ const tableData: TableDataSmall[] = [];
106
+ container = render(
107
+ <Table
108
+ columns={createColumnDefSmall()}
109
+ data={tableData}
110
+ emptyTableCopy={{
111
+ title: "Title empty table",
112
+ description: "Body text explaining why the table is empty and what the user can do.",
113
+ }}
114
+ emptyTableHeadingTag="h2"
115
+ emptyTableIcon={<IllustrativeIconSearchQuestionDuocolor />}
116
+ />
117
+ ).container;
118
+
119
+ table = screen.getByRole("table");
120
+ tableHeaders = within(getTableHead(table)).getAllByRole("columnheader");
121
+
122
+ tableRows = within(getTableBody(table)).getAllByRole("row");
123
+ });
124
+
125
+ it("should have 1 row", () => {
126
+ expect(tableRows).toHaveLength(1);
127
+ });
128
+
129
+ it("should be accessible", async () => {
130
+ const results = await axe(container);
131
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
132
+ // @ts-ignore
133
+ expect(results).toHaveNoViolations();
134
+ });
135
+
136
+ describe("Within the empty row", () => {
137
+ it("should have a empty table title", () => {
138
+ const emptyTableTitle = within(tableRows[0]).getByTestId(Selectors.EMPTY_TABLE.TITLE);
139
+ expect(emptyTableTitle).toHaveTextContent("Title empty table");
140
+ });
141
+
142
+ it("should have a empty table description", () => {
143
+ const emptyTableDescription = within(tableRows[0]).getByTestId(
144
+ Selectors.EMPTY_TABLE.DESCRIPTION
145
+ );
146
+ expect(emptyTableDescription).toHaveTextContent(
147
+ "Body text explaining why the table is empty and what the user can do."
148
+ );
149
+ });
150
+ });
151
+ });
152
+
153
+ describe("Export with one format", () => {
154
+ it("should send an event when clicking on the export button", async () => {
155
+ const onExportDataMock = vi.fn();
156
+ render(
157
+ <Table
158
+ columns={createColumnDefSmall()}
159
+ data={tableDataSmall}
160
+ exportFormats="csv"
161
+ onExportData={onExportDataMock}
162
+ enableToolbar={true}
163
+ toolbarCopy={{
164
+ buttons: {
165
+ export: "Export",
166
+ },
167
+ ariaLabels: {
168
+ export: "Export csv file",
169
+ },
170
+ }}
171
+ />
172
+ );
173
+
174
+ const toolbar = screen.getByTestId(Selectors.TOOLBAR.ROOT);
175
+ const exportButton = within(toolbar).getByTestId(Selectors.TOOLBAR.EXPORT_BUTTON);
176
+
177
+ await userEvent.click(exportButton);
178
+ expect(onExportDataMock).toHaveBeenCalledWith("csv");
179
+ });
180
+ });
181
+
182
+ describe("Expand table", () => {
183
+ it("should have an expand button", async () => {
184
+ const onToggleExpandMock = vi.fn();
185
+
186
+ render(
187
+ <Table
188
+ columns={createColumnDefSmall()}
189
+ data={tableDataSmall}
190
+ enableToolbar={true}
191
+ toolbarCopy={{
192
+ buttons: {
193
+ expand: "Expand",
194
+ },
195
+ ariaLabels: {
196
+ expand: "Expand table",
197
+ },
198
+ }}
199
+ onToggleExpand={onToggleExpandMock}
200
+ />
201
+ );
202
+
203
+ const expandButton = screen.getByTestId(Selectors.TOOLBAR.EXPAND_BUTTON);
204
+
205
+ await userEvent.click(expandButton);
206
+ expect(onToggleExpandMock).toHaveBeenCalled();
207
+ });
208
+ });
209
+
210
+ describe("Settings drawer", () => {
211
+ it("should have an settings button", async () => {
212
+ render(
213
+ <Table
214
+ columns={createColumnDefSmall()}
215
+ data={tableDataSmall}
216
+ enableToolbar={true}
217
+ settingsDrawerCopy={copy.settingsDrawer}
218
+ toolbarCopy={{
219
+ buttons: {
220
+ settings: "Settings",
221
+ },
222
+ ariaLabels: {
223
+ settings: "Settings",
224
+ },
225
+ }}
226
+ />
227
+ );
228
+
229
+ const settingsButton = screen.getByTestId(Selectors.TOOLBAR.SETTINGS_BUTTON);
230
+
231
+ expect(settingsButton).toBeInTheDocument();
232
+ });
233
+ });
234
+
235
+ describe("Loading skeletons", () => {
236
+ let rerender: (ui: React.ReactNode) => void;
237
+
238
+ beforeEach(() => {
239
+ const { container: renderContainer, rerender: renderRerender } = render(
240
+ <Table
241
+ columns={createColumnDefSmall()}
242
+ data={tableDataSmall}
243
+ loading={true}
244
+ skeletonRows={11}
245
+ />
246
+ );
247
+ container = renderContainer;
248
+ rerender = renderRerender;
249
+
250
+ table = screen.getByRole("table");
251
+ tableHeaders = within(getTableHead(table)).getAllByRole("columnheader");
252
+
253
+ tableRows = within(getTableBody(table)).getAllByRole("row");
254
+ });
255
+
256
+ it("should have 10 rows", () => {
257
+ expect(tableRows).toHaveLength(11);
258
+ });
259
+
260
+ it("should have 4 headers", () => {
261
+ expect(tableHeaders).toHaveLength(4);
262
+ });
263
+
264
+ it("should have 4 skeletons in a row", () => {
265
+ const tableRow1 = within(getTableBody(table)).getAllByRole("row")[0];
266
+
267
+ const tableRowCells = within(tableRow1).getAllByRole("cell");
268
+
269
+ expect(tableRowCells).toHaveLength(4);
270
+ tableRowCells.forEach((tableRowCell) => {
271
+ const skeleton = within(tableRowCell).getByTestId(Selectors.SKELETON);
272
+ expect(skeleton).toBeInTheDocument();
273
+ });
274
+ });
275
+
276
+ it("should be accessible", async () => {
277
+ const results = await axe(container);
278
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
279
+ // @ts-ignore
280
+ expect(results).toHaveNoViolations();
281
+ });
282
+
283
+ describe("When loading is false", () => {
284
+ beforeEach(() => {
285
+ rerender(<Table columns={createColumnDefSmall()} data={tableDataSmall} loading={false} />);
286
+ });
287
+
288
+ it("should show table data", () => {
289
+ const tableBody = getTableBody(table);
290
+ tableRows = within(tableBody).getAllByRole("row");
291
+
292
+ expect(tableRows).toHaveLength(11);
293
+
294
+ const firstDataRow = tableRows[0];
295
+ const dataCells = within(firstDataRow).getAllByRole("cell");
296
+
297
+ expect(dataCells[0]).toHaveTextContent("12345"); // ID
298
+ expect(dataCells[1]).toHaveTextContent("Name 1"); // Name
299
+ expect(dataCells[2]).toHaveTextContent("Link 2"); // Link
300
+ expect(dataCells[3]).toHaveTextContent("40"); // Age
301
+
302
+ expect(screen.queryByTestId(Selectors.SKELETON)).not.toBeInTheDocument();
303
+ });
304
+ });
305
+ });
306
+
307
+ describe("Accessibility", () => {
308
+ it("is accessible", async () => {
309
+ const { container } = render(
310
+ <Table columns={createColumnDefSmall()} data={tableDataSmall} />
311
+ );
312
+ const results = await axe(container);
313
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
314
+ // @ts-ignore
315
+ expect(results).toHaveNoViolations();
316
+ });
317
+ });
318
+ });