@happyvertical/smrt-ui 0.53.1 → 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,36 +0,0 @@
1
- /**
2
- * Unit tests for DataTable sort/accessor helpers (coverage uplift, S6 gate).
3
- */
4
- import { describe, expect, it } from 'vitest';
5
- import { defaultSort, getNestedValue } from '../types';
6
- describe('getNestedValue', () => {
7
- it('reads dot-notation paths', () => {
8
- expect(getNestedValue({ a: { b: 5 } }, 'a.b')).toBe(5);
9
- expect(getNestedValue({ a: 1 }, 'a')).toBe(1);
10
- });
11
- it('returns undefined for missing paths / non-objects', () => {
12
- expect(getNestedValue({ a: 1 }, 'x.y')).toBeUndefined();
13
- expect(getNestedValue(null, 'a')).toBeUndefined();
14
- });
15
- });
16
- describe('defaultSort', () => {
17
- it('returns 0 when direction is null', () => {
18
- expect(defaultSort({ a: 1 }, { a: 2 }, 'a', null)).toBe(0);
19
- });
20
- it('sorts numbers by direction', () => {
21
- expect(defaultSort({ a: 1 }, { a: 2 }, 'a', 'asc')).toBeLessThan(0);
22
- expect(defaultSort({ a: 1 }, { a: 2 }, 'a', 'desc')).toBeGreaterThan(0);
23
- });
24
- it('sorts strings and dates', () => {
25
- expect(defaultSort({ a: 'apple' }, { a: 'banana' }, 'a', 'asc')).toBeLessThan(0);
26
- expect(defaultSort({ a: new Date(1) }, { a: new Date(2) }, 'a', 'asc')).toBeLessThan(0);
27
- });
28
- it('handles nullish values', () => {
29
- expect(defaultSort({ a: null }, { a: 1 }, 'a', 'asc')).toBeGreaterThan(0);
30
- expect(defaultSort({ a: 1 }, { a: null }, 'a', 'asc')).toBeLessThan(0);
31
- expect(defaultSort({ a: null }, { a: null }, 'a', 'asc')).toBe(0);
32
- });
33
- it('falls back to string compare for mixed types', () => {
34
- expect(typeof defaultSort({ a: true }, { a: false }, 'a', 'asc')).toBe('number');
35
- });
36
- });
@@ -1,49 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { DATA_TABLE_SURFACE_CONTROL_IDS, dataTableCommandFromDataSurfaceCommand, } from '../data-table-surface.js';
3
- const identity = { surfaceId: 'pin', kind: 'table' };
4
- function command(controlId, payload) {
5
- return {
6
- version: 1,
7
- commandId: 'pin',
8
- identity,
9
- expectedRevision: 0,
10
- controlId,
11
- ...(payload === undefined ? {} : { payload }),
12
- };
13
- }
14
- /**
15
- * A minimal, valid payload for each canonical table controlId — proof that
16
- * `DATA_TABLE_SURFACE_CONTROL_IDS` names exactly the ids
17
- * `dataTableCommandFromDataSurfaceCommand` translates, not a superset or
18
- * subset. Consumers (e.g. `@happyvertical/smrt-svelte`'s
19
- * `mountListDataSurface`) rely on this enumeration to distinguish "a table
20
- * command whose payload failed to translate" from "a genuinely unknown
21
- * control" — drift here would silently break that distinction.
22
- */
23
- const VALID_PAYLOADS = {
24
- 'set-search': { search: 'x' },
25
- 'set-filters': { filters: [] },
26
- 'set-sorting': { sorting: [] },
27
- 'toggle-sorting': { columnId: 'title' },
28
- 'set-page': { page: 1 },
29
- 'set-page-size': { pageSize: 25 },
30
- 'set-column-order': { columnIds: ['title'] },
31
- 'set-column-visibility': { columns: [] },
32
- 'set-selected-rows': { rowIds: [] },
33
- 'toggle-row-selection': { rowId: 'a' },
34
- 'set-expanded-rows': { rowIds: [] },
35
- 'toggle-row-expansion': { rowId: 'a' },
36
- reset: undefined,
37
- };
38
- describe('DATA_TABLE_SURFACE_CONTROL_IDS', () => {
39
- it('names exactly the ids dataTableCommandFromDataSurfaceCommand translates', () => {
40
- for (const controlId of DATA_TABLE_SURFACE_CONTROL_IDS) {
41
- expect(dataTableCommandFromDataSurfaceCommand(command(controlId, VALID_PAYLOADS[controlId])), `expected controlId "${controlId}" to translate with its documented valid payload`).not.toBeNull();
42
- }
43
- });
44
- it('does not include a genuinely unknown/custom controlId', () => {
45
- expect(DATA_TABLE_SURFACE_CONTROL_IDS).not.toContain('refresh');
46
- expect(DATA_TABLE_SURFACE_CONTROL_IDS).not.toContain('focus');
47
- expect(dataTableCommandFromDataSurfaceCommand(command('a-bespoke-control'))).toBeNull();
48
- });
49
- });
@@ -1,172 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { createDataSurfaceRegistry } from '../data-surface.js';
3
- import { registerLinkSurface, resolveLinkTarget } from '../link-surface.js';
4
- import { registerStepSurface } from '../step-surface.js';
5
- const links = [
6
- { id: 'events', label: 'Events', href: '/s/events', group: 'Content' },
7
- { id: 'articles', label: 'Articles', href: '/s/articles', group: 'Content' },
8
- { id: 'ads', label: 'Advertising', href: '/s/ads' },
9
- ];
10
- let sequence = 0;
11
- function command(surfaceId, kind, controlId, expectedRevision, payload) {
12
- sequence += 1;
13
- return {
14
- version: 1,
15
- commandId: `c${sequence}`,
16
- identity: { surfaceId, kind },
17
- expectedRevision,
18
- controlId,
19
- ...(payload === undefined ? {} : { payload: payload }),
20
- };
21
- }
22
- describe('link surface', () => {
23
- it('resolves by id, label, and unique prefix; refuses ambiguity', () => {
24
- expect(resolveLinkTarget('events', links)).toBe('/s/events');
25
- expect(resolveLinkTarget({ target: 'ARTICLES' }, links)).toBe('/s/articles');
26
- expect(resolveLinkTarget({ page: 'adv' }, links)).toBe('/s/ads');
27
- expect(resolveLinkTarget('a', links)).toBeNull();
28
- expect(resolveLinkTarget({ nope: 1 }, links)).toBeNull();
29
- });
30
- it('publishes links without hrefs and navigates through the open control', async () => {
31
- const registry = createDataSurfaceRegistry();
32
- const navigate = vi.fn();
33
- const handle = registerLinkSurface({
34
- registry,
35
- surfaceId: 'site-sections',
36
- label: 'Site sections',
37
- description: 'Pages in this site',
38
- links,
39
- navigate,
40
- });
41
- const snapshot = registry.inspect({
42
- surfaceId: 'site-sections',
43
- kind: 'list',
44
- });
45
- expect(JSON.stringify(snapshot?.state)).not.toContain('/s/');
46
- expect(snapshot?.state.links).toEqual([
47
- { id: 'events', label: 'Events', group: 'Content' },
48
- { id: 'articles', label: 'Articles', group: 'Content' },
49
- { id: 'ads', label: 'Advertising' },
50
- ]);
51
- const ok = await registry.execute(command('site-sections', 'list', 'open', snapshot?.revision ?? 0, {
52
- target: 'Events',
53
- }));
54
- expect(ok.ok).toBe(true);
55
- expect(navigate).toHaveBeenCalledWith('/s/events');
56
- const refused = await registry.execute(command('site-sections', 'list', 'open', snapshot?.revision ?? 0, {
57
- target: 'https://evil.example',
58
- }));
59
- expect(refused.ok).toBe(false);
60
- handle.update({ state: { active: 'events' } });
61
- expect(registry.inspect({ surfaceId: 'site-sections', kind: 'list' })?.revision).toBe((snapshot?.revision ?? 0) + 1);
62
- handle.destroy();
63
- expect(registry.list()).toHaveLength(0);
64
- });
65
- });
66
- describe('step surface', () => {
67
- const steps = [
68
- { id: 'town', label: 'Your town' },
69
- { id: 'features', label: 'Features' },
70
- { id: 'live', label: 'Go live' },
71
- ];
72
- const identity = { surfaceId: 'new-site-steps', kind: 'custom' };
73
- it('advances a non-writing step through the page handler', async () => {
74
- const registry = createDataSurfaceRegistry();
75
- const next = vi.fn(() => true);
76
- const handle = registerStepSurface({
77
- registry,
78
- surfaceId: 'new-site-steps',
79
- label: 'New site steps',
80
- description: 'Start a new town site',
81
- steps,
82
- current: 'town',
83
- next,
84
- });
85
- const before = registry.inspect(identity);
86
- expect(before?.state).toMatchObject({
87
- current: 'town',
88
- currentLabel: 'Your town',
89
- stepNumber: 1,
90
- stepCount: 3,
91
- nextWrites: false,
92
- awaitingPerson: false,
93
- });
94
- const result = await registry.execute(command('new-site-steps', 'custom', 'next', before?.revision ?? 0));
95
- expect(result.ok).toBe(true);
96
- expect(next).toHaveBeenCalledTimes(1);
97
- handle.update({
98
- current: 'features',
99
- steps: [{ ...steps[0], complete: true }, steps[1], steps[2]],
100
- });
101
- const after = registry.inspect(identity);
102
- expect(after?.state.current).toBe('features');
103
- expect(after?.state.steps).toEqual([
104
- { id: 'town', label: 'Your town', status: 'complete' },
105
- { id: 'features', label: 'Features', status: 'current' },
106
- { id: 'live', label: 'Go live', status: 'upcoming' },
107
- ]);
108
- });
109
- it('refuses when the page handler rejects the step (validation failed)', async () => {
110
- const registry = createDataSurfaceRegistry();
111
- registerStepSurface({
112
- registry,
113
- surfaceId: 'new-site-steps',
114
- label: 'New site steps',
115
- description: 'Start a new town site',
116
- steps,
117
- current: 'town',
118
- next: () => false,
119
- });
120
- const result = await registry.execute(command('new-site-steps', 'custom', 'next', 1));
121
- expect(result.ok).toBe(false);
122
- });
123
- it('never presses a writing step: it shows the button and waits for the person', async () => {
124
- const registry = createDataSurfaceRegistry();
125
- const create = vi.fn();
126
- const showNext = vi.fn();
127
- const handle = registerStepSurface({
128
- registry,
129
- surfaceId: 'new-site-steps',
130
- label: 'New site steps',
131
- description: 'Start a new town site',
132
- steps,
133
- current: 'features',
134
- next: create,
135
- nextWrites: true,
136
- showNext,
137
- });
138
- const result = await registry.execute(command('new-site-steps', 'custom', 'next', 1));
139
- expect(result.ok).toBe(true);
140
- expect(create).not.toHaveBeenCalled();
141
- expect(showNext).toHaveBeenCalledTimes(1);
142
- expect(registry.inspect(identity)?.state).toMatchObject({
143
- nextWrites: true,
144
- awaitingPerson: true,
145
- });
146
- handle.update({ current: 'live', nextWrites: false });
147
- expect(registry.inspect(identity)?.state.awaitingPerson).toBe(false);
148
- });
149
- it('goes back and jumps only to available steps', async () => {
150
- const registry = createDataSurfaceRegistry();
151
- const back = vi.fn();
152
- const goTo = vi.fn();
153
- registerStepSurface({
154
- registry,
155
- surfaceId: 'new-site-steps',
156
- label: 'New site steps',
157
- description: 'Start a new town site',
158
- steps: [{ ...steps[0], complete: true }, steps[1], steps[2]],
159
- current: 'features',
160
- back,
161
- goTo,
162
- });
163
- expect((await registry.execute(command('new-site-steps', 'custom', 'back', 1)))
164
- .ok).toBe(true);
165
- expect(back).toHaveBeenCalled();
166
- expect((await registry.execute(command('new-site-steps', 'custom', 'go-to', 1, {
167
- target: 'Go live',
168
- }))).ok).toBe(false);
169
- expect((await registry.execute(command('new-site-steps', 'custom', 'go-to', 1, 'your town'))).ok).toBe(true);
170
- expect(goTo).toHaveBeenCalledWith('town');
171
- });
172
- });
@@ -1,71 +0,0 @@
1
- import { fireEvent, render, screen } from '@testing-library/svelte';
2
- import { describe, expect, it, vi } from 'vitest';
3
- import ListSortSelect from '../ListSortSelect.svelte';
4
- import { createLocalListSort, createUrlListSort, } from '../list-sort-state.svelte.js';
5
- import SortableHeader from '../SortableHeader.svelte';
6
- const spec = {
7
- columns: [{ id: 'published', firstDirection: 'desc' }, 'title'],
8
- default: { columnId: 'published', direction: 'desc' },
9
- };
10
- describe('list sort state', () => {
11
- it('local: starts at the default and toggles', () => {
12
- const list = createLocalListSort(spec);
13
- expect(list.sort).toEqual(spec.default);
14
- list.toggle('title');
15
- expect(list.sort).toEqual({ columnId: 'title', direction: 'asc' });
16
- list.toggle('title');
17
- expect(list.sort).toEqual({ columnId: 'title', direction: 'desc' });
18
- expect(list.href('title')).toBeUndefined();
19
- });
20
- it('url: reads the URL, links headers, and navigates on set', async () => {
21
- let url = new URL('https://x.test/list?sort=title&dir=asc&page=3');
22
- const navigate = vi.fn((href) => {
23
- url = new URL(href, url);
24
- });
25
- const list = createUrlListSort(spec, { url: () => url, navigate });
26
- expect(list.sort).toEqual({ columnId: 'title', direction: 'asc' });
27
- expect(list.href('title')).toBe('/list?sort=title&dir=desc');
28
- expect(list.href('published')).toBe('/list');
29
- await list.set({ columnId: 'published', direction: 'asc' });
30
- expect(navigate).toHaveBeenCalledWith('/list?sort=published&dir=asc');
31
- });
32
- it('ignores an undeclared column or unknown direction on set', async () => {
33
- const local = createLocalListSort(spec);
34
- local.set({ columnId: 'secret_score', direction: 'asc' });
35
- expect(local.sort).toEqual(spec.default);
36
- local.set({ columnId: 'title', direction: 'sideways' });
37
- expect(local.sort).toEqual(spec.default);
38
- local.set({ columnId: 'title', direction: 'asc' });
39
- expect(local.sort).toEqual({ columnId: 'title', direction: 'asc' });
40
- // An invalid initial sort starts at the default.
41
- expect(createLocalListSort(spec, { columnId: 'nope', direction: 'asc' }).sort).toEqual(spec.default);
42
- const navigate = vi.fn();
43
- const url = new URL('https://x.test/list');
44
- const remote = createUrlListSort(spec, { url: () => url, navigate });
45
- await remote.set({ columnId: 'secret_score', direction: 'desc' });
46
- await remote.set(null);
47
- expect(navigate).not.toHaveBeenCalled();
48
- });
49
- it('drives SortableHeader and ListSortSelect through `list`', async () => {
50
- const list = createLocalListSort(spec);
51
- render(SortableHeader, {
52
- props: { list, columnId: 'title', label: 'Title', as: 'div' },
53
- });
54
- const header = screen.getByRole('columnheader');
55
- expect(header.getAttribute('aria-sort')).toBe('none');
56
- await fireEvent.click(screen.getByRole('button', { name: 'Sort by Title, ascending' }));
57
- expect(list.sort).toEqual({ columnId: 'title', direction: 'asc' });
58
- render(ListSortSelect, {
59
- props: {
60
- list,
61
- columns: [
62
- { id: 'published', label: 'Published', kind: 'date' },
63
- { id: 'title', label: 'Title' },
64
- ],
65
- },
66
- });
67
- const select = screen.getByLabelText('Sort by');
68
- await fireEvent.change(select, { target: { value: 'published:desc' } });
69
- expect(list.sort).toEqual(spec.default);
70
- });
71
- });
@@ -1,91 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { compareListValues, isValidListSort, listSortAria, listSortHref, listSortOrderBy, listSortSearchParams, parseListSort, sortListRows, toggleListSort, } from '../list-sort.js';
3
- const spec = {
4
- columns: [
5
- { id: 'published', firstDirection: 'desc' },
6
- 'title',
7
- { id: 'views', firstDirection: 'desc' },
8
- ],
9
- default: { columnId: 'published', direction: 'desc' },
10
- };
11
- describe('list sort', () => {
12
- it('parses the URL, falling back to the default for unknown values', () => {
13
- expect(parseListSort(new URLSearchParams(''), spec)).toEqual(spec.default);
14
- expect(parseListSort(new URLSearchParams('sort=title&dir=desc'), spec)).toEqual({
15
- columnId: 'title',
16
- direction: 'desc',
17
- });
18
- expect(parseListSort(new URLSearchParams('sort=title'), spec)).toEqual({
19
- columnId: 'title',
20
- direction: 'asc',
21
- });
22
- expect(parseListSort(new URLSearchParams('sort=views'), spec)).toEqual({
23
- columnId: 'views',
24
- direction: 'desc',
25
- });
26
- expect(parseListSort(new URLSearchParams('sort=password&dir=asc'), spec)).toEqual(spec.default);
27
- expect(parseListSort(new URLSearchParams('dir=asc'), spec)).toEqual({
28
- columnId: 'published',
29
- direction: 'asc',
30
- });
31
- expect(parseListSort(new URLSearchParams('sort=title&dir=sideways'), spec)).toEqual({
32
- columnId: 'title',
33
- direction: 'asc',
34
- });
35
- });
36
- it('toggles asc/desc and starts a new column at its first direction', () => {
37
- const start = spec.default;
38
- expect(toggleListSort(start, 'published', spec)).toEqual({
39
- columnId: 'published',
40
- direction: 'asc',
41
- });
42
- expect(toggleListSort({ columnId: 'published', direction: 'asc' }, 'published', spec)).toEqual(start);
43
- expect(toggleListSort(start, 'title', spec)).toEqual({
44
- columnId: 'title',
45
- direction: 'asc',
46
- });
47
- expect(toggleListSort(start, 'views', spec)).toEqual({
48
- columnId: 'views',
49
- direction: 'desc',
50
- });
51
- expect(toggleListSort(start, 'nope', spec)).toBe(start);
52
- expect(isValidListSort(spec, { columnId: 'nope', direction: 'asc' })).toBe(false);
53
- });
54
- it('writes the sort to the URL, omits the default, and resets the page', () => {
55
- const params = listSortSearchParams('status=draft&page=3', { columnId: 'title', direction: 'asc' }, spec);
56
- expect(params.toString()).toBe('status=draft&sort=title&dir=asc');
57
- expect(listSortSearchParams('sort=title&dir=asc&page=2', spec.default, spec).toString()).toBe('');
58
- expect(listSortSearchParams('sort=title&dir=asc&page=2', { columnId: 'title', direction: 'asc' }, spec).toString()).toBe('sort=title&dir=asc&page=2');
59
- expect(listSortHref(new URL('https://x.test/a?page=2'), { columnId: 'views', direction: 'desc' }, spec)).toBe('/a?sort=views&dir=desc');
60
- expect(listSortHref(new URL('https://x.test/a?page=2'), spec.default, spec)).toBe('/a?page=2');
61
- });
62
- it('reports aria-sort', () => {
63
- expect(listSortAria(spec.default, 'published')).toBe('descending');
64
- expect(listSortAria({ columnId: 'title', direction: 'asc' }, 'title')).toBe('ascending');
65
- expect(listSortAria(spec.default, 'title')).toBe('none');
66
- });
67
- it('sorts rows stably with empty values last in both directions', () => {
68
- const rows = [
69
- { id: 1, at: '2026-01-02' },
70
- { id: 2, at: null },
71
- { id: 3, at: '2026-03-01' },
72
- { id: 4, at: '2026-01-02' },
73
- ];
74
- const value = (row) => row.at;
75
- expect(sortListRows(rows, { columnId: 'at', direction: 'desc' }, value).map((r) => r.id)).toEqual([3, 1, 4, 2]);
76
- expect(sortListRows(rows, { columnId: 'at', direction: 'asc' }, value).map((r) => r.id)).toEqual([1, 4, 3, 2]);
77
- expect(compareListValues('item 2', 'item 10', 'asc')).toBeLessThan(0);
78
- expect(compareListValues(new Date('2026-01-01'), new Date('2025-01-01'), 'desc')).toBeLessThan(0);
79
- });
80
- it('builds ORDER BY only from the allow-list', () => {
81
- const map = {
82
- published: 'COALESCE(c.publish_date, c.created_at)',
83
- title: 'c.title',
84
- };
85
- expect(listSortOrderBy(spec.default, map)).toBe('COALESCE(c.publish_date, c.created_at) DESC NULLS LAST');
86
- expect(listSortOrderBy({ columnId: 'title', direction: 'asc' }, map, {
87
- nulls: false,
88
- })).toBe('c.title ASC');
89
- expect(() => listSortOrderBy({ columnId: 'title; drop', direction: 'asc' }, map)).toThrow();
90
- });
91
- });