@mattilsynet/design 3.2.9 → 3.3.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 (98) hide show
  1. package/mtds/ai/AGENTS.md +892 -0
  2. package/mtds/ai/alert.mdx +63 -0
  3. package/mtds/ai/alert.stories.tsx +128 -0
  4. package/mtds/ai/analytics.mdx +185 -0
  5. package/mtds/ai/app.mdx +60 -0
  6. package/mtds/ai/app.stories.tsx +897 -0
  7. package/mtds/ai/atlas.mdx +82 -0
  8. package/mtds/ai/atlas.stories.tsx +424 -0
  9. package/mtds/ai/avatar.mdx +45 -0
  10. package/mtds/ai/avatar.stories.tsx +109 -0
  11. package/mtds/ai/badge.mdx +70 -0
  12. package/mtds/ai/badge.stories.tsx +122 -0
  13. package/mtds/ai/breadcrumbs.mdx +36 -0
  14. package/mtds/ai/breadcrumbs.stories.tsx +158 -0
  15. package/mtds/ai/button.mdx +179 -0
  16. package/mtds/ai/button.stories.tsx +440 -0
  17. package/mtds/ai/card.mdx +51 -0
  18. package/mtds/ai/card.stories.tsx +469 -0
  19. package/mtds/ai/chart.mdx +67 -0
  20. package/mtds/ai/chart.stories.tsx +519 -0
  21. package/mtds/ai/chip.mdx +71 -0
  22. package/mtds/ai/chip.stories.tsx +211 -0
  23. package/mtds/ai/details.mdx +33 -0
  24. package/mtds/ai/details.stories.tsx +91 -0
  25. package/mtds/ai/dialog.mdx +38 -0
  26. package/mtds/ai/dialog.stories.tsx +373 -0
  27. package/mtds/ai/divider.mdx +19 -0
  28. package/mtds/ai/divider.stories.tsx +50 -0
  29. package/mtds/ai/errorsummary.mdx +26 -0
  30. package/mtds/ai/errorsummary.stories.tsx +137 -0
  31. package/mtds/ai/field.mdx +86 -0
  32. package/mtds/ai/field.stories.tsx +863 -0
  33. package/mtds/ai/fieldset.mdx +126 -0
  34. package/mtds/ai/fieldset.stories.tsx +298 -0
  35. package/mtds/ai/fileupload.mdx +16 -0
  36. package/mtds/ai/fileupload.stories.tsx +126 -0
  37. package/mtds/ai/helptext.mdx +24 -0
  38. package/mtds/ai/helptext.stories.tsx +106 -0
  39. package/mtds/ai/input.mdx +223 -0
  40. package/mtds/ai/input.stories.tsx +352 -0
  41. package/mtds/ai/law.mdx +115 -0
  42. package/mtds/ai/law.stories.tsx +168 -0
  43. package/mtds/ai/layout.mdx +145 -0
  44. package/mtds/ai/layout.stories.tsx +443 -0
  45. package/mtds/ai/link.mdx +45 -0
  46. package/mtds/ai/link.stories.tsx +44 -0
  47. package/mtds/ai/logo.mdx +86 -0
  48. package/mtds/ai/logo.stories.tsx +146 -0
  49. package/mtds/ai/pagination.mdx +136 -0
  50. package/mtds/ai/pagination.stories.tsx +404 -0
  51. package/mtds/ai/popover.mdx +86 -0
  52. package/mtds/ai/popover.stories.tsx +355 -0
  53. package/mtds/ai/print.mdx +96 -0
  54. package/mtds/ai/print.stories.tsx +839 -0
  55. package/mtds/ai/progress.mdx +41 -0
  56. package/mtds/ai/progress.stories.tsx +141 -0
  57. package/mtds/ai/skeleton.mdx +26 -0
  58. package/mtds/ai/skeleton.stories.tsx +131 -0
  59. package/mtds/ai/spinner.mdx +26 -0
  60. package/mtds/ai/spinner.stories.tsx +72 -0
  61. package/mtds/ai/steps.mdx +37 -0
  62. package/mtds/ai/steps.stories.tsx +568 -0
  63. package/mtds/ai/table.mdx +124 -0
  64. package/mtds/ai/table.stories.tsx +1715 -0
  65. package/mtds/ai/tabs.mdx +106 -0
  66. package/mtds/ai/tabs.stories.tsx +159 -0
  67. package/mtds/ai/tag.mdx +49 -0
  68. package/mtds/ai/tag.stories.tsx +111 -0
  69. package/mtds/ai/toast.mdx +67 -0
  70. package/mtds/ai/toast.stories.tsx +215 -0
  71. package/mtds/ai/togglegroup.mdx +75 -0
  72. package/mtds/ai/togglegroup.stories.tsx +96 -0
  73. package/mtds/ai/tooltip.mdx +32 -0
  74. package/mtds/ai/tooltip.stories.tsx +34 -0
  75. package/mtds/ai/typography.mdx +67 -0
  76. package/mtds/ai/typography.stories.tsx +798 -0
  77. package/mtds/ai/validation.mdx +19 -0
  78. package/mtds/ai/validation.stories.tsx +45 -0
  79. package/mtds/app/app-observer.js +1 -1
  80. package/mtds/app/app-toggle.js +10 -26
  81. package/mtds/app/app-toggle.js.map +1 -1
  82. package/mtds/app/app-toggle2.js +26 -10
  83. package/mtds/app/app-toggle2.js.map +1 -1
  84. package/mtds/app/app.js +1 -1
  85. package/mtds/atlas/atlas-element.js +1 -1
  86. package/mtds/chart/chart-lines.js +19 -19
  87. package/mtds/chart/chart-lines.js.map +1 -1
  88. package/mtds/chart/chart.css.js +16 -1
  89. package/mtds/chart/chart.css.js.map +1 -1
  90. package/mtds/chart/chart.stories.d.ts +1 -0
  91. package/mtds/index.iife.js +32 -17
  92. package/mtds/index.js +21 -20
  93. package/mtds/index.js.map +1 -1
  94. package/mtds/package.json.js +1 -1
  95. package/mtds/styles.css +1 -1
  96. package/mtds/table/table-observer.js +26 -15
  97. package/mtds/table/table-observer.js.map +1 -1
  98. package/package.json +4 -2
@@ -0,0 +1,1715 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import {
3
+ type ColumnDef,
4
+ type ExpandedState,
5
+ flexRender,
6
+ getCoreRowModel,
7
+ getFilteredRowModel,
8
+ getPaginationRowModel,
9
+ getSortedRowModel,
10
+ type SortingState,
11
+ useReactTable,
12
+ } from "@tanstack/react-table";
13
+ import { Fragment, useEffect, useMemo, useState } from "react";
14
+ import { pagination } from "../";
15
+ import { Button, Field, Pagination, Table } from "../react";
16
+ import styles from "../styles.module.css";
17
+ import mockData from "./table.mockData";
18
+
19
+ const meta = {
20
+ title: "Designsystem/Table",
21
+ parameters: {
22
+ layout: "padded",
23
+ },
24
+ decorators: [
25
+ (Story) => (
26
+ <div className={styles.grid} data-gap="4">
27
+ <Story />
28
+ </div>
29
+ ),
30
+ ],
31
+ } satisfies Meta;
32
+
33
+ export default meta;
34
+
35
+ type Story = StoryObj<typeof meta>;
36
+ type ColumnKeys = keyof (typeof mockData)[0];
37
+ type ColumnsType = {
38
+ key: ColumnKeys;
39
+ label: string;
40
+ numeric?: boolean;
41
+ expand?: React.ReactNode;
42
+ }[];
43
+ type RowType = (typeof mockData)[0] & { expand?: React.ReactNode };
44
+
45
+ const mockDataSmall = mockData.slice(0, 4);
46
+ const mockExpand = mockData.slice(0, 10).map((row: RowType) => ({
47
+ ...row,
48
+ expand: (
49
+ <div>
50
+ Content {row.firstName} {row.lastName}
51
+ </div>
52
+ ),
53
+ }));
54
+
55
+ const mockColumns = [
56
+ { accessorKey: "firstName", header: "First name" },
57
+ { accessorKey: "lastName", header: "Last name" },
58
+ { header: "Age", accessorKey: "age" },
59
+ { header: "Visits", accessorKey: "visits" },
60
+ ];
61
+
62
+ const mobileDecorators: StoryObj["decorators"] = [
63
+ (Story) => {
64
+ useEffect(() => {
65
+ if (document.querySelector(".sbdocs-wrapper")) return; // Do not shrink in docs mode
66
+ const el = window.frameElement as HTMLElement;
67
+ const iframe = el?.nodeName === "IFRAME" ? el : undefined;
68
+ if (iframe) iframe.style.maxWidth = "400px";
69
+ return () => iframe?.removeAttribute("style");
70
+ }, []);
71
+
72
+ return <Story />;
73
+ },
74
+ ];
75
+
76
+ export const Default: Story = {
77
+ render: () => (
78
+ <table className={styles.table} aria-label="Example table">
79
+ <thead>
80
+ <tr>
81
+ <th>First name</th>
82
+ <th>Last name</th>
83
+ <th>Age</th>
84
+ <th>Visits</th>
85
+ </tr>
86
+ </thead>
87
+ <tbody>
88
+ <tr>
89
+ <td>Antoni</td>
90
+ <td>Foyston</td>
91
+ <td>74</td>
92
+ <td>128</td>
93
+ </tr>
94
+ <tr>
95
+ <td>Jenine</td>
96
+ <td>Healey</td>
97
+ <td>22</td>
98
+ <td>194</td>
99
+ </tr>
100
+ <tr>
101
+ <td>Leigh</td>
102
+ <td>Klein</td>
103
+ <td>26</td>
104
+ <td>114</td>
105
+ </tr>
106
+ <tr>
107
+ <td>Zara</td>
108
+ <td>Greenrodd</td>
109
+ <td>28</td>
110
+ <td>36</td>
111
+ </tr>
112
+ </tbody>
113
+ </table>
114
+ ),
115
+ };
116
+
117
+ export const React: Story = {
118
+ render: () => (
119
+ <Table aria-label="Example table">
120
+ <Table.Thead>
121
+ <Table.Tr>
122
+ <Table.Th>First name</Table.Th>
123
+ <Table.Th>Last name</Table.Th>
124
+ <Table.Th>Age</Table.Th>
125
+ <Table.Th>Visits</Table.Th>
126
+ </Table.Tr>
127
+ </Table.Thead>
128
+ <Table.Tbody>
129
+ <Table.Tr>
130
+ <Table.Td>Antoni</Table.Td>
131
+ <Table.Td>Foyston</Table.Td>
132
+ <Table.Td>74</Table.Td>
133
+ <Table.Td>128</Table.Td>
134
+ </Table.Tr>
135
+ <Table.Tr>
136
+ <Table.Td>Jenine</Table.Td>
137
+ <Table.Td>Healey</Table.Td>
138
+ <Table.Td>22</Table.Td>
139
+ <Table.Td>194</Table.Td>
140
+ </Table.Tr>
141
+ <Table.Tr>
142
+ <Table.Td>Leigh</Table.Td>
143
+ <Table.Td>Klein</Table.Td>
144
+ <Table.Td>26</Table.Td>
145
+ <Table.Td>114</Table.Td>
146
+ </Table.Tr>
147
+ <Table.Tr>
148
+ <Table.Td>Zara</Table.Td>
149
+ <Table.Td>Greenrodd</Table.Td>
150
+ <Table.Td>28</Table.Td>
151
+ <Table.Td>36</Table.Td>
152
+ </Table.Tr>
153
+ </Table.Tbody>
154
+ </Table>
155
+ ),
156
+ };
157
+
158
+ export const DefaultTanstack: Story = {
159
+ render: function Render(args) {
160
+ const isNumeric = ["age", "visits"];
161
+ const table = useReactTable({
162
+ getCoreRowModel: getCoreRowModel(),
163
+ data: mockDataSmall,
164
+ columns: mockColumns,
165
+ });
166
+
167
+ return (
168
+ <Table aria-label="Tanstack table" {...args}>
169
+ <Table.Thead>
170
+ {table.getHeaderGroups().map(({ id, headers }) => (
171
+ <Table.Tr key={id}>
172
+ {headers.map((header) => (
173
+ <Table.Th
174
+ key={header.id}
175
+ colSpan={header.colSpan}
176
+ data-numeric={isNumeric.includes(header.id)}
177
+ >
178
+ {flexRender(
179
+ header.column.columnDef.header,
180
+ header.getContext(),
181
+ )}
182
+ </Table.Th>
183
+ ))}
184
+ </Table.Tr>
185
+ ))}
186
+ </Table.Thead>
187
+ <Table.Tbody>
188
+ {table.getRowModel().rows.map((row) => (
189
+ <Table.Tr key={row.id}>
190
+ {row.getVisibleCells().map((cell) => (
191
+ <Table.Td
192
+ key={cell.id}
193
+ data-numeric={isNumeric.includes(cell.column.id)}
194
+ >
195
+ {cell.getValue() as React.ReactNode}
196
+ </Table.Td>
197
+ ))}
198
+ </Table.Tr>
199
+ ))}
200
+ </Table.Tbody>
201
+ </Table>
202
+ );
203
+ },
204
+ };
205
+
206
+ export const HeadingsSimple: Story = {
207
+ render: (args) => {
208
+ const columns: ColumnsType = [
209
+ { key: "firstName", label: "First name" },
210
+ { key: "lastName", label: "Last name" },
211
+ { key: "age", label: "Age", numeric: true },
212
+ { key: "visits", label: "Visits", numeric: true },
213
+ { key: "date", label: "Date", numeric: true },
214
+ ];
215
+
216
+ return (
217
+ <table className={styles.table} aria-label="Headings table" {...args}>
218
+ <thead>
219
+ <tr>
220
+ <th colSpan={2}>Name</th>
221
+ <th colSpan={3}>Stats</th>
222
+ </tr>
223
+ <tr>
224
+ {columns.map(({ label, numeric }) => (
225
+ <th key={label} data-numeric={numeric}>
226
+ {label}
227
+ </th>
228
+ ))}
229
+ </tr>
230
+ </thead>
231
+ <tbody>
232
+ {mockDataSmall.map((row) => (
233
+ <tr key={`${row.firstName}-${row.lastName}`}>
234
+ {columns.map(({ key, numeric }) => (
235
+ <td key={key} data-numeric={numeric}>
236
+ {key === "date"
237
+ ? new Date(Number(row[key])).toLocaleDateString()
238
+ : row[key]}
239
+ </td>
240
+ ))}
241
+ </tr>
242
+ ))}
243
+ </tbody>
244
+ </table>
245
+ );
246
+ },
247
+ };
248
+
249
+ export const HeadingsTanstack: Story = {
250
+ render: function Render(args) {
251
+ const columns: ColumnDef<RowType>[] = useMemo(
252
+ () => [
253
+ {
254
+ header: "Name",
255
+ columns: [
256
+ {
257
+ accessorKey: "firstName",
258
+ header: "First name",
259
+ },
260
+ {
261
+ accessorKey: "lastName",
262
+ header: "Last name",
263
+ },
264
+ ],
265
+ },
266
+ {
267
+ header: "Stats",
268
+ columns: [
269
+ {
270
+ header: "Age",
271
+ accessorKey: "age",
272
+ },
273
+ {
274
+ header: "Visits",
275
+ accessorKey: "visits",
276
+ },
277
+ {
278
+ header: "Date",
279
+ accessorKey: "date",
280
+ cell: (info) =>
281
+ new Date(Number(info.getValue())).toLocaleDateString(),
282
+ },
283
+ ],
284
+ },
285
+ ],
286
+ [],
287
+ );
288
+
289
+ const table = useReactTable({
290
+ getCoreRowModel: getCoreRowModel(),
291
+ data: mockDataSmall,
292
+ columns,
293
+ });
294
+
295
+ return (
296
+ <Table aria-label="Headings Tanstack table" {...args}>
297
+ <Table.Thead>
298
+ {table.getHeaderGroups().map(({ id, headers }) => (
299
+ <Table.Tr key={id}>
300
+ {headers.map((header) => (
301
+ <Table.Th key={header.id} colSpan={header.colSpan}>
302
+ {header.isPlaceholder ||
303
+ flexRender(
304
+ header.column.columnDef.header,
305
+ header.getContext(),
306
+ )}
307
+ </Table.Th>
308
+ ))}
309
+ </Table.Tr>
310
+ ))}
311
+ </Table.Thead>
312
+ <Table.Tbody>
313
+ {table.getRowModel().rows.map((row) => (
314
+ <Table.Tr key={row.id}>
315
+ {row.getVisibleCells().map((cell) => (
316
+ <Table.Td key={cell.id}>
317
+ {cell.getValue() as React.ReactNode}
318
+ </Table.Td>
319
+ ))}
320
+ </Table.Tr>
321
+ ))}
322
+ </Table.Tbody>
323
+ </Table>
324
+ );
325
+ },
326
+ };
327
+
328
+ export const SortableSimple: Story = {
329
+ render: function Render(args) {
330
+ const [sort, setSort] = useState<{
331
+ key: ColumnKeys;
332
+ value: "none" | "ascending" | "descending";
333
+ }>({
334
+ key: "firstName",
335
+ value: "none",
336
+ });
337
+
338
+ const updateSort = (newKey: ColumnKeys) =>
339
+ setSort(({ key, value }) => ({
340
+ key: newKey,
341
+ value:
342
+ newKey !== key || value === "none"
343
+ ? "ascending"
344
+ : value === "ascending"
345
+ ? "descending"
346
+ : "none",
347
+ }));
348
+
349
+ const columns: ColumnsType = [
350
+ { key: "firstName", label: "First name" },
351
+ { key: "lastName", label: "Last name" },
352
+ { key: "age", label: "Age", numeric: true },
353
+ { key: "visits", label: "Visits", numeric: true },
354
+ ];
355
+
356
+ return (
357
+ <table className={styles.table} aria-label="Sortable table" {...args}>
358
+ <thead>
359
+ <tr>
360
+ {columns.map(({ key, label, numeric }) => (
361
+ <th
362
+ key={key}
363
+ data-numeric={numeric}
364
+ aria-sort={sort.key === key ? sort.value : "none"}
365
+ >
366
+ <button type="button" onClick={() => updateSort(key)}>
367
+ {label}
368
+ </button>
369
+ </th>
370
+ ))}
371
+ </tr>
372
+ </thead>
373
+ <tbody>
374
+ {mockDataSmall
375
+ .slice() // Make a copy for mutability
376
+ .sort((a, b) => {
377
+ if (sort.value === "none") return 0;
378
+ const asc = sort.value === "ascending";
379
+ const aVal = asc ? a[sort.key] : b[sort.key];
380
+ const bVal = asc ? b[sort.key] : a[sort.key];
381
+
382
+ return typeof aVal === "number"
383
+ ? Number(aVal) - Number(bVal)
384
+ : String(aVal).localeCompare(String(bVal));
385
+ })
386
+ .map((row) => (
387
+ <tr key={`${row.firstName}-${row.lastName}`}>
388
+ {columns.map(({ key, numeric }) => (
389
+ <td key={key} data-numeric={numeric}>
390
+ {row[key]}
391
+ </td>
392
+ ))}
393
+ </tr>
394
+ ))}
395
+ </tbody>
396
+ </table>
397
+ );
398
+ },
399
+ };
400
+
401
+ export const SortableTanstack: Story = {
402
+ render: function Render(args) {
403
+ const [sorting, setSorting] = useState<SortingState>([]);
404
+ const table = useReactTable({
405
+ getCoreRowModel: getCoreRowModel(),
406
+ getSortedRowModel: getSortedRowModel(),
407
+ onSortingChange: setSorting,
408
+ state: { sorting },
409
+ data: mockData,
410
+ columns: mockColumns,
411
+ });
412
+
413
+ return (
414
+ <Table aria-label="Sortable Tanstack table" {...args}>
415
+ <Table.Thead>
416
+ {table.getHeaderGroups().map(({ id, headers }) => (
417
+ <Table.Tr key={id}>
418
+ {headers.map((header) => (
419
+ <Table.ThSortable
420
+ key={header.id}
421
+ colSpan={header.colSpan}
422
+ aria-sort={header.column.getIsSorted()}
423
+ onClick={header.column.getToggleSortingHandler()}
424
+ >
425
+ {flexRender(
426
+ header.column.columnDef.header,
427
+ header.getContext(),
428
+ )}
429
+ </Table.ThSortable>
430
+ ))}
431
+ </Table.Tr>
432
+ ))}
433
+ </Table.Thead>
434
+ <Table.Tbody>
435
+ {table.getRowModel().rows.map((row) => (
436
+ <Table.Tr key={row.id}>
437
+ {row.getVisibleCells().map((cell) => (
438
+ <Table.Td key={cell.id}>
439
+ {cell.getValue() as React.ReactNode}
440
+ </Table.Td>
441
+ ))}
442
+ </Table.Tr>
443
+ ))}
444
+ </Table.Tbody>
445
+ </Table>
446
+ );
447
+ },
448
+ };
449
+
450
+ export const PaginatableSimple: Story = {
451
+ render: function Render(args) {
452
+ const size = 10;
453
+ const [page, setPage] = useState(0);
454
+ const index = page * size;
455
+ const columns: ColumnsType = [
456
+ { key: "firstName", label: "First name" },
457
+ { key: "lastName", label: "Last name" },
458
+ { key: "age", label: "Age", numeric: true },
459
+ { key: "visits", label: "Visits", numeric: true },
460
+ ];
461
+
462
+ const { pages, next, prev } = pagination({
463
+ current: page + 1,
464
+ total: Math.ceil(mockData.length / size),
465
+ show: 7,
466
+ });
467
+
468
+ return (
469
+ <>
470
+ <table
471
+ className={styles.table}
472
+ aria-label="Paginatable table"
473
+ {...args}
474
+ >
475
+ <thead>
476
+ <tr>
477
+ {columns.map(({ label, numeric }) => (
478
+ <th key={label} data-numeric={numeric}>
479
+ {label}
480
+ </th>
481
+ ))}
482
+ </tr>
483
+ </thead>
484
+ <tbody>
485
+ {mockData.slice(index, index + size).map((row) => (
486
+ <tr key={`${row.firstName}-${row.lastName}`}>
487
+ {columns.map(({ key, numeric }) => (
488
+ <td key={key} data-numeric={numeric}>
489
+ {row[key]}
490
+ </td>
491
+ ))}
492
+ </tr>
493
+ ))}
494
+ </tbody>
495
+ </table>
496
+ <nav className={styles.pagination} data-size="sm">
497
+ <ul>
498
+ <li>
499
+ <button
500
+ disabled={!prev}
501
+ className={styles.button}
502
+ type="button"
503
+ onClick={() => setPage(prev - 1)}
504
+ >
505
+ Forrige
506
+ </button>
507
+ </li>
508
+ {pages.map(({ current, key, page }) => (
509
+ <li key={key}>
510
+ {!!page && (
511
+ <button
512
+ aria-current={current}
513
+ className={styles.button}
514
+ type="button"
515
+ onClick={() => setPage(page - 1)}
516
+ >
517
+ {page}
518
+ </button>
519
+ )}
520
+ </li>
521
+ ))}
522
+ <li>
523
+ <button
524
+ disabled={!next}
525
+ className={styles.button}
526
+ type="button"
527
+ onClick={() => setPage(next - 1)}
528
+ >
529
+ Neste
530
+ </button>
531
+ </li>
532
+ </ul>
533
+ </nav>
534
+ </>
535
+ );
536
+ },
537
+ };
538
+
539
+ export const PaginatableTanstack: Story = {
540
+ render: function Render(args) {
541
+ const table = useReactTable({
542
+ getCoreRowModel: getCoreRowModel(),
543
+ getPaginationRowModel: getPaginationRowModel(),
544
+ data: mockData,
545
+ columns: mockColumns,
546
+ });
547
+
548
+ const { pages, next, prev } = pagination({
549
+ current: table.getState().pagination.pageIndex + 1,
550
+ total: table.getPageCount(),
551
+ show: 7,
552
+ });
553
+
554
+ return (
555
+ <>
556
+ <Table aria-label="Paginatable Tanstack table" {...args}>
557
+ <Table.Thead>
558
+ {table.getHeaderGroups().map(({ id, headers }) => (
559
+ <Table.Tr key={id}>
560
+ {headers.map((header) => (
561
+ <Table.Th key={header.id} colSpan={header.colSpan}>
562
+ {flexRender(
563
+ header.column.columnDef.header,
564
+ header.getContext(),
565
+ )}
566
+ </Table.Th>
567
+ ))}
568
+ </Table.Tr>
569
+ ))}
570
+ </Table.Thead>
571
+ <Table.Tbody>
572
+ {table.getRowModel().rows.map((row) => (
573
+ <Table.Tr key={row.id}>
574
+ {row.getVisibleCells().map((cell) => (
575
+ <Table.Td key={cell.id}>
576
+ {cell.getValue() as React.ReactNode}
577
+ </Table.Td>
578
+ ))}
579
+ </Table.Tr>
580
+ ))}
581
+ </Table.Tbody>
582
+ </Table>
583
+ <Pagination data-size="sm" aria-label="Sidenavigering">
584
+ <ul>
585
+ <li>
586
+ <Button
587
+ disabled={!prev}
588
+ onClick={() => table.setPageIndex(prev - 1)}
589
+ >
590
+ Forrige
591
+ </Button>
592
+ </li>
593
+ {pages.map(({ current, key, page }) => (
594
+ <li key={key}>
595
+ {!!page && (
596
+ <Button
597
+ aria-current={current}
598
+ onClick={() => table.setPageIndex(page - 1)}
599
+ >
600
+ {page}
601
+ </Button>
602
+ )}
603
+ </li>
604
+ ))}
605
+ <li>
606
+ <Button
607
+ disabled={!next}
608
+ onClick={() => table.setPageIndex(next - 1)}
609
+ >
610
+ Neste
611
+ </Button>
612
+ </li>
613
+ </ul>
614
+ </Pagination>
615
+ </>
616
+ );
617
+ },
618
+ };
619
+
620
+ export const SearchableSimple: Story = {
621
+ render: function Render(args) {
622
+ const [search, setSearch] = useState("");
623
+ const columns: ColumnsType = [
624
+ { key: "firstName", label: "First name" },
625
+ { key: "lastName", label: "Last name" },
626
+ { key: "age", label: "Age", numeric: true },
627
+ { key: "visits", label: "Visits", numeric: true },
628
+ ];
629
+ const filtered = mockData.filter((row) => {
630
+ const text = Object.values(row).join(" ");
631
+ return text.toLowerCase().includes(search.toLowerCase());
632
+ });
633
+
634
+ return (
635
+ <>
636
+ <ds-field className={styles.field}>
637
+ <label>Search</label>
638
+ <input
639
+ className={styles.input}
640
+ type="search"
641
+ onChange={({ target }) => setSearch(target.value)}
642
+ value={search}
643
+ />
644
+ </ds-field>
645
+ <table
646
+ className={styles.table}
647
+ aria-label="Searchable table"
648
+ data-fixed
649
+ {...args}
650
+ >
651
+ <thead>
652
+ <tr>
653
+ {columns.map(({ label, numeric }) => (
654
+ <th key={label} data-numeric={numeric}>
655
+ {label}
656
+ </th>
657
+ ))}
658
+ </tr>
659
+ </thead>
660
+ <tbody>
661
+ {filtered.map((row) => (
662
+ <tr key={`${row.firstName}-${row.lastName}`}>
663
+ {columns.map(({ key, numeric }) => (
664
+ <td key={key} data-numeric={numeric}>
665
+ {row[key]}
666
+ </td>
667
+ ))}
668
+ </tr>
669
+ ))}
670
+ </tbody>
671
+ </table>
672
+ </>
673
+ );
674
+ },
675
+ };
676
+
677
+ export const SearchableTanstack: Story = {
678
+ render: function Render(args) {
679
+ const [search, setSearch] = useState("");
680
+ const table = useReactTable({
681
+ getCoreRowModel: getCoreRowModel(),
682
+ getFilteredRowModel: getFilteredRowModel(),
683
+ onGlobalFilterChange: setSearch,
684
+ data: mockData,
685
+ state: { globalFilter: search },
686
+ columns: mockColumns,
687
+ });
688
+
689
+ return (
690
+ <>
691
+ <Field
692
+ as="input"
693
+ type="search"
694
+ label="Search"
695
+ onChange={({ target }) => setSearch(target.value)}
696
+ value={search}
697
+ />
698
+ <Table aria-label="Searchable Tanstack table" data-fixed {...args}>
699
+ <Table.Thead>
700
+ {table.getHeaderGroups().map(({ id, headers }) => (
701
+ <Table.Tr key={id}>
702
+ {headers.map((header) => (
703
+ <Table.Th key={header.id} colSpan={header.colSpan}>
704
+ {flexRender(
705
+ header.column.columnDef.header,
706
+ header.getContext(),
707
+ )}
708
+ </Table.Th>
709
+ ))}
710
+ </Table.Tr>
711
+ ))}
712
+ </Table.Thead>
713
+ <Table.Tbody>
714
+ {table.getRowModel().rows.map((row) => (
715
+ <Table.Tr key={row.id}>
716
+ {row.getVisibleCells().map((cell) => (
717
+ <Table.Td key={cell.id}>
718
+ {cell.getValue() as React.ReactNode}
719
+ </Table.Td>
720
+ ))}
721
+ </Table.Tr>
722
+ ))}
723
+ </Table.Tbody>
724
+ </Table>
725
+ </>
726
+ );
727
+ },
728
+ };
729
+
730
+ export const ExpandableSimple: Story = {
731
+ render: function Render(args) {
732
+ const columns: ColumnsType = [
733
+ { key: "firstName", label: "First name" },
734
+ { key: "lastName", label: "Last name" },
735
+ { key: "age", label: "Age", numeric: true },
736
+ { key: "visits", label: "Visits", numeric: true },
737
+ ];
738
+
739
+ return (
740
+ <table className={styles.table} aria-label="Expandable table" {...args}>
741
+ <thead>
742
+ <tr>
743
+ {columns.map(({ label, numeric }) => (
744
+ <th key={label} data-numeric={numeric}>
745
+ {label}
746
+ </th>
747
+ ))}
748
+ </tr>
749
+ </thead>
750
+ <tbody>
751
+ {mockExpand.map(function Row(row) {
752
+ const [expanded, setExpanded] = useState(false);
753
+
754
+ return (
755
+ <Fragment key={`${row.firstName}-${row.lastName}`}>
756
+ <tr>
757
+ {columns.map(({ key, numeric }, cellIndex) => (
758
+ <td key={key} data-numeric={numeric}>
759
+ {cellIndex === 0 ? (
760
+ <button
761
+ aria-expanded={expanded}
762
+ onClick={() => setExpanded(!expanded)}
763
+ type="button"
764
+ >
765
+ {row[key]}
766
+ </button>
767
+ ) : (
768
+ row[key]
769
+ )}
770
+ </td>
771
+ ))}
772
+ </tr>
773
+ <tr>
774
+ <td colSpan={columns.length}>{row.expand}</td>
775
+ </tr>
776
+ </Fragment>
777
+ );
778
+ })}
779
+ </tbody>
780
+ </table>
781
+ );
782
+ },
783
+ };
784
+
785
+ export const ExpandableTanstack: Story = {
786
+ render: function Render(args) {
787
+ const [expanded, setExpanded] = useState<ExpandedState>({});
788
+ const table = useReactTable({
789
+ getCoreRowModel: getCoreRowModel(),
790
+ onExpandedChange: setExpanded,
791
+ state: { expanded },
792
+ data: mockExpand,
793
+ columns: mockColumns,
794
+ });
795
+
796
+ return (
797
+ <Table aria-label="Expandable Tanstack table" {...args}>
798
+ <Table.Thead>
799
+ {table.getHeaderGroups().map(({ id, headers }) => (
800
+ <Table.Tr key={id}>
801
+ {headers.map((header) => (
802
+ <Table.Th key={header.id} colSpan={header.colSpan}>
803
+ {flexRender(
804
+ header.column.columnDef.header,
805
+ header.getContext(),
806
+ )}
807
+ </Table.Th>
808
+ ))}
809
+ </Table.Tr>
810
+ ))}
811
+ </Table.Thead>
812
+ <Table.Tbody>
813
+ {table.getRowModel().rows.map((row) => (
814
+ <Fragment key={row.id}>
815
+ <Table.Tr>
816
+ {row.getVisibleCells().map((cell, cellIndex) => (
817
+ <Table.Td key={cell.id}>
818
+ {cellIndex === 0 ? (
819
+ <Button
820
+ aria-expanded={row.getIsExpanded()}
821
+ onClick={() => row.toggleExpanded()}
822
+ >
823
+ {cell.getValue() as React.ReactNode}
824
+ </Button>
825
+ ) : (
826
+ (cell.getValue() as React.ReactNode)
827
+ )}
828
+ </Table.Td>
829
+ ))}
830
+ </Table.Tr>
831
+ <Table.Tr hidden={!row.getIsExpanded()}>
832
+ <Table.Td colSpan={row.getVisibleCells().length}>
833
+ {row.original.expand}
834
+ </Table.Td>
835
+ </Table.Tr>
836
+ </Fragment>
837
+ ))}
838
+ </Table.Tbody>
839
+ </Table>
840
+ );
841
+ },
842
+ };
843
+
844
+ export const CheckableSimple: Story = {
845
+ render: function Render(args) {
846
+ const columns: ColumnsType = [
847
+ { key: "firstName", label: "First name" },
848
+ { key: "lastName", label: "Last name" },
849
+ { key: "age", label: "Age", numeric: true },
850
+ { key: "visits", label: "Visits", numeric: true },
851
+ ];
852
+
853
+ return (
854
+ <table className={styles.table} aria-label="Checkable table" {...args}>
855
+ <thead>
856
+ <tr>
857
+ {columns.map(({ label, numeric }) => (
858
+ <th key={label} data-numeric={numeric}>
859
+ {label}
860
+ </th>
861
+ ))}
862
+ </tr>
863
+ </thead>
864
+ <tbody>
865
+ {mockDataSmall.map((row, i) => (
866
+ <tr
867
+ key={`${row.firstName}-${row.lastName}`}
868
+ data-clickdelegatefor={`check-${i}`}
869
+ >
870
+ {columns.map(({ key, numeric }, cellIndex) => (
871
+ <td key={key} data-numeric={numeric}>
872
+ {cellIndex ? (
873
+ row[key]
874
+ ) : (
875
+ <ds-field className={styles.field}>
876
+ <input
877
+ className={styles.input}
878
+ type="checkbox"
879
+ id={`check-${i}`}
880
+ />
881
+ <label>{row[key]}</label>
882
+ </ds-field>
883
+ )}
884
+ </td>
885
+ ))}
886
+ </tr>
887
+ ))}
888
+ </tbody>
889
+ </table>
890
+ );
891
+ },
892
+ };
893
+
894
+ export const CheckableTanstack: Story = {
895
+ render: function Render(args) {
896
+ const table = useReactTable({
897
+ getCoreRowModel: getCoreRowModel(),
898
+ data: mockData,
899
+ columns: mockColumns,
900
+ });
901
+
902
+ return (
903
+ <Table aria-label="Checkable Tanstack table" {...args}>
904
+ <Table.Thead>
905
+ {table.getHeaderGroups().map(({ id, headers }) => (
906
+ <Table.Tr key={id}>
907
+ {headers.map((header) => (
908
+ <Table.Th key={header.id} colSpan={header.colSpan}>
909
+ {header.isPlaceholder ||
910
+ flexRender(
911
+ header.column.columnDef.header,
912
+ header.getContext(),
913
+ )}
914
+ </Table.Th>
915
+ ))}
916
+ </Table.Tr>
917
+ ))}
918
+ </Table.Thead>
919
+ <Table.Tbody>
920
+ {table.getRowModel().rows.map((row) => (
921
+ <Table.Tr key={row.id} data-clickdelegatefor={`check-${row.id}`}>
922
+ {row.getVisibleCells().map((cell, index) => (
923
+ <Table.Td key={cell.id}>
924
+ {index ? (
925
+ (cell.getValue() as React.ReactNode)
926
+ ) : (
927
+ <Field
928
+ id={`check-${row.id}`}
929
+ as="input"
930
+ type="checkbox"
931
+ checked={row.getIsSelected()}
932
+ onChange={row.getToggleSelectedHandler()}
933
+ label={cell.getValue() as React.ReactNode}
934
+ />
935
+ )}
936
+ </Table.Td>
937
+ ))}
938
+ </Table.Tr>
939
+ ))}
940
+ </Table.Tbody>
941
+ </Table>
942
+ );
943
+ },
944
+ };
945
+
946
+ export const ClickableSimple: Story = {
947
+ render: () => (
948
+ <table
949
+ className={styles.table}
950
+ data-border="true"
951
+ data-fixed
952
+ aria-label="Table with clickable rows"
953
+ >
954
+ <thead>
955
+ <tr>
956
+ <th>First name</th>
957
+ <th>Last name</th>
958
+ <th>Age</th>
959
+ <th>Visits</th>
960
+ </tr>
961
+ </thead>
962
+ <tbody>
963
+ <tr data-clickdelegatefor="button-1">
964
+ <td>
965
+ <button
966
+ id="button-1"
967
+ type="button"
968
+ onClick={() => alert("clicked row 1")}
969
+ >
970
+ Antoni
971
+ </button>
972
+ </td>
973
+ <td>Foyston</td>
974
+ <td>74</td>
975
+ <td>
976
+ <a href="#none">128</a>
977
+ </td>
978
+ </tr>
979
+ <tr data-clickdelegatefor="button-2">
980
+ <td>
981
+ <button
982
+ id="button-2"
983
+ type="button"
984
+ onClick={() => alert("clicked row 2")}
985
+ >
986
+ Jenine
987
+ </button>
988
+ </td>
989
+ <td>Healey</td>
990
+ <td>22</td>
991
+ <td>194</td>
992
+ </tr>
993
+ <tr data-clickdelegatefor="button-3">
994
+ <td>
995
+ <button
996
+ id="button-3"
997
+ type="button"
998
+ onClick={() => alert("clicked row 3")}
999
+ >
1000
+ Leigh
1001
+ </button>
1002
+ </td>
1003
+ <td>Klein</td>
1004
+ <td>26</td>
1005
+ <td>114</td>
1006
+ </tr>
1007
+ </tbody>
1008
+ </table>
1009
+ ),
1010
+ };
1011
+
1012
+ export const ClickableTanstack: Story = {
1013
+ render: function Render(args) {
1014
+ const table = useReactTable({
1015
+ getCoreRowModel: getCoreRowModel(),
1016
+ data: mockData,
1017
+ columns: mockColumns,
1018
+ });
1019
+
1020
+ return (
1021
+ <Table aria-label="Checkable Tanstack table" {...args}>
1022
+ <Table.Thead>
1023
+ {table.getHeaderGroups().map(({ id, headers }) => (
1024
+ <Table.Tr key={id}>
1025
+ {headers.map((header) => (
1026
+ <Table.Th key={header.id} colSpan={header.colSpan}>
1027
+ {header.isPlaceholder ||
1028
+ flexRender(
1029
+ header.column.columnDef.header,
1030
+ header.getContext(),
1031
+ )}
1032
+ </Table.Th>
1033
+ ))}
1034
+ </Table.Tr>
1035
+ ))}
1036
+ </Table.Thead>
1037
+ <Table.Tbody>
1038
+ {table.getRowModel().rows.map((row, rowIndex) => (
1039
+ <Table.Tr key={row.id} data-clickdelegatefor={`button-${rowIndex}`}>
1040
+ {row.getVisibleCells().map((cell, index) => (
1041
+ <Table.Td key={cell.id}>
1042
+ {index ? (
1043
+ (cell.getValue() as React.ReactNode)
1044
+ ) : (
1045
+ <Button
1046
+ id={`button-${rowIndex}`}
1047
+ onClick={() => alert(`clicked row ${rowIndex + 1}`)}
1048
+ >
1049
+ {cell.getValue() as React.ReactNode}
1050
+ </Button>
1051
+ )}
1052
+ </Table.Td>
1053
+ ))}
1054
+ </Table.Tr>
1055
+ ))}
1056
+ </Table.Tbody>
1057
+ </Table>
1058
+ );
1059
+ },
1060
+ };
1061
+
1062
+ export const WithHorizontalTitles: Story = {
1063
+ render: () => (
1064
+ <table className={styles.table} aria-label="Table with horizontal titles">
1065
+ <thead>
1066
+ <tr>
1067
+ <th>First name</th>
1068
+ <th>Last name</th>
1069
+ <th>Age</th>
1070
+ <th>Visits</th>
1071
+ </tr>
1072
+ </thead>
1073
+ <tbody>
1074
+ <tr>
1075
+ <th scope="row">Antoni</th>
1076
+ <td>Foyston</td>
1077
+ <td>74</td>
1078
+ <td>128</td>
1079
+ </tr>
1080
+ <tr>
1081
+ <th scope="row">Jenine</th>
1082
+ <td>Healey</td>
1083
+ <td>22</td>
1084
+ <td>194</td>
1085
+ </tr>
1086
+ <tr>
1087
+ <th scope="row">Leigh</th>
1088
+ <td>Klein</td>
1089
+ <td>26</td>
1090
+ <td>114</td>
1091
+ </tr>
1092
+ <tr>
1093
+ <th scope="row">Zara</th>
1094
+ <td>Greenrodd</td>
1095
+ <td>28</td>
1096
+ <td>36</td>
1097
+ </tr>
1098
+ </tbody>
1099
+ </table>
1100
+ ),
1101
+ };
1102
+
1103
+ export const WithFixedWidths: Story = {
1104
+ render: () => (
1105
+ <table
1106
+ className={styles.table}
1107
+ data-fixed
1108
+ aria-label="Table with fixed widths"
1109
+ >
1110
+ <thead>
1111
+ <tr>
1112
+ <th>First name</th>
1113
+ <th>Last name</th>
1114
+ <th>Age</th>
1115
+ <th>Visits</th>
1116
+ </tr>
1117
+ </thead>
1118
+ <tbody>
1119
+ <tr>
1120
+ <td>Antoni</td>
1121
+ <td>Foyston</td>
1122
+ <td>74</td>
1123
+ <td>128</td>
1124
+ </tr>
1125
+ <tr>
1126
+ <td>Jenine</td>
1127
+ <td>Healey</td>
1128
+ <td>22</td>
1129
+ <td>194</td>
1130
+ </tr>
1131
+ <tr>
1132
+ <td>Leigh</td>
1133
+ <td>Klein</td>
1134
+ <td>26</td>
1135
+ <td>114</td>
1136
+ </tr>
1137
+ <tr>
1138
+ <td>Zara</td>
1139
+ <td>Greenrodd</td>
1140
+ <td>28</td>
1141
+ <td>36</td>
1142
+ </tr>
1143
+ </tbody>
1144
+ </table>
1145
+ ),
1146
+ };
1147
+
1148
+ export const WithAlign: Story = {
1149
+ render: () => (
1150
+ <div className={styles.grid}>
1151
+ <code>data-align="start":</code>
1152
+ <table
1153
+ className={styles.table}
1154
+ data-align="start"
1155
+ aria-label="Table with align start"
1156
+ >
1157
+ <tbody>
1158
+ <tr>
1159
+ <th data-nowrap>Reference number</th>
1160
+ <td>1</td>
1161
+ </tr>
1162
+ <tr>
1163
+ <th>Description</th>
1164
+ <td>
1165
+ A preliminary version.
1166
+ <br />
1167
+ An application for a certificate has been initiated.
1168
+ </td>
1169
+ </tr>
1170
+ <tr>
1171
+ <th>Template</th>
1172
+ <td>
1173
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sed
1174
+ enim ut ex posuere suscipit id eu justo. Cras vehicula ornare
1175
+ efficitur. Etiam commodo est velit, eget mattis felis sollicitudin
1176
+ sit amet. Etiam non dui fermentum, malesuada augue in, elementum
1177
+ felis.
1178
+ </td>
1179
+ </tr>
1180
+ </tbody>
1181
+ </table>
1182
+ <br />
1183
+ <code>data-align="center":</code>
1184
+ <table
1185
+ className={styles.table}
1186
+ data-align="center"
1187
+ aria-label="Table with align start"
1188
+ >
1189
+ <tbody>
1190
+ <tr>
1191
+ <th data-nowrap>Reference number</th>
1192
+ <td>1</td>
1193
+ </tr>
1194
+ <tr>
1195
+ <th>Description</th>
1196
+ <td>
1197
+ A preliminary version.
1198
+ <br />
1199
+ An application for a certificate has been initiated.
1200
+ </td>
1201
+ </tr>
1202
+ <tr>
1203
+ <th>Template</th>
1204
+ <td>
1205
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sed
1206
+ enim ut ex posuere suscipit id eu justo. Cras vehicula ornare
1207
+ efficitur. Etiam commodo est velit, eget mattis felis sollicitudin
1208
+ sit amet. Etiam non dui fermentum, malesuada augue in, elementum
1209
+ felis.
1210
+ </td>
1211
+ </tr>
1212
+ </tbody>
1213
+ </table>
1214
+ </div>
1215
+ ),
1216
+ };
1217
+
1218
+ export const WithNumericValues: Story = {
1219
+ render: () => (
1220
+ <table className={styles.table} aria-label="Table with numeric values">
1221
+ <thead>
1222
+ <tr>
1223
+ <th>First name</th>
1224
+ <th>Last name</th>
1225
+ <th>Age</th>
1226
+ <th>Visits</th>
1227
+ </tr>
1228
+ </thead>
1229
+ <tbody>
1230
+ <tr>
1231
+ <td>Antoni</td>
1232
+ <td>Foyston</td>
1233
+ <td data-numeric>74</td>
1234
+ <td data-numeric>128</td>
1235
+ </tr>
1236
+ <tr>
1237
+ <td>Jenine</td>
1238
+ <td>Healey</td>
1239
+ <td data-numeric>22</td>
1240
+ <td data-numeric>194</td>
1241
+ </tr>
1242
+ <tr>
1243
+ <td>Leigh</td>
1244
+ <td>Klein</td>
1245
+ <td data-numeric>26</td>
1246
+ <td data-numeric>114</td>
1247
+ </tr>
1248
+ <tr>
1249
+ <td>Zara</td>
1250
+ <td>Greenrodd</td>
1251
+ <td data-numeric>28</td>
1252
+ <td data-numeric>36</td>
1253
+ </tr>
1254
+ </tbody>
1255
+ </table>
1256
+ ),
1257
+ };
1258
+
1259
+ export const WithJustify: Story = {
1260
+ render: () => (
1261
+ <table className={styles.table} aria-label="Table with numeric values">
1262
+ <thead>
1263
+ <tr>
1264
+ <th>Kostnad</th>
1265
+ <th data-justify="end">Pris</th>
1266
+ </tr>
1267
+ </thead>
1268
+ <tbody>
1269
+ <tr>
1270
+ <td>Gebyr 1</td>
1271
+ <td data-justify="end" data-numeric>
1272
+ 128 kr
1273
+ </td>
1274
+ </tr>
1275
+ <tr>
1276
+ <td>Gebyr 2</td>
1277
+ <td data-justify="end" data-numeric>
1278
+ 194 kr
1279
+ </td>
1280
+ </tr>
1281
+ <tr>
1282
+ <td>Gebyr 3</td>
1283
+ <td data-justify="end" data-numeric>
1284
+ 114 kr
1285
+ </td>
1286
+ </tr>
1287
+ <tr>
1288
+ <th>Total</th>
1289
+ <th data-justify="end" data-numeric>
1290
+ 194 kr
1291
+ </th>
1292
+ </tr>
1293
+ </tbody>
1294
+ </table>
1295
+ ),
1296
+ };
1297
+
1298
+ export const WithFooter: Story = {
1299
+ render: () => (
1300
+ <table className={styles.table} aria-label="Table with footer">
1301
+ <thead>
1302
+ <tr>
1303
+ <th>First name</th>
1304
+ <th>Last name</th>
1305
+ <th>Age</th>
1306
+ <th>Visits</th>
1307
+ </tr>
1308
+ </thead>
1309
+ <tbody>
1310
+ <tr>
1311
+ <td>Antoni</td>
1312
+ <td>Foyston</td>
1313
+ <td>74</td>
1314
+ <td>128</td>
1315
+ </tr>
1316
+ <tr>
1317
+ <td>Jenine</td>
1318
+ <td>Healey</td>
1319
+ <td>22</td>
1320
+ <td>194</td>
1321
+ </tr>
1322
+ <tr>
1323
+ <td>Leigh</td>
1324
+ <td>Klein</td>
1325
+ <td>26</td>
1326
+ <td>114</td>
1327
+ </tr>
1328
+ <tr>
1329
+ <td>Zara</td>
1330
+ <td>Greenrodd</td>
1331
+ <td>28</td>
1332
+ <td>36</td>
1333
+ </tr>
1334
+ </tbody>
1335
+ <tfoot>
1336
+ <tr>
1337
+ <th>First name</th>
1338
+ <th>Last name</th>
1339
+ <th>Age</th>
1340
+ <th>Visits</th>
1341
+ </tr>
1342
+ </tfoot>
1343
+ </table>
1344
+ ),
1345
+ };
1346
+
1347
+ export const WithBorderAround: Story = {
1348
+ render: () => (
1349
+ <table
1350
+ className={styles.table}
1351
+ data-border="true"
1352
+ aria-label="Table with border around"
1353
+ >
1354
+ <thead>
1355
+ <tr>
1356
+ <th data-nowrap>First name</th>
1357
+ <th>Last name</th>
1358
+ <th>Age</th>
1359
+ <th>Visits</th>
1360
+ </tr>
1361
+ </thead>
1362
+ <tbody>
1363
+ <tr>
1364
+ <th>Antoni</th>
1365
+ <td>Foyston</td>
1366
+ <td>74</td>
1367
+ <td>128</td>
1368
+ </tr>
1369
+ <tr>
1370
+ <th>Jenine</th>
1371
+ <td>Healey</td>
1372
+ <td>22</td>
1373
+ <td>194</td>
1374
+ </tr>
1375
+ <tr>
1376
+ <th>Leigh</th>
1377
+ <td>Klein</td>
1378
+ <td>
1379
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam felis
1380
+ quam, pulvinar et lacus et, molestie semper ante.
1381
+ </td>
1382
+ <td>114</td>
1383
+ </tr>
1384
+ <tr>
1385
+ <th>Zara</th>
1386
+ <td>Greenrodd</td>
1387
+ <td>28</td>
1388
+ <td>36</td>
1389
+ </tr>
1390
+ </tbody>
1391
+ </table>
1392
+ ),
1393
+ };
1394
+
1395
+ export const WithoutBorders: Story = {
1396
+ render: () => (
1397
+ <div className={styles.grid} style={{ minWidth: 400 }}>
1398
+ <h2 className={styles.heading}>Table heading</h2>
1399
+ <table
1400
+ className={styles.table}
1401
+ data-border="false"
1402
+ aria-label="Table without borders"
1403
+ >
1404
+ <tbody>
1405
+ <tr>
1406
+ <th>Antoni Foyston</th>
1407
+ <td>74</td>
1408
+ </tr>
1409
+ <tr>
1410
+ <th>Jenine Healey</th>
1411
+ <td>22</td>
1412
+ </tr>
1413
+ <tr>
1414
+ <th>Leigh Klein</th>
1415
+ <td>14</td>
1416
+ </tr>
1417
+ <tr>
1418
+ <th>Zara Greenrodd</th>
1419
+ <td>28</td>
1420
+ </tr>
1421
+ </tbody>
1422
+ </table>
1423
+ </div>
1424
+ ),
1425
+ };
1426
+
1427
+ export const Sizes: Story = {
1428
+ render: () => (
1429
+ <>
1430
+ <table
1431
+ data-fixed
1432
+ data-size="sm"
1433
+ className={styles.table}
1434
+ aria-label="Small table"
1435
+ >
1436
+ <thead>
1437
+ <tr>
1438
+ <th>Size</th>
1439
+ <th>Attr</th>
1440
+ </tr>
1441
+ </thead>
1442
+ <tbody>
1443
+ <tr>
1444
+ <td>Small</td>
1445
+ <td>
1446
+ <code>data-size="sm"</code>
1447
+ </td>
1448
+ </tr>
1449
+ </tbody>
1450
+ </table>
1451
+ <table
1452
+ data-fixed
1453
+ data-size="md"
1454
+ className={styles.table}
1455
+ aria-label="Small table"
1456
+ >
1457
+ <thead>
1458
+ <tr>
1459
+ <th>Size</th>
1460
+ <th>Attr</th>
1461
+ </tr>
1462
+ </thead>
1463
+ <tbody>
1464
+ <tr>
1465
+ <td>Medium</td>
1466
+ <td>
1467
+ <code>data-size="md"</code>
1468
+ </td>
1469
+ </tr>
1470
+ </tbody>
1471
+ </table>
1472
+ <table
1473
+ data-fixed
1474
+ data-size="lg"
1475
+ className={styles.table}
1476
+ aria-label="Small table"
1477
+ >
1478
+ <thead>
1479
+ <tr>
1480
+ <th>Size</th>
1481
+ <th>Attr</th>
1482
+ </tr>
1483
+ </thead>
1484
+ <tbody>
1485
+ <tr>
1486
+ <td>Large</td>
1487
+ <td>
1488
+ <code>data-size="lg"</code>
1489
+ </td>
1490
+ </tr>
1491
+ </tbody>
1492
+ </table>
1493
+ </>
1494
+ ),
1495
+ };
1496
+
1497
+ export const MobileScroll: Story = {
1498
+ decorators: mobileDecorators,
1499
+ parameters: {
1500
+ viewport: {
1501
+ defaultViewport: "mobile2", // Large mobile default viewport
1502
+ },
1503
+ },
1504
+ render: () => (
1505
+ <figure>
1506
+ <table
1507
+ className={styles.table}
1508
+ aria-label="Mobile scrollable table"
1509
+ data-nowrap
1510
+ >
1511
+ <thead>
1512
+ <tr>
1513
+ <th>First name</th>
1514
+ <th>Last name</th>
1515
+ <th>Description</th>
1516
+ <th>Age</th>
1517
+ <th>Visits</th>
1518
+ </tr>
1519
+ </thead>
1520
+ <tbody>
1521
+ <tr>
1522
+ <td>Antoni</td>
1523
+ <td>Foyston</td>
1524
+ <td>Lorem ipsum dolor sit amet consectetur.</td>
1525
+ <td>74</td>
1526
+ <td>128</td>
1527
+ </tr>
1528
+ <tr>
1529
+ <td>Jenine</td>
1530
+ <td>Healey</td>
1531
+ <td>Lorem ipsum dolor sit amet consectetur.</td>
1532
+ <td>22</td>
1533
+ <td>194</td>
1534
+ </tr>
1535
+ <tr>
1536
+ <td>Leigh</td>
1537
+ <td>Klein</td>
1538
+ <td>Lorem ipsum dolor sit amet consectetur.</td>
1539
+ <td>26</td>
1540
+ <td>114</td>
1541
+ </tr>
1542
+ <tr>
1543
+ <td>Zara</td>
1544
+ <td>Greenrodd</td>
1545
+ <td>Lorem ipsum dolor sit amet consectetur.</td>
1546
+ <td>28</td>
1547
+ <td>36</td>
1548
+ </tr>
1549
+ </tbody>
1550
+ </table>
1551
+ </figure>
1552
+ ),
1553
+ };
1554
+
1555
+ export const MobileDivided: Story = {
1556
+ decorators: mobileDecorators,
1557
+ parameters: {
1558
+ viewport: {
1559
+ defaultViewport: "mobile2", // Large mobile default viewport
1560
+ },
1561
+ },
1562
+ render: () => (
1563
+ <table
1564
+ className={styles.table}
1565
+ data-mobile="divided"
1566
+ aria-label="Mobile divided table"
1567
+ >
1568
+ <thead>
1569
+ <tr>
1570
+ <th>First name</th>
1571
+ <th>Last name</th>
1572
+ <th>Age</th>
1573
+ <th>Visits</th>
1574
+ </tr>
1575
+ </thead>
1576
+ <tbody>
1577
+ <tr>
1578
+ <th>Antoni</th>
1579
+ <td>Foyston</td>
1580
+ <td>74</td>
1581
+ <td>128</td>
1582
+ </tr>
1583
+ <tr>
1584
+ <th>Jenine</th>
1585
+ <td>Healey</td>
1586
+ <td>22</td>
1587
+ <td>194</td>
1588
+ </tr>
1589
+ <tr>
1590
+ <th>Leigh</th>
1591
+ <td>Klein</td>
1592
+ <td>
1593
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam felis
1594
+ quam, pulvinar et lacus et, molestie semper ante.
1595
+ </td>
1596
+ <td>114</td>
1597
+ </tr>
1598
+ <tr>
1599
+ <th>Zara</th>
1600
+ <td>Greenrodd</td>
1601
+ <td>28</td>
1602
+ <td>36</td>
1603
+ </tr>
1604
+ </tbody>
1605
+ </table>
1606
+ ),
1607
+ };
1608
+
1609
+ export const MobileSpaced: Story = {
1610
+ decorators: mobileDecorators,
1611
+ parameters: {
1612
+ viewport: {
1613
+ defaultViewport: "mobile2", // Large mobile default viewport
1614
+ },
1615
+ },
1616
+ render: () => (
1617
+ <table
1618
+ className={styles.table}
1619
+ data-mobile="spaced"
1620
+ aria-label="Mobile spaced table"
1621
+ >
1622
+ <thead>
1623
+ <tr>
1624
+ <th>First name</th>
1625
+ <th>Last name</th>
1626
+ <th>Age</th>
1627
+ <th>Visits</th>
1628
+ </tr>
1629
+ </thead>
1630
+ <tbody>
1631
+ <tr>
1632
+ <th>Antoni</th>
1633
+ <td>Foyston</td>
1634
+ <td>74</td>
1635
+ <td>128</td>
1636
+ </tr>
1637
+ <tr>
1638
+ <th>Jenine</th>
1639
+ <td>Healey</td>
1640
+ <td>22</td>
1641
+ <td>194</td>
1642
+ </tr>
1643
+ <tr>
1644
+ <th>Leigh</th>
1645
+ <td>Klein</td>
1646
+ <td>
1647
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam felis
1648
+ quam, pulvinar et lacus et, molestie semper ante.
1649
+ </td>
1650
+ <td>114</td>
1651
+ </tr>
1652
+ <tr>
1653
+ <th>Zara</th>
1654
+ <td>Greenrodd</td>
1655
+ <td>28</td>
1656
+ <td>36</td>
1657
+ </tr>
1658
+ </tbody>
1659
+ </table>
1660
+ ),
1661
+ };
1662
+
1663
+ export const MobileStacked: Story = {
1664
+ decorators: mobileDecorators,
1665
+ parameters: {
1666
+ viewport: {
1667
+ defaultViewport: "mobile2", // Large mobile default viewport
1668
+ },
1669
+ },
1670
+ render: () => (
1671
+ <table
1672
+ className={styles.table}
1673
+ data-mobile="stacked"
1674
+ aria-label="Mobile stacked table"
1675
+ >
1676
+ <thead>
1677
+ <tr>
1678
+ <th>First name</th>
1679
+ <th>Last name</th>
1680
+ <th>Age</th>
1681
+ <th>Visits</th>
1682
+ </tr>
1683
+ </thead>
1684
+ <tbody>
1685
+ <tr>
1686
+ <th>Antoni</th>
1687
+ <td>Foyston</td>
1688
+ <td>74</td>
1689
+ <td>128</td>
1690
+ </tr>
1691
+ <tr>
1692
+ <th>Jenine</th>
1693
+ <td>Healey</td>
1694
+ <td>22</td>
1695
+ <td>194</td>
1696
+ </tr>
1697
+ <tr>
1698
+ <th>Leigh</th>
1699
+ <td>Klein</td>
1700
+ <td>
1701
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam felis
1702
+ quam, pulvinar et lacus et, molestie semper ante.
1703
+ </td>
1704
+ <td>114</td>
1705
+ </tr>
1706
+ <tr>
1707
+ <th>Zara</th>
1708
+ <td>Greenrodd</td>
1709
+ <td>28</td>
1710
+ <td>36</td>
1711
+ </tr>
1712
+ </tbody>
1713
+ </table>
1714
+ ),
1715
+ };