@purpurds/table 8.6.0 → 8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@purpurds/table",
3
- "version": "8.6.0",
3
+ "version": "8.7.0",
4
4
  "license": "AGPL-3.0-only",
5
5
  "main": "./dist/table.cjs.js",
6
6
  "types": "./dist/table.d.ts",
@@ -21,22 +21,22 @@
21
21
  "@dnd-kit/utilities": "~3.2.2",
22
22
  "@tanstack/react-table": "~8.21.2",
23
23
  "classnames": "~2.5.0",
24
- "@purpurds/heading": "8.6.0",
25
- "@purpurds/button": "8.6.0",
26
- "@purpurds/drawer": "8.6.0",
27
- "@purpurds/icon": "8.6.0",
28
- "@purpurds/link": "8.6.0",
29
- "@purpurds/select": "8.6.0",
30
- "@purpurds/text-field": "8.6.0",
31
- "@purpurds/badge": "8.6.0",
32
- "@purpurds/skeleton": "8.6.0",
33
- "@purpurds/cta-link": "8.6.0",
34
- "@purpurds/toggle": "8.6.0",
35
- "@purpurds/tokens": "8.6.0",
36
- "@purpurds/visually-hidden": "8.6.0",
37
- "@purpurds/checkbox": "8.6.0",
38
- "@purpurds/paragraph": "8.6.0",
39
- "@purpurds/tooltip": "8.6.0"
24
+ "@purpurds/badge": "8.7.0",
25
+ "@purpurds/button": "8.7.0",
26
+ "@purpurds/cta-link": "8.7.0",
27
+ "@purpurds/drawer": "8.7.0",
28
+ "@purpurds/heading": "8.7.0",
29
+ "@purpurds/icon": "8.7.0",
30
+ "@purpurds/paragraph": "8.7.0",
31
+ "@purpurds/link": "8.7.0",
32
+ "@purpurds/select": "8.7.0",
33
+ "@purpurds/skeleton": "8.7.0",
34
+ "@purpurds/text-field": "8.7.0",
35
+ "@purpurds/checkbox": "8.7.0",
36
+ "@purpurds/visually-hidden": "8.7.0",
37
+ "@purpurds/tooltip": "8.7.0",
38
+ "@purpurds/tokens": "8.7.0",
39
+ "@purpurds/toggle": "8.7.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@rushstack/eslint-patch": "~1.10.0",
@@ -60,13 +60,13 @@
60
60
  "vitest-axe": "~0.1.0",
61
61
  "vitest-canvas-mock": "~0.3.3",
62
62
  "vitest": "^3.1.2",
63
- "@purpurds/autocomplete": "8.6.0",
64
- "@purpurds/illustrative-icon": "8.6.0",
63
+ "@purpurds/autocomplete": "8.7.0",
65
64
  "@purpurds/component-rig": "1.0.0",
66
- "@purpurds/grid": "8.6.0",
67
- "@purpurds/label": "8.6.0",
68
- "@purpurds/pagination": "8.6.0",
69
- "@purpurds/listbox": "8.6.0"
65
+ "@purpurds/label": "8.7.0",
66
+ "@purpurds/illustrative-icon": "8.7.0",
67
+ "@purpurds/listbox": "8.7.0",
68
+ "@purpurds/grid": "8.7.0",
69
+ "@purpurds/pagination": "8.7.0"
70
70
  },
71
71
  "scripts": {
72
72
  "build:dev": "vite",
@@ -18,7 +18,7 @@ export const DateCell = <TData extends RowData>({ cell }: DateCellProps<TData>)
18
18
  return <EmptyCell />;
19
19
  }
20
20
 
21
- const showTime = cell.column.columnDef.meta?.showTime || true;
21
+ const showTime = cell.column.columnDef.meta?.showTime ?? true;
22
22
 
23
23
  const dateObj = new Date(dateString);
24
24
  const date = dateObj.toLocaleDateString("sv-SE");
@@ -2,14 +2,12 @@ import React from "react";
2
2
  import { render, screen } from "@testing-library/react";
3
3
  import { axe } from "vitest-axe";
4
4
 
5
- import { EmptyTableContent, LoadingTableContent, NormalTableContent } from "./table-content";
5
+ import { TableContent } from "./table-content";
6
6
 
7
- // Mock tanstack table
8
7
  const mockTanstackTable = {
9
8
  getVisibleLeafColumns: vi.fn(() => [{ id: "col1" }, { id: "col2" }]),
10
- } as unknown as Parameters<typeof NormalTableContent>[0]["tanstackTable"];
9
+ } as unknown as Parameters<typeof TableContent>[0]["tanstackTable"];
11
10
 
12
- // Mock row data
13
11
  const mockTableRows = [
14
12
  {
15
13
  id: "row1",
@@ -39,467 +37,82 @@ const mockTableRows = [
39
37
  },
40
38
  ],
41
39
  },
42
- {
43
- id: "row2",
44
- getIsSelected: () => true,
45
- getVisibleCells: () => [
46
- {
47
- id: "cell3",
48
- column: {
49
- id: "col1",
50
- getIsFirstColumn: () => true,
51
- getIsLastColumn: () => false,
52
- columnDef: { meta: { cellType: "default" } },
53
- },
54
- getValue: () => "Value 3",
55
- getContext: () => ({ getValue: () => "Value 3" }),
56
- },
57
- {
58
- id: "cell4",
59
- column: {
60
- id: "col2",
61
- getIsFirstColumn: () => false,
62
- getIsLastColumn: () => true,
63
- columnDef: { meta: { cellType: "default" } },
64
- },
65
- getValue: () => "Value 4",
66
- getContext: () => ({ getValue: () => "Value 4" }),
67
- },
68
- ],
69
- },
70
- ] as unknown as Parameters<typeof NormalTableContent>[0]["tableRows"];
71
-
72
- const mockRenderTableHeaders = () => (
73
- <tr>
74
- <th>Header 1</th>
75
- <th>Header 2</th>
76
- </tr>
40
+ ] as unknown as Parameters<typeof TableContent>[0]["tableRows"];
41
+
42
+ const mockTableHeader = (
43
+ <thead>
44
+ <tr>
45
+ <th>Header 1</th>
46
+ <th>Header 2</th>
47
+ </tr>
48
+ </thead>
77
49
  );
78
50
 
79
- describe("Table Content Components with Drag and Drop", () => {
80
- describe("NormalTableContent", () => {
81
- const defaultProps = {
82
- tanstackTable: mockTanstackTable,
83
- tableRows: mockTableRows,
84
- showColumnFiltersEnabled: false,
85
- fullWidth: false,
86
- renderTableHeaders: mockRenderTableHeaders,
87
- stickyFirstColumn: false,
88
- getStickyColumn: () => false,
89
- isScrolled: false,
90
- showBorder: () => false,
91
- enableColumnDrag: false,
92
- activeId: null,
93
- };
94
-
95
- it("should render table with drag disabled by default", () => {
96
- render(<NormalTableContent {...defaultProps} />);
97
-
98
- const table = screen.getByRole("table");
99
- expect(table).toBeInTheDocument();
100
- expect(table.className).toContain("purpur-table__table");
101
- });
102
-
103
- it("should render table with drag enabled", () => {
104
- render(<NormalTableContent {...defaultProps} enableColumnDrag={true} />);
51
+ describe("Table Content with unified component", () => {
52
+ const defaultProps = {
53
+ tanstackTable: mockTanstackTable,
54
+ tableRows: mockTableRows,
55
+ fullWidth: false,
56
+ tableHeader: mockTableHeader,
57
+ stickyFirstColumn: false,
58
+ getStickyColumn: () => false,
59
+ isScrolled: false,
60
+ showBorder: () => false,
61
+ enableColumnDrag: false,
62
+ activeId: null,
63
+ };
105
64
 
65
+ describe("NormalTableContent", () => {
66
+ it("should render table", () => {
67
+ render(<TableContent {...defaultProps} />);
106
68
  const table = screen.getByRole("table");
107
69
  expect(table).toBeInTheDocument();
108
70
  });
109
71
 
110
72
  it("should apply full width class when fullWidth is true", () => {
111
- render(<NormalTableContent {...defaultProps} fullWidth={true} />);
112
-
73
+ render(<TableContent {...defaultProps} fullWidth={true} />);
113
74
  const table = screen.getByRole("table");
114
75
  expect(table.className).toContain("purpur-table__table--full-width");
115
76
  });
116
77
 
117
- it("should render correct number of rows", () => {
118
- render(<NormalTableContent {...defaultProps} />);
119
-
120
- const rows = screen.getAllByRole("row");
121
- // Should have header row + data rows
122
- expect(rows.length).toBeGreaterThan(mockTableRows.length);
123
- });
124
-
125
- it("should pass enableColumnDrag prop to table cells", () => {
126
- render(<NormalTableContent {...defaultProps} enableColumnDrag={true} />);
127
-
128
- // All cells should receive the enableColumnDrag prop
129
- const table = screen.getByRole("table");
130
- expect(table).toBeInTheDocument();
131
- });
132
-
133
- it("should pass draggingActive prop to cells when activeId matches", () => {
134
- render(<NormalTableContent {...defaultProps} enableColumnDrag={true} activeId="col1" />);
135
-
136
- const table = screen.getByRole("table");
137
- expect(table).toBeInTheDocument();
138
- });
139
-
140
- it("should handle sticky columns with drag enabled", () => {
141
- render(
142
- <NormalTableContent
143
- {...defaultProps}
144
- stickyFirstColumn={true}
145
- getStickyColumn={(index) => index === 0}
146
- enableColumnDrag={true}
147
- />
148
- );
149
-
150
- const table = screen.getByRole("table");
151
- expect(table).toBeInTheDocument();
152
- });
153
-
154
- it("should handle scrolled state with drag enabled", () => {
155
- render(<NormalTableContent {...defaultProps} isScrolled={true} enableColumnDrag={true} />);
156
-
157
- const table = screen.getByRole("table");
158
- expect(table).toBeInTheDocument();
159
- });
160
-
161
- it("should handle border showing with drag enabled", () => {
162
- render(
163
- <NormalTableContent
164
- {...defaultProps}
165
- showBorder={(index) => index === 0}
166
- enableColumnDrag={true}
167
- />
168
- );
169
-
170
- const table = screen.getByRole("table");
171
- expect(table).toBeInTheDocument();
172
- });
173
-
174
- it("should be accessible with drag enabled", async () => {
175
- const { container } = render(
176
- <NormalTableContent {...defaultProps} enableColumnDrag={true} />
177
- );
178
-
78
+ it("should be accessible", async () => {
79
+ const { container } = render(<TableContent {...defaultProps} />);
179
80
  const results = await axe(container);
180
81
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
181
82
  // @ts-ignore
182
83
  expect(results).toHaveNoViolations();
183
84
  });
184
-
185
- it("should handle empty table rows", () => {
186
- render(<NormalTableContent {...defaultProps} tableRows={[]} enableColumnDrag={true} />);
187
-
188
- const table = screen.getByRole("table");
189
- expect(table).toBeInTheDocument();
190
- });
191
-
192
- it("should handle selected rows with drag enabled", () => {
193
- const selectedRows = mockTableRows.map(
194
- (row: Parameters<typeof NormalTableContent>[0]["tableRows"][0]) => ({
195
- ...row,
196
- getIsSelected: () => true,
197
- })
198
- );
199
-
200
- render(
201
- <NormalTableContent {...defaultProps} tableRows={selectedRows} enableColumnDrag={true} />
202
- );
203
-
204
- const table = screen.getByRole("table");
205
- expect(table).toBeInTheDocument();
206
- });
207
85
  });
208
86
 
209
- describe("LoadingTableContent", () => {
210
- const defaultProps = {
211
- tanstackTable: mockTanstackTable,
212
- tableRows: [],
213
- showColumnFiltersEnabled: false,
214
- fullWidth: false,
215
- renderTableHeaders: mockRenderTableHeaders,
216
- skeletonRows: 5,
217
- getStickyColumn: () => false,
218
- stickyFirstColumn: false,
219
- isScrolled: false,
220
- showBorder: () => false,
221
- getColumnWidths: () => ["100px", "150px"],
222
- };
223
-
224
- it("should render loading table with drag context support", () => {
225
- render(<LoadingTableContent {...defaultProps} />);
226
-
227
- const table = screen.getByRole("table");
228
- expect(table).toBeInTheDocument();
229
- expect(table.className).toContain("purpur-table__table");
230
- });
231
-
232
- it("should apply full width class when fullWidth is true", () => {
233
- render(<LoadingTableContent {...defaultProps} fullWidth={true} />);
234
-
235
- const table = screen.getByRole("table");
236
- expect(table.className).toContain("purpur-table__table--full-width");
237
- });
238
-
239
- it("should render with column filters enabled", () => {
240
- render(<LoadingTableContent {...defaultProps} showColumnFiltersEnabled={true} />);
241
-
242
- const table = screen.getByRole("table");
243
- expect(table).toBeInTheDocument();
244
- });
245
-
246
- it("should handle sticky columns in loading state", () => {
87
+ describe("Loading state", () => {
88
+ it("should render loading state", () => {
247
89
  render(
248
- <LoadingTableContent
90
+ <TableContent
249
91
  {...defaultProps}
250
- stickyFirstColumn={true}
251
- getStickyColumn={(index) => index === 0}
92
+ loading={true}
93
+ skeletonRows={5}
94
+ getColumnWidths={() => []}
252
95
  />
253
96
  );
254
-
255
- const table = screen.getByRole("table");
256
- expect(table).toBeInTheDocument();
257
- });
258
-
259
- it("should handle scrolled state in loading", () => {
260
- render(<LoadingTableContent {...defaultProps} isScrolled={true} />);
261
-
262
- const table = screen.getByRole("table");
263
- expect(table).toBeInTheDocument();
264
- });
265
-
266
- it("should pass correct skeleton row count", () => {
267
- const skeletonRows = 3;
268
- render(<LoadingTableContent {...defaultProps} skeletonRows={skeletonRows} />);
269
-
270
97
  const table = screen.getByRole("table");
271
98
  expect(table).toBeInTheDocument();
272
99
  });
273
-
274
- it("should pass column widths to loading rows", () => {
275
- const columnWidths = ["120px", "180px", "200px"];
276
- render(<LoadingTableContent {...defaultProps} getColumnWidths={() => columnWidths} />);
277
-
278
- const table = screen.getByRole("table");
279
- expect(table).toBeInTheDocument();
280
- });
281
-
282
- it("should be accessible in loading state", async () => {
283
- const { container } = render(<LoadingTableContent {...defaultProps} />);
284
-
285
- const results = await axe(container);
286
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
287
- // @ts-ignore
288
- expect(results).toHaveNoViolations();
289
- });
290
100
  });
291
101
 
292
- describe("EmptyTableContent", () => {
293
- const defaultProps = {
294
- tanstackTable: mockTanstackTable,
295
- tableRows: [],
296
- showColumnFiltersEnabled: false,
297
- fullWidth: false,
298
- renderTableHeaders: mockRenderTableHeaders,
299
- variant: "primary" as const,
300
- emptyTableHeadingTag: "h2" as const,
301
- emptyTableCopy: {
302
- title: "No data available",
303
- description: "There are no items to display in this table.",
304
- },
305
- emptyTableIcon: <div data-testid="empty-icon">Empty Icon</div>,
306
- };
307
-
308
- it("should render empty table with drag context support", () => {
309
- render(<EmptyTableContent {...defaultProps} />);
310
-
311
- const table = screen.getByRole("table");
312
- expect(table).toBeInTheDocument();
313
- expect(table.className).toContain("purpur-table__table");
314
- });
315
-
316
- it("should apply full width class when fullWidth is true", () => {
317
- render(<EmptyTableContent {...defaultProps} fullWidth={true} />);
318
-
319
- const table = screen.getByRole("table");
320
- expect(table.className).toContain("purpur-table__table--full-width");
321
- });
322
-
323
- it("should render with column filters enabled", () => {
324
- render(<EmptyTableContent {...defaultProps} showColumnFiltersEnabled={true} />);
325
-
326
- const table = screen.getByRole("table");
327
- expect(table).toBeInTheDocument();
328
- });
329
-
330
- it("should display empty table content", () => {
331
- render(<EmptyTableContent {...defaultProps} />);
332
-
333
- expect(screen.getByText(defaultProps.emptyTableCopy.title)).toBeInTheDocument();
334
- expect(screen.getByText(defaultProps.emptyTableCopy.description)).toBeInTheDocument();
335
- expect(screen.getByTestId("empty-icon")).toBeInTheDocument();
336
- });
337
-
338
- it("should handle different variants", () => {
339
- const variants = ["primary", "secondary"] as const;
340
-
341
- variants.forEach((variant) => {
342
- const { unmount } = render(<EmptyTableContent {...defaultProps} variant={variant} />);
343
-
344
- const table = screen.getByRole("table");
345
- expect(table).toBeInTheDocument();
346
-
347
- unmount();
348
- });
349
- });
350
-
351
- it("should handle different heading tags", () => {
352
- const headingTags = ["h1", "h2", "h3", "h4", "h5", "h6"] as const;
353
-
354
- headingTags.forEach((tag) => {
355
- const { unmount } = render(
356
- <EmptyTableContent {...defaultProps} emptyTableHeadingTag={tag} />
357
- );
358
-
359
- const table = screen.getByRole("table");
360
- expect(table).toBeInTheDocument();
361
-
362
- unmount();
363
- });
364
- });
365
-
366
- it("should handle missing icon", () => {
367
- render(<EmptyTableContent {...defaultProps} emptyTableIcon={undefined} />);
368
-
369
- const table = screen.getByRole("table");
370
- expect(table).toBeInTheDocument();
371
- expect(screen.queryByTestId("empty-icon")).not.toBeInTheDocument();
372
- });
373
-
374
- it("should calculate correct colSpan from tanstack table", () => {
375
- const mockTableWithMoreColumns = {
376
- getVisibleLeafColumns: vi.fn(() => [
377
- { id: "col1" },
378
- { id: "col2" },
379
- { id: "col3" },
380
- { id: "col4" },
381
- ]),
382
- } as unknown as Parameters<typeof EmptyTableContent>[0]["tanstackTable"];
383
-
384
- render(<EmptyTableContent {...defaultProps} tanstackTable={mockTableWithMoreColumns} />);
385
-
386
- const table = screen.getByRole("table");
387
- expect(table).toBeInTheDocument();
388
- });
389
-
390
- it("should be accessible in empty state", async () => {
391
- const { container } = render(<EmptyTableContent {...defaultProps} />);
392
-
393
- const results = await axe(container);
394
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
395
- // @ts-ignore
396
- expect(results).toHaveNoViolations();
397
- });
398
-
399
- it("should handle empty copy strings", () => {
102
+ describe("Empty state", () => {
103
+ it("should render empty state", () => {
400
104
  render(
401
- <EmptyTableContent
105
+ <TableContent
402
106
  {...defaultProps}
403
- emptyTableCopy={{
404
- title: "",
405
- description: "",
406
- }}
407
- />
408
- );
409
-
410
- const table = screen.getByRole("table");
411
- expect(table).toBeInTheDocument();
412
- });
413
- });
414
-
415
- describe("Integration between content types", () => {
416
- it("should maintain consistent table structure across all content types", () => {
417
- const baseProps = {
418
- tanstackTable: mockTanstackTable,
419
- showColumnFiltersEnabled: false,
420
- fullWidth: true,
421
- renderTableHeaders: mockRenderTableHeaders,
422
- };
423
-
424
- // Normal content
425
- const { container: normalContainer, unmount: unmountNormal } = render(
426
- <NormalTableContent
427
- {...baseProps}
428
- tableRows={mockTableRows}
429
- stickyFirstColumn={false}
430
- getStickyColumn={() => false}
431
- isScrolled={false}
432
- showBorder={() => false}
433
- enableColumnDrag={false}
434
- activeId={null}
435
- />
436
- );
437
-
438
- const normalTable = normalContainer.querySelector("table");
439
- expect(normalTable?.className).toContain("purpur-table__table");
440
- expect(normalTable?.className).toContain("purpur-table__table--full-width");
441
- unmountNormal();
442
-
443
- // Loading content
444
- const { container: loadingContainer, unmount: unmountLoading } = render(
445
- <LoadingTableContent
446
- {...baseProps}
447
- tableRows={[]}
448
- skeletonRows={5}
449
- getStickyColumn={() => false}
450
- stickyFirstColumn={false}
451
- isScrolled={false}
452
- showBorder={() => false}
453
- getColumnWidths={() => []}
454
- />
455
- );
456
-
457
- const loadingTable = loadingContainer.querySelector("table");
458
- expect(loadingTable?.className).toContain("purpur-table__table");
459
- expect(loadingTable?.className).toContain("purpur-table__table--full-width");
460
- unmountLoading();
461
-
462
- // Empty content
463
- const { container: emptyContainer, unmount: unmountEmpty } = render(
464
- <EmptyTableContent
465
- {...baseProps}
466
107
  tableRows={[]}
108
+ isEmptyTable={true}
467
109
  variant="primary"
468
110
  emptyTableHeadingTag="h2"
469
- emptyTableCopy={{
470
- title: "Empty",
471
- description: "No data",
472
- }}
111
+ emptyTableCopy={{ title: "Empty", description: "No data" }}
473
112
  />
474
113
  );
475
-
476
- const emptyTable = emptyContainer.querySelector("table");
477
- expect(emptyTable?.className).toContain("purpur-table__table");
478
- expect(emptyTable?.className).toContain("purpur-table__table--full-width");
479
- unmountEmpty();
480
- });
481
-
482
- it("should handle drag and drop props consistently", () => {
483
- // Test that drag-related props don't break any content type
484
- expect(() => {
485
- render(
486
- <div>
487
- <NormalTableContent
488
- tanstackTable={mockTanstackTable}
489
- tableRows={mockTableRows}
490
- showColumnFiltersEnabled={false}
491
- fullWidth={false}
492
- renderTableHeaders={mockRenderTableHeaders}
493
- stickyFirstColumn={false}
494
- getStickyColumn={() => false}
495
- isScrolled={false}
496
- showBorder={() => false}
497
- enableColumnDrag={true}
498
- activeId="col1"
499
- />
500
- </div>
501
- );
502
- }).not.toThrow();
114
+ const table = screen.getByRole("table");
115
+ expect(table).toBeInTheDocument();
503
116
  });
504
117
  });
505
118
  });