@happyvertical/smrt-ui 0.53.0 → 0.53.2

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 (205) hide show
  1. package/package.json +3 -3
  2. package/dist/actions/__tests__/ripple.test.js +0 -82
  3. package/dist/actions/__tests__/swipe-dismiss.test.js +0 -69
  4. package/dist/components/calendar/__tests__/CalendarView.test.js +0 -226
  5. package/dist/components/calendar/__tests__/calendar-model.test.js +0 -138
  6. package/dist/components/chat/__tests__/chat-primitives.test.js +0 -118
  7. package/dist/components/data/__tests__/CollectionList.test.js +0 -37
  8. package/dist/components/data/__tests__/DataTable.test.js +0 -807
  9. package/dist/components/data/__tests__/DataTableConformance.test.js +0 -212
  10. package/dist/components/data/__tests__/DataTableController.test.js +0 -371
  11. package/dist/components/data/__tests__/DataTableIdentity.test.js +0 -29
  12. package/dist/components/data/__tests__/DataTableLayout.test.js +0 -195
  13. package/dist/components/data/__tests__/DataTablePerformance.test.js +0 -21
  14. package/dist/components/data/__tests__/DataTableResponsive.test.js +0 -80
  15. package/dist/components/data/__tests__/DataTableVirtualization.test.js +0 -80
  16. package/dist/components/data/__tests__/DataTableVirtualizationComponent.test.js +0 -255
  17. package/dist/components/data/__tests__/SortableHeader.test.js +0 -101
  18. package/dist/components/data/__tests__/data-surface.test.js +0 -746
  19. package/dist/components/data/__tests__/data-table-helpers.test.js +0 -36
  20. package/dist/components/data/__tests__/data-table-surface.test.js +0 -49
  21. package/dist/components/data/__tests__/link-step-surface.test.js +0 -172
  22. package/dist/components/data/__tests__/list-sort-state.svelte.test.js +0 -71
  23. package/dist/components/data/__tests__/list-sort.test.js +0 -91
  24. package/dist/components/data/__tests__/mounted-data-surface.test.js +0 -794
  25. package/dist/components/data/__tests__/responsive-table.fixture.svelte +0 -29
  26. package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts +0 -7
  27. package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts.map +0 -1
  28. package/dist/components/data/__tests__/surface-navigation.test.js +0 -121
  29. package/dist/components/display/__tests__/ConfidenceBadge.test.js +0 -109
  30. package/dist/components/display/__tests__/CurrencyDisplay.test.js +0 -304
  31. package/dist/components/display/__tests__/CurrencyDisplayI18nHarness.svelte +0 -20
  32. package/dist/components/display/__tests__/CurrencyDisplayI18nHarness.svelte.d.ts +0 -19
  33. package/dist/components/display/__tests__/CurrencyDisplayI18nHarness.svelte.d.ts.map +0 -1
  34. package/dist/components/display/__tests__/CurrencyDisplaySsrHarness.svelte +0 -7
  35. package/dist/components/display/__tests__/CurrencyDisplaySsrHarness.svelte.d.ts +0 -19
  36. package/dist/components/display/__tests__/CurrencyDisplaySsrHarness.svelte.d.ts.map +0 -1
  37. package/dist/components/display/__tests__/DateDisplay.test.js +0 -195
  38. package/dist/components/display/__tests__/Icon.test.js +0 -223
  39. package/dist/components/display/__tests__/StatusBadge.test.js +0 -191
  40. package/dist/components/feedback/__tests__/ConfirmDialog.test.js +0 -181
  41. package/dist/components/feedback/__tests__/LoadingOverlay.test.js +0 -113
  42. package/dist/components/feedback/__tests__/Modal.test.js +0 -72
  43. package/dist/components/feedback/__tests__/PhoneSheet.test.js +0 -101
  44. package/dist/components/feedback/__tests__/ProgressBar.test.js +0 -89
  45. package/dist/components/feedback/__tests__/WorkingStrip.test.js +0 -172
  46. package/dist/components/feedback/__tests__/confirm-message.fixture.svelte +0 -9
  47. package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts +0 -19
  48. package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts.map +0 -1
  49. package/dist/components/feedback/__tests__/toast.test.js +0 -190
  50. package/dist/components/forms/__tests__/CameraCapture.test.js +0 -504
  51. package/dist/components/forms/__tests__/Combobox.test.js +0 -354
  52. package/dist/components/forms/__tests__/ControlHitArea.test.js +0 -73
  53. package/dist/components/forms/__tests__/DictationButton.test.js +0 -176
  54. package/dist/components/forms/__tests__/Fieldset.test.js +0 -37
  55. package/dist/components/forms/__tests__/Form.test.js +0 -201
  56. package/dist/components/forms/__tests__/FormActionBar.test.js +0 -22
  57. package/dist/components/forms/__tests__/FormGroup.test.js +0 -48
  58. package/dist/components/forms/__tests__/Input.test.js +0 -49
  59. package/dist/components/forms/__tests__/Listbox.test.js +0 -303
  60. package/dist/components/forms/__tests__/MultiSelect.test.js +0 -281
  61. package/dist/components/forms/__tests__/SearchInput.test.js +0 -92
  62. package/dist/components/forms/__tests__/Select.test.js +0 -37
  63. package/dist/components/forms/__tests__/SignaturePad.test.js +0 -338
  64. package/dist/components/forms/__tests__/StagedControlReview.test.js +0 -702
  65. package/dist/components/forms/__tests__/Textarea.test.js +0 -39
  66. package/dist/components/forms/__tests__/Toggle.test.js +0 -87
  67. package/dist/components/forms/__tests__/camera-capture-session.test.js +0 -207
  68. package/dist/components/forms/__tests__/capture-form-retry.test.js +0 -166
  69. package/dist/components/forms/__tests__/capture-form.fixture.svelte +0 -38
  70. package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts +0 -10
  71. package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts.map +0 -1
  72. package/dist/components/forms/__tests__/capture-test-env.d.ts +0 -70
  73. package/dist/components/forms/__tests__/capture-test-env.d.ts.map +0 -1
  74. package/dist/components/forms/__tests__/capture-test-env.js +0 -122
  75. package/dist/components/forms/__tests__/capture-touch.fixture.svelte +0 -15
  76. package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts +0 -8
  77. package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts.map +0 -1
  78. package/dist/components/forms/__tests__/combobox-form.fixture.svelte +0 -29
  79. package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts +0 -14
  80. package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts.map +0 -1
  81. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte +0 -19
  82. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts +0 -11
  83. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts.map +0 -1
  84. package/dist/components/forms/__tests__/composite-controls.fixture.svelte +0 -47
  85. package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts +0 -12
  86. package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts.map +0 -1
  87. package/dist/components/forms/__tests__/composite-controls.test.js +0 -128
  88. package/dist/components/forms/__tests__/composite-registration.fixture.svelte +0 -29
  89. package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts +0 -8
  90. package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts.map +0 -1
  91. package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte +0 -41
  92. package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte.d.ts +0 -13
  93. package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte.d.ts.map +0 -1
  94. package/dist/components/forms/__tests__/control-interaction.test.js +0 -3421
  95. package/dist/components/forms/__tests__/control-proposals.test.js +0 -63
  96. package/dist/components/forms/__tests__/core-controls.fixture.svelte +0 -50
  97. package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts +0 -9
  98. package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts.map +0 -1
  99. package/dist/components/forms/__tests__/core-controls.test.js +0 -311
  100. package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte +0 -25
  101. package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte.d.ts +0 -8
  102. package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte.d.ts.map +0 -1
  103. package/dist/components/forms/__tests__/dictation-recording.test.js +0 -582
  104. package/dist/components/forms/__tests__/dictation.test.js +0 -486
  105. package/dist/components/forms/__tests__/form-attachment.fixture.svelte +0 -28
  106. package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts +0 -11
  107. package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts.map +0 -1
  108. package/dist/components/forms/__tests__/form-group-input.fixture.svelte +0 -16
  109. package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts +0 -9
  110. package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts.map +0 -1
  111. package/dist/components/forms/__tests__/form-retry-form.fixture.svelte +0 -31
  112. package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts +0 -13
  113. package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts.map +0 -1
  114. package/dist/components/forms/__tests__/form-retry-form.test.js +0 -107
  115. package/dist/components/forms/__tests__/hidden-controls-form-retry.test.js +0 -69
  116. package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte +0 -29
  117. package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts +0 -9
  118. package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts.map +0 -1
  119. package/dist/components/forms/__tests__/listbox-form.fixture.svelte +0 -27
  120. package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts +0 -13
  121. package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts.map +0 -1
  122. package/dist/components/forms/__tests__/long-press.test.js +0 -175
  123. package/dist/components/forms/__tests__/multiselect-form.fixture.svelte +0 -27
  124. package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts +0 -13
  125. package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts.map +0 -1
  126. package/dist/components/forms/__tests__/native-file-field.test.js +0 -177
  127. package/dist/components/forms/__tests__/place-picker.fixture.svelte +0 -33
  128. package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts +0 -12
  129. package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts.map +0 -1
  130. package/dist/components/forms/__tests__/segmented-posting.fixture.svelte +0 -28
  131. package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts +0 -12
  132. package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts.map +0 -1
  133. package/dist/components/forms/__tests__/segmented-posting.test.js +0 -92
  134. package/dist/components/forms/__tests__/select-interaction.fixture.svelte +0 -25
  135. package/dist/components/forms/__tests__/select-interaction.fixture.svelte.d.ts +0 -9
  136. package/dist/components/forms/__tests__/select-interaction.fixture.svelte.d.ts.map +0 -1
  137. package/dist/components/forms/__tests__/signature-pad-logic.test.js +0 -54
  138. package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte +0 -21
  139. package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte.d.ts +0 -8
  140. package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte.d.ts.map +0 -1
  141. package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte +0 -23
  142. package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte.d.ts +0 -8
  143. package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte.d.ts.map +0 -1
  144. package/dist/components/forms/__tests__/staged-review.fixture.svelte +0 -56
  145. package/dist/components/forms/__tests__/staged-review.fixture.svelte.d.ts +0 -12
  146. package/dist/components/forms/__tests__/staged-review.fixture.svelte.d.ts.map +0 -1
  147. package/dist/components/forms/__tests__/touch-density.fixture.svelte +0 -34
  148. package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts +0 -8
  149. package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts.map +0 -1
  150. package/dist/components/forms/__tests__/touch-density.test.js +0 -66
  151. package/dist/components/forms/form-retry/__tests__/boundaries.test.js +0 -23
  152. package/dist/components/forms/form-retry/__tests__/content-digest.test.js +0 -70
  153. package/dist/components/forms/form-retry/__tests__/controller.test.js +0 -802
  154. package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts +0 -82
  155. package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts.map +0 -1
  156. package/dist/components/forms/form-retry/__tests__/fake-kit.js +0 -180
  157. package/dist/components/forms/form-retry/__tests__/form-draft.test.js +0 -169
  158. package/dist/components/forms/form-retry/__tests__/ssr.test.js +0 -37
  159. package/dist/components/forms/form-retry/__tests__/storage.d.ts +0 -26
  160. package/dist/components/forms/form-retry/__tests__/storage.d.ts.map +0 -1
  161. package/dist/components/forms/form-retry/__tests__/storage.js +0 -63
  162. package/dist/components/forms/form-retry/__tests__/submission-key.test.js +0 -236
  163. package/dist/components/forms/form-retry/__tests__/submitted-draft.test.js +0 -334
  164. package/dist/components/layout/__tests__/Container.test.js +0 -62
  165. package/dist/components/layout/__tests__/EmptyState.test.js +0 -83
  166. package/dist/components/layout/__tests__/Footer.test.js +0 -50
  167. package/dist/components/layout/__tests__/Grid.test.js +0 -121
  168. package/dist/components/layout/__tests__/Header.test.js +0 -48
  169. package/dist/components/layout/__tests__/Masthead.test.js +0 -93
  170. package/dist/components/layout/__tests__/PageHeader.test.js +0 -166
  171. package/dist/components/layout/__tests__/SummaryCard.test.js +0 -104
  172. package/dist/components/nav/__tests__/FilterChips.test.js +0 -94
  173. package/dist/components/nav/__tests__/LinkTabs.test.js +0 -117
  174. package/dist/components/nav/__tests__/Tabs.test.js +0 -128
  175. package/dist/components/roles/__tests__/RoleSelector.test.js +0 -134
  176. package/dist/components/ui/__tests__/Badge.test.js +0 -76
  177. package/dist/components/ui/__tests__/Button.test.js +0 -166
  178. package/dist/components/ui/__tests__/Card.test.js +0 -103
  179. package/dist/components/ui/__tests__/IconToggle.test.js +0 -59
  180. package/dist/components/ui/__tests__/IconToggleGroup.test.js +0 -30
  181. package/dist/components/ui/__tests__/Pagination.test.js +0 -113
  182. package/dist/components/ui/__tests__/Popover.test.js +0 -44
  183. package/dist/components/ui/__tests__/dropdown-popover-icon.test.js +0 -125
  184. package/dist/components/ui/__tests__/gap-primitives-interactive.test.js +0 -112
  185. package/dist/components/ui/__tests__/gap-primitives.test.js +0 -84
  186. package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte +0 -13
  187. package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte.d.ts +0 -19
  188. package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte.d.ts.map +0 -1
  189. package/dist/components/ui/__tests__/interaction-primitives.test.js +0 -31
  190. package/dist/components/ui/__tests__/phone-touch-targets.test.js +0 -39
  191. package/dist/i18n/__tests__/i18n.test.js +0 -74
  192. package/dist/i18n/__tests__/render-parity.spec.js +0 -37
  193. package/dist/svelte/__tests__/playground.test.js +0 -20
  194. package/dist/themes/__tests__/control-boundary-contrast.test.js +0 -112
  195. package/dist/themes/__tests__/create-theme.test.js +0 -79
  196. package/dist/themes/__tests__/css-generator.test.js +0 -129
  197. package/dist/themes/__tests__/happyvertical-theme.test.js +0 -231
  198. package/dist/themes/__tests__/registry.test.js +0 -43
  199. package/dist/themes/__tests__/styles-lockstep.test.js +0 -32
  200. package/dist/themes/__tests__/theme-provider.test.js +0 -147
  201. package/dist/themes/__tests__/theme-script.test.js +0 -130
  202. package/dist/themes/__tests__/token-aliases.test.js +0 -178
  203. package/dist/utils/forms/__tests__/formatters.test.js +0 -27
  204. package/dist/utils/theme/__tests__/color.test.js +0 -89
  205. package/dist/utils/theme/__tests__/typography.test.js +0 -11
@@ -1,195 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { resolveDataTableLayout } from '../DataTableLayout.js';
3
- const columns = [
4
- {
5
- id: 'account',
6
- label: 'Account',
7
- accessor: 'account',
8
- width: '180px',
9
- },
10
- {
11
- id: 'actual',
12
- label: 'Actual',
13
- accessor: 'actual',
14
- headerPath: [{ id: 'measures', label: 'Measures' }],
15
- },
16
- {
17
- id: 'budget',
18
- label: 'Budget',
19
- accessor: 'budget',
20
- headerPath: [{ id: 'measures', label: 'Measures' }],
21
- },
22
- {
23
- id: 'status',
24
- label: 'Status',
25
- accessor: 'status',
26
- role: 'status',
27
- responsive: { keepVisible: true, priority: 10 },
28
- },
29
- ];
30
- const state = {
31
- search: '',
32
- filters: [],
33
- sorting: [],
34
- page: 1,
35
- pageSize: null,
36
- columnOrder: ['account', 'actual', 'budget', 'status'],
37
- columnVisibility: columns.map((column) => ({
38
- columnId: column.id,
39
- visible: true,
40
- })),
41
- columnWidths: [],
42
- columnPinning: [],
43
- selection: { scope: 'explicit', rowIds: [] },
44
- selectedRowIds: [],
45
- expandedRowIds: [],
46
- };
47
- describe('resolveDataTableLayout', () => {
48
- it('rebuilds grouped headers after visibility, order, and pinning are resolved', () => {
49
- const layout = resolveDataTableLayout(columns, {
50
- ...state,
51
- columnOrder: ['budget', 'account', 'actual', 'status'],
52
- columnVisibility: state.columnVisibility.map((entry) => entry.columnId === 'actual' ? { ...entry, visible: false } : entry),
53
- columnPinning: [
54
- { columnId: 'account', position: 'start' },
55
- { columnId: 'status', position: 'end' },
56
- ],
57
- columnWidths: [{ columnId: 'account', width: 220 }],
58
- });
59
- expect(layout.columns.map(({ column }) => column.id)).toEqual([
60
- 'account',
61
- 'budget',
62
- 'status',
63
- ]);
64
- expect(layout.columns[0]).toMatchObject({
65
- pin: 'start',
66
- width: 220,
67
- stickyOffset: '0px',
68
- });
69
- expect(layout.columns[2]).toMatchObject({
70
- pin: 'end',
71
- stickyOffset: '0px',
72
- });
73
- expect(layout.headerRows).toHaveLength(2);
74
- expect(layout.headerRows[0]).toEqual([
75
- expect.objectContaining({ kind: 'leaf', rowspan: 2 }),
76
- expect.objectContaining({
77
- kind: 'group',
78
- label: 'Measures',
79
- colspan: 1,
80
- }),
81
- expect.objectContaining({ kind: 'leaf', rowspan: 2 }),
82
- ]);
83
- expect(layout.headerRows[1]).toEqual([
84
- expect.objectContaining({
85
- kind: 'leaf',
86
- column: expect.objectContaining({
87
- column: expect.objectContaining({ id: 'budget' }),
88
- }),
89
- }),
90
- ]);
91
- });
92
- it('splits a repeated group when restored order separates its leaves', () => {
93
- const layout = resolveDataTableLayout(columns, {
94
- ...state,
95
- columnOrder: ['actual', 'status', 'budget', 'account'],
96
- });
97
- const groups = layout.headerRows[0].filter((cell) => cell.kind === 'group');
98
- expect(groups).toHaveLength(2);
99
- expect(groups).toEqual([
100
- expect.objectContaining({ label: 'Measures', colspan: 1 }),
101
- expect.objectContaining({ label: 'Measures', colspan: 1 }),
102
- ]);
103
- });
104
- it('keeps an uneven header path rectangular with leaf row spans', () => {
105
- const layout = resolveDataTableLayout([
106
- columns[0],
107
- {
108
- ...columns[1],
109
- headerPath: [{ id: 'performance', label: 'Performance' }],
110
- },
111
- {
112
- ...columns[2],
113
- headerPath: [
114
- { id: 'performance', label: 'Performance' },
115
- { id: 'plan', label: 'Plan' },
116
- ],
117
- },
118
- ], {
119
- ...state,
120
- columnOrder: ['account', 'actual', 'budget'],
121
- columnVisibility: [
122
- { columnId: 'account', visible: true },
123
- { columnId: 'actual', visible: true },
124
- { columnId: 'budget', visible: true },
125
- ],
126
- });
127
- expect(layout.headerRows).toHaveLength(3);
128
- expect(layout.headerRows[0]).toEqual([
129
- expect.objectContaining({ kind: 'leaf', rowspan: 3 }),
130
- expect.objectContaining({
131
- kind: 'group',
132
- label: 'Performance',
133
- colspan: 2,
134
- rowspan: 1,
135
- }),
136
- ]);
137
- expect(layout.headerRows[1]).toEqual([
138
- expect.objectContaining({ kind: 'leaf', rowspan: 2 }),
139
- expect.objectContaining({ kind: 'group', label: 'Plan' }),
140
- ]);
141
- expect(layout.headerRows[2]).toEqual([
142
- expect.objectContaining({ kind: 'leaf', rowspan: 1 }),
143
- ]);
144
- });
145
- it('uses measured auto widths for sibling pins and keeps an all-pinned group aligned', () => {
146
- const layout = resolveDataTableLayout([columns[1], columns[2], columns[0]], {
147
- ...state,
148
- columnOrder: ['actual', 'budget', 'account'],
149
- columnVisibility: [
150
- { columnId: 'actual', visible: true },
151
- { columnId: 'budget', visible: true },
152
- { columnId: 'account', visible: true },
153
- ],
154
- columnPinning: [
155
- { columnId: 'actual', position: 'start' },
156
- { columnId: 'budget', position: 'start' },
157
- ],
158
- }, { actual: 104, budget: 116 });
159
- expect(layout.columns[0]).toMatchObject({
160
- column: { id: 'actual' },
161
- pin: 'start',
162
- stickyOffset: '0px',
163
- });
164
- expect(layout.columns[1]).toMatchObject({
165
- column: { id: 'budget' },
166
- pin: 'start',
167
- stickyOffset: 'calc(0px + 104px)',
168
- });
169
- expect(layout.headerRows[0]).toContainEqual(expect.objectContaining({
170
- kind: 'group',
171
- label: 'Measures',
172
- pin: 'start',
173
- stickyOffset: '0px',
174
- }));
175
- });
176
- it('uses a rendered width before a restored width for pinned offsets', () => {
177
- const layout = resolveDataTableLayout([columns[1], columns[2]], {
178
- ...state,
179
- columnOrder: ['actual', 'budget'],
180
- columnVisibility: [
181
- { columnId: 'actual', visible: true },
182
- { columnId: 'budget', visible: true },
183
- ],
184
- columnPinning: [
185
- { columnId: 'actual', position: 'start' },
186
- { columnId: 'budget', position: 'start' },
187
- ],
188
- columnWidths: [{ columnId: 'actual', width: 80 }],
189
- }, { actual: 200, budget: 120 });
190
- expect(layout.columns[1]).toMatchObject({
191
- column: { id: 'budget' },
192
- stickyOffset: 'calc(0px + 200px)',
193
- });
194
- });
195
- });
@@ -1,21 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createDataTablePerformanceColumns, createDataTablePerformanceRows, } from '../__fixtures__/DataTablePerformanceFixture.js';
3
- import { DATA_TABLE_SCALE_THRESHOLDS, recommendDataTableScaleStrategy, } from '../DataTablePerformance.js';
4
- describe('DataTable scale thresholds', () => {
5
- it('uses deterministic, stable-identity benchmark fixtures', () => {
6
- const rows = createDataTablePerformanceRows(3, 2);
7
- expect(rows.map((row) => row.id)).toEqual(['row-0', 'row-1', 'row-2']);
8
- expect(rows[2]?.values).toEqual({ value0: 6, value1: 8 });
9
- expect(createDataTablePerformanceColumns(2).map((column) => column.id)).toEqual(['name', 'group', 'value0', 'value1']);
10
- });
11
- it('recommends a bounded scale strategy from documented thresholds', () => {
12
- expect(recommendDataTableScaleStrategy(250, 20)).toBe('local');
13
- expect(recommendDataTableScaleStrategy(251, 20)).toBe('virtualization');
14
- expect(recommendDataTableScaleStrategy(1_001, 2)).toBe('manual-paging');
15
- expect(DATA_TABLE_SCALE_THRESHOLDS.manualPaging.recommendedPageSize).toBe(100);
16
- });
17
- it('rejects ambiguous scale inputs', () => {
18
- expect(() => recommendDataTableScaleStrategy(-1, 2)).toThrow(/non-negative/);
19
- expect(() => recommendDataTableScaleStrategy(2.5, 2)).toThrow(/integers/);
20
- });
21
- });
@@ -1,80 +0,0 @@
1
- import { render, screen, waitFor } from '@testing-library/svelte';
2
- import { describe, expect, it } from 'vitest';
3
- import DataTable from '../DataTable.svelte';
4
- import { createDataTableController } from '../DataTableController.js';
5
- import { responsiveColumns } from '../DataTableResponsive.js';
6
- const columns = Array.from({ length: 6 }, (_, index) => ({
7
- id: `c${index}`,
8
- label: `Column ${index}`,
9
- responsive: { priority: index, keepVisible: index === 0 },
10
- headerPath: [{ id: 'group', label: 'Group' }],
11
- }));
12
- const data = [
13
- {
14
- id: 'row',
15
- ...Object.fromEntries(columns.map((column) => [column.id, column.label])),
16
- },
17
- ];
18
- async function resize(container, width) {
19
- const root = container.querySelector('.data-table-container');
20
- Object.defineProperty(root, 'clientWidth', {
21
- configurable: true,
22
- value: width,
23
- });
24
- window.dispatchEvent(new Event('resize'));
25
- await waitFor(() => expect(root).toHaveClass(width <= 800 ? 'data-table-container--narrow' : 'data-table-container'));
26
- }
27
- describe('responsive column collapse', () => {
28
- it('keeps mandatory columns and highest priorities in display order', () => {
29
- expect(responsiveColumns(columns, 390, 160).map((column) => column.id)).toEqual(['c0', 'c5']);
30
- expect(responsiveColumns(columns, 768, 160).map((column) => column.id)).toEqual(['c0', 'c3', 'c4', 'c5']);
31
- });
32
- it('handles missing metadata, equal priorities, invalid numbers, and oversized mandatory sets', () => {
33
- const plain = columns.map((column) => ({
34
- ...column,
35
- responsive: undefined,
36
- }));
37
- expect(responsiveColumns(plain, 320, Number.NaN).map((column) => column.id)).toEqual(['c0', 'c1']);
38
- expect(responsiveColumns([{ ...plain[0], responsive: { priority: Number.NaN } }, plain[1]], 160, 160).map((column) => column.id)).toEqual(['c0']);
39
- expect(responsiveColumns(columns.map((column) => ({
40
- ...column,
41
- responsive: { keepVisible: true },
42
- })), 100, 160)).toHaveLength(6);
43
- expect(responsiveColumns([], 0, 160)).toEqual([]);
44
- });
45
- it('matches headers/cells/groups to container width and restores without mutating controller visibility', async () => {
46
- const controller = createDataTableController({
47
- initialState: { columnVisibility: [{ columnId: 'c5', visible: false }] },
48
- });
49
- const { container } = render(DataTable, {
50
- props: {
51
- data,
52
- columns,
53
- rowKey: 'id',
54
- controller,
55
- responsiveMode: 'hide-columns',
56
- },
57
- });
58
- const visibility = controller.getState().columnVisibility;
59
- await resize(container, 390);
60
- await waitFor(() => expect(screen.getAllByRole('cell')).toHaveLength(2));
61
- expect(screen.getByRole('columnheader', { name: 'Group' })).toHaveAttribute('colspan', '2');
62
- expect(screen.queryByRole('columnheader', { name: 'Column 5' })).toBeNull();
63
- expect(screen.getByRole('columnheader', { name: 'Column 4' })).toBeInTheDocument();
64
- expect(controller.getState().columnVisibility).toEqual(visibility);
65
- await resize(container, 1200);
66
- await waitFor(() => expect(screen.getAllByRole('cell')).toHaveLength(5));
67
- expect(screen.getByRole('columnheader', { name: 'Group' })).toHaveAttribute('colspan', '5');
68
- });
69
- it('preserves the scroll default regardless of narrow width', async () => {
70
- const { container } = render(DataTable, { props: { data, columns } });
71
- const root = container.querySelector('.data-table-container');
72
- Object.defineProperty(root, 'clientWidth', {
73
- configurable: true,
74
- value: 390,
75
- });
76
- window.dispatchEvent(new Event('resize'));
77
- await waitFor(() => expect(screen.getAllByRole('cell')).toHaveLength(6));
78
- expect(root).not.toHaveClass('data-table-container--narrow');
79
- });
80
- });
@@ -1,80 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { maximumDataTableVirtualScrollTop, resolveDataTableVirtualWindow, scrollTopForDataTableRow, } from '../DataTableVirtualization.js';
3
- const options = { rowHeight: 20, viewportHeight: 100, overscan: 2 };
4
- describe('DataTable virtualization window', () => {
5
- it('renders a fixed-height window with bounded overscan', () => {
6
- expect(resolveDataTableVirtualWindow({
7
- options,
8
- rowCount: 100,
9
- scrollTop: 200,
10
- })).toMatchObject({
11
- enabled: true,
12
- startIndex: 8,
13
- endIndex: 17,
14
- topSpacerHeight: 160,
15
- bottomSpacerHeight: 1_660,
16
- totalBodyHeight: 2_000,
17
- });
18
- });
19
- it('clamps a restored scroll position to the body extent', () => {
20
- expect(resolveDataTableVirtualWindow({
21
- options,
22
- rowCount: 10,
23
- scrollTop: 1_000,
24
- })).toMatchObject({
25
- startIndex: 3,
26
- endIndex: 10,
27
- topSpacerHeight: 60,
28
- bottomSpacerHeight: 0,
29
- });
30
- });
31
- it('falls back to a semantic full body for variable-height data rows', () => {
32
- expect(resolveDataTableVirtualWindow({
33
- options,
34
- rowCount: 12,
35
- scrollTop: 100,
36
- hasVariableRowHeight: true,
37
- headerRowCount: 2,
38
- summaryRowCount: 1,
39
- })).toEqual({
40
- enabled: false,
41
- reason: 'variable-row-height',
42
- startIndex: 0,
43
- endIndex: 12,
44
- topSpacerHeight: 0,
45
- bottomSpacerHeight: 0,
46
- totalBodyHeight: 0,
47
- headerRowCount: 2,
48
- summaryRowCount: 1,
49
- });
50
- });
51
- it('keeps headers and summaries outside the body calculation', () => {
52
- const withStructuralRows = resolveDataTableVirtualWindow({
53
- options,
54
- rowCount: 100,
55
- scrollTop: 0,
56
- headerRowCount: 2,
57
- summaryRowCount: 3,
58
- });
59
- expect(withStructuralRows.totalBodyHeight).toBe(2_000);
60
- expect(withStructuralRows.headerRowCount).toBe(2);
61
- expect(withStructuralRows.summaryRowCount).toBe(3);
62
- });
63
- it('extends the virtual scroll range by a measured summary footer', () => {
64
- expect(maximumDataTableVirtualScrollTop(100, options, 24)).toBe(1_924);
65
- expect(() => maximumDataTableVirtualScrollTop(1, options, -1)).toThrow(/footerHeight/);
66
- });
67
- it('derives scroll restoration from a row position, not a display key', () => {
68
- expect(scrollTopForDataTableRow(0, 100, options, 400)).toBe(0);
69
- expect(scrollTopForDataTableRow(25, 100, options, 0)).toBe(420);
70
- expect(scrollTopForDataTableRow(99, 100, options, 0)).toBe(1_900);
71
- });
72
- it('rejects invalid fixed-height configuration', () => {
73
- expect(() => resolveDataTableVirtualWindow({
74
- options: { rowHeight: 0, viewportHeight: 100 },
75
- rowCount: 1,
76
- scrollTop: 0,
77
- })).toThrow(/rowHeight/);
78
- expect(() => scrollTopForDataTableRow(2, 2, options)).toThrow(/rowIndex/);
79
- });
80
- });
@@ -1,255 +0,0 @@
1
- import { fireEvent, render, screen } from '@testing-library/svelte';
2
- import userEvent from '@testing-library/user-event';
3
- import { createRawSnippet } from 'svelte';
4
- import { describe, expect, it, vi } from 'vitest';
5
- import { expectNoA11yViolations } from '../../../test-support/a11y';
6
- import DataTable from '../DataTable.svelte';
7
- import { createDataTableController } from '../DataTableController.js';
8
- const rows = Array.from({ length: 100 }, (_, index) => ({
9
- id: `row-${index}`,
10
- name: `Record ${index.toString().padStart(5, '0')}`,
11
- }));
12
- const columns = [{ id: 'name', label: 'Name', accessor: 'name' }];
13
- const virtualization = { rowHeight: 20, viewportHeight: 100, overscan: 1 };
14
- function getScrollContainer(container) {
15
- const node = container.querySelector('.data-table-container');
16
- if (!node)
17
- throw new Error('Expected DataTable scroll container');
18
- return node;
19
- }
20
- describe('DataTable virtualization seam', () => {
21
- it('renders only a fixed-height body window with accessible row context', async () => {
22
- const { container } = render(DataTable, {
23
- props: { data: rows, columns, rowKey: 'id', virtualization },
24
- });
25
- const scrollContainer = getScrollContainer(container);
26
- expect(screen.getByRole('columnheader', { name: 'Name' })).toBeInTheDocument();
27
- expect(screen.getByRole('table')).toHaveAttribute('aria-rowcount', '101');
28
- expect(getScrollContainer(container)).toHaveAttribute('role', 'region');
29
- expect(getScrollContainer(container)).toHaveAttribute('tabindex', '0');
30
- expect(screen.getByText('Record 00000')).toBeInTheDocument();
31
- expect(screen.getByText('Record 00000').closest('tr')).toHaveAttribute('aria-rowindex', '2');
32
- expect(screen.queryByText('Record 00020')).not.toBeInTheDocument();
33
- expect(container.querySelectorAll('.data-table__virtual-spacer')).toHaveLength(1);
34
- scrollContainer.scrollTop = 200;
35
- await fireEvent.scroll(scrollContainer);
36
- await vi.waitFor(() => {
37
- expect(screen.queryByText('Record 00000')).not.toBeInTheDocument();
38
- expect(screen.getByText('Record 00010')).toBeInTheDocument();
39
- });
40
- expect(screen.getByText('Record 00010').closest('tr')).toHaveAttribute('aria-rowindex', '12');
41
- expect(container.querySelectorAll('.data-table__virtual-spacer')).toHaveLength(2);
42
- });
43
- it('keeps manual pages and selected stable IDs independent of the rendered window', async () => {
44
- const footer = createRawSnippet(() => ({
45
- render: () => '<strong>Remote summary</strong>',
46
- }));
47
- const controller = createDataTableController({
48
- columnIds: ['name'],
49
- modes: { filtering: 'manual', sorting: 'manual', pagination: 'manual' },
50
- initialState: { page: 12, pageSize: 100 },
51
- });
52
- const { container } = render(DataTable, {
53
- props: {
54
- data: rows,
55
- columns,
56
- rowKey: 'id',
57
- selectable: true,
58
- controller,
59
- totalRows: 10_000,
60
- footer,
61
- virtualization,
62
- },
63
- });
64
- const scrollContainer = getScrollContainer(container);
65
- expect(screen.getByRole('table')).toHaveAttribute('aria-rowcount', '10002');
66
- expect(screen.getByText('Remote summary').closest('tr')).toHaveAttribute('aria-rowindex', '10002');
67
- await userEvent.click(screen.getAllByRole('checkbox', { name: 'Select row 1101' })[0]);
68
- expect(controller.getState().selectedRowIds).toEqual(['row-0']);
69
- scrollContainer.scrollTop = 400;
70
- await fireEvent.scroll(scrollContainer);
71
- await vi.waitFor(() => expect(screen.getByText('Record 00020')).toBeInTheDocument());
72
- expect(screen.getByText('Record 00020').closest('tr')).toHaveAttribute('aria-rowindex', '1122');
73
- expect(controller.getState().selectedRowIds).toEqual(['row-0']);
74
- expect(screen.queryByText('Record 00099')).not.toBeInTheDocument();
75
- });
76
- it('restores a controlled focused row by stable identity and reports row focus', async () => {
77
- const onFocusedRowIdChange = vi.fn();
78
- const { container } = render(DataTable, {
79
- props: {
80
- data: rows,
81
- columns,
82
- rowKey: 'id',
83
- onRowClick: () => undefined,
84
- virtualization: {
85
- ...virtualization,
86
- focusedRowId: 'row-40',
87
- onFocusedRowIdChange,
88
- },
89
- },
90
- });
91
- const scrollContainer = getScrollContainer(container);
92
- const focusedRow = await screen.findByText('Record 00040');
93
- expect(scrollContainer.scrollTop).toBeGreaterThan(0);
94
- expect(document.activeElement).toBe(focusedRow.closest('tr'));
95
- expect(onFocusedRowIdChange).toHaveBeenCalledWith('row-40');
96
- });
97
- it('restores focus without aliasing numeric and string row identities', async () => {
98
- const identityRows = [
99
- { id: 1, name: 'Numeric row' },
100
- { id: '1', name: 'String row' },
101
- ];
102
- const { container } = render(DataTable, {
103
- props: {
104
- data: identityRows,
105
- columns,
106
- rowKey: 'id',
107
- virtualization: { ...virtualization, focusedRowId: '1' },
108
- },
109
- });
110
- const stringRow = await screen.findByText('String row');
111
- expect(stringRow.closest('tr')).toHaveAttribute('data-row-id', 'string:1');
112
- expect(document.activeElement).toBe(stringRow.closest('tr'));
113
- expect(container.querySelector('tr[data-row-id="number:1"]')).toBeTruthy();
114
- });
115
- it('scrolls the virtual body from the keyboard-accessible region', async () => {
116
- const onScrollTopChange = vi.fn();
117
- const { container } = render(DataTable, {
118
- props: {
119
- data: rows,
120
- columns,
121
- rowKey: 'id',
122
- virtualization: { ...virtualization, onScrollTopChange },
123
- },
124
- });
125
- const scrollContainer = getScrollContainer(container);
126
- scrollContainer.focus();
127
- await fireEvent.keyDown(scrollContainer, { key: 'PageDown' });
128
- expect(scrollContainer.scrollTop).toBe(80);
129
- expect(onScrollTopChange).toHaveBeenCalledWith(80);
130
- await vi.waitFor(() => expect(screen.getByText('Record 00004')).toBeInTheDocument());
131
- });
132
- it('stripes virtual rows by their logical display index', async () => {
133
- const { container } = render(DataTable, {
134
- props: {
135
- data: rows,
136
- columns,
137
- rowKey: 'id',
138
- striped: true,
139
- virtualization,
140
- },
141
- });
142
- const scrollContainer = getScrollContainer(container);
143
- scrollContainer.scrollTop = 40;
144
- await fireEvent.scroll(scrollContainer);
145
- await vi.waitFor(() => expect(screen.getByText('Record 00001')).toBeInTheDocument());
146
- expect(screen.getByText('Record 00001').closest('tr')).toHaveClass('data-table__row--striped');
147
- expect(screen.getByText('Record 00002').closest('tr')).not.toHaveClass('data-table__row--striped');
148
- });
149
- it('keeps the summary footer reachable at the end of the virtual scroll range', async () => {
150
- const footer = createRawSnippet(() => ({
151
- render: () => '<strong>Summary total</strong>',
152
- }));
153
- const offsetHeight = vi
154
- .spyOn(HTMLElement.prototype, 'offsetHeight', 'get')
155
- .mockImplementation(function () {
156
- return this.tagName === 'TFOOT' ? 24 : 0;
157
- });
158
- const onScrollTopChange = vi.fn();
159
- try {
160
- const { container } = render(DataTable, {
161
- props: {
162
- data: rows,
163
- columns,
164
- rowKey: 'id',
165
- footer,
166
- virtualization: { ...virtualization, onScrollTopChange },
167
- },
168
- });
169
- const scrollContainer = getScrollContainer(container);
170
- await fireEvent.keyDown(scrollContainer, { key: 'End' });
171
- expect(scrollContainer.scrollTop).toBe(1_924);
172
- expect(onScrollTopChange).toHaveBeenCalledWith(1_924);
173
- expect(screen.getByText('Summary total')).toBeInTheDocument();
174
- }
175
- finally {
176
- offsetHeight.mockRestore();
177
- }
178
- });
179
- it('accounts for grouped headers and structural summary rows in virtual context', async () => {
180
- const offsetHeight = vi
181
- .spyOn(HTMLElement.prototype, 'offsetHeight', 'get')
182
- .mockImplementation(function () {
183
- if (this.tagName === 'TFOOT')
184
- return 24;
185
- if (this.tagName === 'TBODY' &&
186
- this.classList.contains('data-table__body--structural')) {
187
- return 20;
188
- }
189
- return 0;
190
- });
191
- const groupedColumns = [
192
- {
193
- ...columns[0],
194
- headerPath: [{ id: 'identity', label: 'Identity' }],
195
- },
196
- ];
197
- try {
198
- const { container } = render(DataTable, {
199
- props: {
200
- data: rows,
201
- columns: groupedColumns,
202
- rowKey: 'id',
203
- structuralRows: [
204
- { id: 'subtotal', kind: 'subtotal', label: 'Current page' },
205
- { id: 'footer', kind: 'footer', label: 'All rows' },
206
- ],
207
- virtualization,
208
- },
209
- });
210
- const scrollContainer = getScrollContainer(container);
211
- expect(screen.getByRole('table')).toHaveAttribute('aria-rowcount', '104');
212
- expect(screen.getByText('Record 00000').closest('tr')).toHaveAttribute('aria-rowindex', '3');
213
- expect(screen.getByText('Current page').closest('tr')).toHaveAttribute('aria-rowindex', '103');
214
- expect(screen.getByText('All rows').closest('tr')).toHaveAttribute('aria-rowindex', '104');
215
- await fireEvent.keyDown(scrollContainer, { key: 'End' });
216
- expect(scrollContainer.scrollTop).toBe(1_944);
217
- }
218
- finally {
219
- offsetHeight.mockRestore();
220
- }
221
- });
222
- it('falls back for expansion without virtual scroll behavior', async () => {
223
- const expandedContent = createRawSnippet(() => ({
224
- render: () => '<p>Variable detail</p>',
225
- }));
226
- const footer = createRawSnippet(() => ({
227
- render: () => '<strong>Summary total</strong>',
228
- }));
229
- const onScrollTopChange = vi.fn();
230
- const { container } = render(DataTable, {
231
- props: {
232
- data: rows,
233
- columns,
234
- rowKey: 'id',
235
- expandedContent,
236
- footer,
237
- virtualization: { ...virtualization, onScrollTopChange },
238
- },
239
- });
240
- expect(screen.getByText('Record 00099')).toBeInTheDocument();
241
- expect(screen.getByText('Summary total')).toBeInTheDocument();
242
- expect(container.querySelector('.data-table__virtual-spacer')).toBeNull();
243
- const scrollContainer = getScrollContainer(container);
244
- expect(scrollContainer).not.toHaveAttribute('tabindex');
245
- scrollContainer.scrollTop = 200;
246
- await fireEvent.scroll(scrollContainer);
247
- expect(onScrollTopChange).not.toHaveBeenCalled();
248
- });
249
- it('is axe-clean with virtual spacer rows', async () => {
250
- const { container } = render(DataTable, {
251
- props: { data: rows, columns, rowKey: 'id', virtualization },
252
- });
253
- await expectNoA11yViolations(container);
254
- });
255
- });