@happyvertical/smrt-svelte 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 (254) hide show
  1. package/package.json +15 -15
  2. package/dist/__tests__/app-state.test.js +0 -156
  3. package/dist/__tests__/data-surface-bridge.test.js +0 -351
  4. package/dist/__tests__/provider-mode-harness.svelte +0 -14
  5. package/dist/__tests__/provider-mode-harness.svelte.d.ts +0 -11
  6. package/dist/__tests__/provider-mode-harness.svelte.d.ts.map +0 -1
  7. package/dist/__tests__/provider-mode-probe.svelte +0 -13
  8. package/dist/__tests__/provider-mode-probe.svelte.d.ts +0 -8
  9. package/dist/__tests__/provider-mode-probe.svelte.d.ts.map +0 -1
  10. package/dist/__tests__/provider-webmcp-harness.svelte +0 -14
  11. package/dist/__tests__/provider-webmcp-harness.svelte.d.ts +0 -8
  12. package/dist/__tests__/provider-webmcp-harness.svelte.d.ts.map +0 -1
  13. package/dist/__tests__/success-never-text.test.js +0 -100
  14. package/dist/__tests__/warm-clients.test.js +0 -186
  15. package/dist/browser-ai/adapters/llm/bitgpu-inference-backend.test.js +0 -464
  16. package/dist/browser-ai/adapters/llm/webllm-inference-backend.test.js +0 -646
  17. package/dist/browser-ai/adapters/stt/browser-speech.test.js +0 -113
  18. package/dist/browser-ai/adapters/stt/dictation-source.test.js +0 -52
  19. package/dist/browser-ai/svelte/components/__tests__/ModelStatusControl.test.js +0 -172
  20. package/dist/components/board/__tests__/Board.test.js +0 -498
  21. package/dist/components/board/__tests__/BoardSsrHarness.svelte +0 -30
  22. package/dist/components/board/__tests__/BoardSsrHarness.svelte.d.ts +0 -19
  23. package/dist/components/board/__tests__/BoardSsrHarness.svelte.d.ts.map +0 -1
  24. package/dist/components/forms/__tests__/AddressInput.behavior.test.js +0 -216
  25. package/dist/components/forms/__tests__/CheckboxInput.test.js +0 -92
  26. package/dist/components/forms/__tests__/DateRangeInput.behavior.test.js +0 -135
  27. package/dist/components/forms/__tests__/DateTimeInput.behavior.test.js +0 -113
  28. package/dist/components/forms/__tests__/FileUpload.error-alert.test.js +0 -37
  29. package/dist/components/forms/__tests__/FileUpload.native-form.test.js +0 -249
  30. package/dist/components/forms/__tests__/FileUpload.test.js +0 -90
  31. package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte +0 -24
  32. package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte.d.ts +0 -10
  33. package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte.d.ts.map +0 -1
  34. package/dist/components/forms/__tests__/Form.behavior.test.js +0 -137
  35. package/dist/components/forms/__tests__/Form.extraction.test.js +0 -68
  36. package/dist/components/forms/__tests__/Form.stt-error.test.js +0 -57
  37. package/dist/components/forms/__tests__/Form.test.js +0 -86
  38. package/dist/components/forms/__tests__/Form.ui-controls.test.js +0 -119
  39. package/dist/components/forms/__tests__/Form.webmcp.test.js +0 -2336
  40. package/dist/components/forms/__tests__/FormMicButton.test.js +0 -92
  41. package/dist/components/forms/__tests__/FormScope.test.js +0 -39
  42. package/dist/components/forms/__tests__/MeasurementInput.behavior.test.js +0 -129
  43. package/dist/components/forms/__tests__/MoneyInput.behavior.test.js +0 -124
  44. package/dist/components/forms/__tests__/NumberInput.behavior.test.js +0 -151
  45. package/dist/components/forms/__tests__/PhoneInput.behavior.test.js +0 -96
  46. package/dist/components/forms/__tests__/SearchInput.test.js +0 -79
  47. package/dist/components/forms/__tests__/SelectInput.behavior.test.js +0 -132
  48. package/dist/components/forms/__tests__/TextInput.behavior.test.js +0 -131
  49. package/dist/components/forms/__tests__/TextareaInput.behavior.test.js +0 -96
  50. package/dist/components/forms/__tests__/async-invalid-field.fixture.svelte +0 -30
  51. package/dist/components/forms/__tests__/async-invalid-field.fixture.svelte.d.ts +0 -7
  52. package/dist/components/forms/__tests__/async-invalid-field.fixture.svelte.d.ts.map +0 -1
  53. package/dist/components/forms/__tests__/async-validation-form.fixture.svelte +0 -19
  54. package/dist/components/forms/__tests__/async-validation-form.fixture.svelte.d.ts +0 -9
  55. package/dist/components/forms/__tests__/async-validation-form.fixture.svelte.d.ts.map +0 -1
  56. package/dist/components/forms/__tests__/async-validation-snapshot-form.fixture.svelte +0 -21
  57. package/dist/components/forms/__tests__/async-validation-snapshot-form.fixture.svelte.d.ts +0 -8
  58. package/dist/components/forms/__tests__/async-validation-snapshot-form.fixture.svelte.d.ts.map +0 -1
  59. package/dist/components/forms/__tests__/composite-inputs-a11y.test.js +0 -69
  60. package/dist/components/forms/__tests__/compound-registration-wrapper.fixture.svelte +0 -27
  61. package/dist/components/forms/__tests__/compound-registration-wrapper.fixture.svelte.d.ts +0 -9
  62. package/dist/components/forms/__tests__/compound-registration-wrapper.fixture.svelte.d.ts.map +0 -1
  63. package/dist/components/forms/__tests__/custom-registration-field.fixture.svelte +0 -48
  64. package/dist/components/forms/__tests__/custom-registration-field.fixture.svelte.d.ts +0 -10
  65. package/dist/components/forms/__tests__/custom-registration-field.fixture.svelte.d.ts.map +0 -1
  66. package/dist/components/forms/__tests__/form-extraction-values.fixture.svelte +0 -26
  67. package/dist/components/forms/__tests__/form-extraction-values.fixture.svelte.d.ts +0 -13
  68. package/dist/components/forms/__tests__/form-extraction-values.fixture.svelte.d.ts.map +0 -1
  69. package/dist/components/forms/__tests__/form-registration-lifecycle.fixture.svelte +0 -52
  70. package/dist/components/forms/__tests__/form-registration-lifecycle.fixture.svelte.d.ts +0 -16
  71. package/dist/components/forms/__tests__/form-registration-lifecycle.fixture.svelte.d.ts.map +0 -1
  72. package/dist/components/forms/__tests__/form-scope.fixture.svelte +0 -30
  73. package/dist/components/forms/__tests__/form-scope.fixture.svelte.d.ts +0 -10
  74. package/dist/components/forms/__tests__/form-scope.fixture.svelte.d.ts.map +0 -1
  75. package/dist/components/forms/__tests__/form-with-fields.fixture.svelte +0 -158
  76. package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts +0 -51
  77. package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts.map +0 -1
  78. package/dist/components/forms/__tests__/form-with-policy-field.fixture.svelte +0 -24
  79. package/dist/components/forms/__tests__/form-with-policy-field.fixture.svelte.d.ts +0 -11
  80. package/dist/components/forms/__tests__/form-with-policy-field.fixture.svelte.d.ts.map +0 -1
  81. package/dist/components/forms/__tests__/form-with-structured-fields.fixture.svelte +0 -105
  82. package/dist/components/forms/__tests__/form-with-structured-fields.fixture.svelte.d.ts +0 -42
  83. package/dist/components/forms/__tests__/form-with-structured-fields.fixture.svelte.d.ts.map +0 -1
  84. package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte +0 -84
  85. package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte.d.ts +0 -14
  86. package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte.d.ts.map +0 -1
  87. package/dist/components/forms/__tests__/legacy-form-context.fixture.svelte +0 -44
  88. package/dist/components/forms/__tests__/legacy-form-context.fixture.svelte.d.ts +0 -9
  89. package/dist/components/forms/__tests__/legacy-form-context.fixture.svelte.d.ts.map +0 -1
  90. package/dist/components/forms/__tests__/mic-keyboard-a11y.test.js +0 -74
  91. package/dist/components/forms/__tests__/policy-field.fixture.svelte +0 -35
  92. package/dist/components/forms/__tests__/policy-field.fixture.svelte.d.ts +0 -8
  93. package/dist/components/forms/__tests__/policy-field.fixture.svelte.d.ts.map +0 -1
  94. package/dist/components/forms/__tests__/rich-inputs-a11y.test.js +0 -87
  95. package/dist/components/settings/__tests__/SettingsCatalog.test.js +0 -69
  96. package/dist/components/settings/__tests__/catalog.test.js +0 -46
  97. package/dist/components/workspace/__tests__/AdminShell.responsive.test.js +0 -597
  98. package/dist/components/workspace/__tests__/AdminShell.test.js +0 -572
  99. package/dist/components/workspace/__tests__/RoleShell.test.js +0 -772
  100. package/dist/components/workspace/__tests__/TenantNav.test.js +0 -208
  101. package/dist/components/workspace/__tests__/WorkspaceAccountMenu.test.js +0 -79
  102. package/dist/components/workspace/__tests__/WorkspaceShell.test.js +0 -630
  103. package/dist/components/workspace/__tests__/admin-shell-header-overflow.test.js +0 -25
  104. package/dist/components/workspace/__tests__/admin-shell-hotkeys.test.js +0 -34
  105. package/dist/components/workspace/__tests__/admin-shell-mobile.test.js +0 -217
  106. package/dist/components/workspace/__tests__/admin-shell-mutation-effect-harness.svelte +0 -52
  107. package/dist/components/workspace/__tests__/admin-shell-mutation-effect-harness.svelte.d.ts +0 -23
  108. package/dist/components/workspace/__tests__/admin-shell-mutation-effect-harness.svelte.d.ts.map +0 -1
  109. package/dist/components/workspace/__tests__/admin-shell-page-contracts.test.js +0 -30
  110. package/dist/components/workspace/__tests__/admin-shell-responsive-state.test.js +0 -184
  111. package/dist/components/workspace/__tests__/admin-shell-settings.test.js +0 -25
  112. package/dist/components/workspace/__tests__/admin-shell-state.test.js +0 -286
  113. package/dist/components/workspace/__tests__/admin-shell-theme-hooks.test.js +0 -36
  114. package/dist/components/workspace/__tests__/breadcrumbs-helpers.test.js +0 -141
  115. package/dist/components/workspace/__tests__/context-forwarding-harness.svelte +0 -45
  116. package/dist/components/workspace/__tests__/context-forwarding-harness.svelte.d.ts +0 -21
  117. package/dist/components/workspace/__tests__/context-forwarding-harness.svelte.d.ts.map +0 -1
  118. package/dist/components/workspace/__tests__/define-tools-dock.test.js +0 -1188
  119. package/dist/components/workspace/__tests__/harness.svelte +0 -25
  120. package/dist/components/workspace/__tests__/harness.svelte.d.ts +0 -14
  121. package/dist/components/workspace/__tests__/harness.svelte.d.ts.map +0 -1
  122. package/dist/components/workspace/__tests__/index.test.js +0 -28
  123. package/dist/components/workspace/__tests__/legacy-index.test.js +0 -9
  124. package/dist/components/workspace/__tests__/manifest-nav-helpers.test.js +0 -24
  125. package/dist/components/workspace/__tests__/manifest-nav.test.js +0 -599
  126. package/dist/components/workspace/__tests__/nav-helpers.test.js +0 -95
  127. package/dist/components/workspace/__tests__/render-harness.svelte +0 -66
  128. package/dist/components/workspace/__tests__/render-harness.svelte.d.ts +0 -32
  129. package/dist/components/workspace/__tests__/render-harness.svelte.d.ts.map +0 -1
  130. package/dist/components/workspace/__tests__/render-tools-dock.test.js +0 -260
  131. package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte +0 -58
  132. package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte.d.ts +0 -16
  133. package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte.d.ts.map +0 -1
  134. package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte +0 -41
  135. package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte.d.ts +0 -13
  136. package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte.d.ts.map +0 -1
  137. package/dist/components/workspace/__tests__/shell-chrome-components.test.js +0 -108
  138. package/dist/components/workspace/__tests__/system-feed.test.js +0 -327
  139. package/dist/components/workspace/__tests__/test-icon.svelte +0 -17
  140. package/dist/components/workspace/__tests__/test-icon.svelte.d.ts +0 -19
  141. package/dist/components/workspace/__tests__/test-icon.svelte.d.ts.map +0 -1
  142. package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte +0 -38
  143. package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte.d.ts +0 -22
  144. package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte.d.ts.map +0 -1
  145. package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.d.ts +0 -66
  146. package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.d.ts.map +0 -1
  147. package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.js +0 -115
  148. package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.d.ts +0 -15
  149. package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.d.ts.map +0 -1
  150. package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.js +0 -7
  151. package/dist/components/workspace/__tests__/typed-tool-fixture.test.js +0 -115
  152. package/dist/components/workspace/__tests__/use-harness-orphan.svelte +0 -9
  153. package/dist/components/workspace/__tests__/use-harness-orphan.svelte.d.ts +0 -19
  154. package/dist/components/workspace/__tests__/use-harness-orphan.svelte.d.ts.map +0 -1
  155. package/dist/components/workspace/__tests__/use-harness.svelte +0 -23
  156. package/dist/components/workspace/__tests__/use-harness.svelte.d.ts +0 -8
  157. package/dist/components/workspace/__tests__/use-harness.svelte.d.ts.map +0 -1
  158. package/dist/components/workspace/__tests__/use-tools-dock.test.js +0 -33
  159. package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte +0 -43
  160. package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte.d.ts +0 -11
  161. package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte.d.ts.map +0 -1
  162. package/dist/components/workspace/server/__tests__/compose-availability.test.js +0 -383
  163. package/dist/components/workspace/server/__tests__/typed-context-fixture.d.ts +0 -78
  164. package/dist/components/workspace/server/__tests__/typed-context-fixture.d.ts.map +0 -1
  165. package/dist/components/workspace/server/__tests__/typed-context-fixture.js +0 -104
  166. package/dist/hooks/__tests__/stt-consumer.fixture.svelte +0 -25
  167. package/dist/hooks/__tests__/stt-consumer.fixture.svelte.d.ts +0 -15
  168. package/dist/hooks/__tests__/stt-consumer.fixture.svelte.d.ts.map +0 -1
  169. package/dist/hooks/__tests__/stt-ownership-harness.svelte +0 -42
  170. package/dist/hooks/__tests__/stt-ownership-harness.svelte.d.ts +0 -20
  171. package/dist/hooks/__tests__/stt-ownership-harness.svelte.d.ts.map +0 -1
  172. package/dist/hooks/__tests__/useSTT-ownership.test.js +0 -102
  173. package/dist/i18n/__tests__/server.spec.js +0 -57
  174. package/dist/internal/logger.test.js +0 -54
  175. package/dist/provider-mode.test.js +0 -86
  176. package/dist/provider-webmcp.test.js +0 -244
  177. package/dist/state/__tests__/app-state-ai-lifecycle.test.js +0 -240
  178. package/dist/state/__tests__/warm-clients.test.js +0 -40
  179. package/dist/web/__tests__/activity-feed-harness.svelte +0 -32
  180. package/dist/web/__tests__/activity-feed-harness.svelte.d.ts +0 -18
  181. package/dist/web/__tests__/activity-feed-harness.svelte.d.ts.map +0 -1
  182. package/dist/web/__tests__/activity-feed.integration.svelte.test.js +0 -190
  183. package/dist/web/__tests__/activity-feed.svelte.test.js +0 -235
  184. package/dist/web/__tests__/assistant-dock.integration.svelte.test.js +0 -204
  185. package/dist/web/__tests__/data-surface-conformance.integration.svelte.test.js +0 -1392
  186. package/dist/web/__tests__/harness.svelte +0 -25
  187. package/dist/web/__tests__/harness.svelte.d.ts +0 -15
  188. package/dist/web/__tests__/harness.svelte.d.ts.map +0 -1
  189. package/dist/web/__tests__/list-data-surface.fixture.svelte +0 -59
  190. package/dist/web/__tests__/list-data-surface.fixture.svelte.d.ts +0 -14
  191. package/dist/web/__tests__/list-data-surface.fixture.svelte.d.ts.map +0 -1
  192. package/dist/web/__tests__/list-data-surface.svelte.test.js +0 -106
  193. package/dist/web/__tests__/list-data-surface.test.js +0 -651
  194. package/dist/web/__tests__/list-sort-surface.fixture.svelte +0 -41
  195. package/dist/web/__tests__/list-sort-surface.fixture.svelte.d.ts +0 -15
  196. package/dist/web/__tests__/list-sort-surface.fixture.svelte.d.ts.map +0 -1
  197. package/dist/web/__tests__/list-sort-surface.svelte.test.js +0 -59
  198. package/dist/web/__tests__/live-collection.svelte.test.js +0 -185
  199. package/dist/web/__tests__/provider-ui-registry.fixture.svelte +0 -43
  200. package/dist/web/__tests__/provider-ui-registry.fixture.svelte.d.ts +0 -13
  201. package/dist/web/__tests__/provider-ui-registry.fixture.svelte.d.ts.map +0 -1
  202. package/dist/web/__tests__/provider-ui-registry.test.js +0 -235
  203. package/dist/web/__tests__/remote-query-harness.svelte +0 -27
  204. package/dist/web/__tests__/remote-query-harness.svelte.d.ts +0 -11
  205. package/dist/web/__tests__/remote-query-harness.svelte.d.ts.map +0 -1
  206. package/dist/web/__tests__/remote-query.svelte.test.js +0 -101
  207. package/dist/web/__tests__/surfaces.child.fixture.svelte +0 -69
  208. package/dist/web/__tests__/surfaces.child.fixture.svelte.d.ts +0 -16
  209. package/dist/web/__tests__/surfaces.child.fixture.svelte.d.ts.map +0 -1
  210. package/dist/web/__tests__/surfaces.fixture.svelte +0 -26
  211. package/dist/web/__tests__/surfaces.fixture.svelte.d.ts +0 -18
  212. package/dist/web/__tests__/surfaces.fixture.svelte.d.ts.map +0 -1
  213. package/dist/web/__tests__/surfaces.svelte.test.js +0 -140
  214. package/dist/web/__tests__/update-available-harness.svelte +0 -46
  215. package/dist/web/__tests__/update-available-harness.svelte.d.ts +0 -22
  216. package/dist/web/__tests__/update-available-harness.svelte.d.ts.map +0 -1
  217. package/dist/web/__tests__/update-available.svelte.test.js +0 -137
  218. package/dist/web/__tests__/view-intent-provider.fixture.svelte +0 -28
  219. package/dist/web/__tests__/view-intent-provider.fixture.svelte.d.ts +0 -13
  220. package/dist/web/__tests__/view-intent-provider.fixture.svelte.d.ts.map +0 -1
  221. package/dist/web/__tests__/view-intent.fixture.svelte +0 -33
  222. package/dist/web/__tests__/view-intent.fixture.svelte.d.ts +0 -15
  223. package/dist/web/__tests__/view-intent.fixture.svelte.d.ts.map +0 -1
  224. package/dist/web/__tests__/view-intent.intents.d.ts +0 -14
  225. package/dist/web/__tests__/view-intent.intents.d.ts.map +0 -1
  226. package/dist/web/__tests__/view-intent.intents.js +0 -38
  227. package/dist/web/__tests__/view-intent.svelte.test.js +0 -244
  228. package/dist/web/__tests__/webmcp-composed.child.fixture.svelte +0 -73
  229. package/dist/web/__tests__/webmcp-composed.child.fixture.svelte.d.ts +0 -8
  230. package/dist/web/__tests__/webmcp-composed.child.fixture.svelte.d.ts.map +0 -1
  231. package/dist/web/__tests__/webmcp-composed.fixture.svelte +0 -52
  232. package/dist/web/__tests__/webmcp-composed.fixture.svelte.d.ts +0 -12
  233. package/dist/web/__tests__/webmcp-composed.fixture.svelte.d.ts.map +0 -1
  234. package/dist/web/__tests__/webmcp-composed.integration.svelte.test.js +0 -251
  235. package/dist/web/__tests__/webmcp-effects-reactivity.child.fixture.svelte +0 -16
  236. package/dist/web/__tests__/webmcp-effects-reactivity.child.fixture.svelte.d.ts +0 -19
  237. package/dist/web/__tests__/webmcp-effects-reactivity.child.fixture.svelte.d.ts.map +0 -1
  238. package/dist/web/__tests__/webmcp-effects-reactivity.fixture.svelte +0 -11
  239. package/dist/web/__tests__/webmcp-effects-reactivity.fixture.svelte.d.ts +0 -8
  240. package/dist/web/__tests__/webmcp-effects-reactivity.fixture.svelte.d.ts.map +0 -1
  241. package/dist/web/__tests__/webmcp-harness-reactive-samename.svelte +0 -20
  242. package/dist/web/__tests__/webmcp-harness-reactive-samename.svelte.d.ts +0 -7
  243. package/dist/web/__tests__/webmcp-harness-reactive-samename.svelte.d.ts.map +0 -1
  244. package/dist/web/__tests__/webmcp-harness-unannotated.svelte +0 -14
  245. package/dist/web/__tests__/webmcp-harness-unannotated.svelte.d.ts +0 -19
  246. package/dist/web/__tests__/webmcp-harness-unannotated.svelte.d.ts.map +0 -1
  247. package/dist/web/__tests__/webmcp-harness.svelte +0 -18
  248. package/dist/web/__tests__/webmcp-harness.svelte.d.ts +0 -7
  249. package/dist/web/__tests__/webmcp-harness.svelte.d.ts.map +0 -1
  250. package/dist/web/__tests__/webmcp-ssr.fixture.svelte +0 -13
  251. package/dist/web/__tests__/webmcp-ssr.fixture.svelte.d.ts +0 -19
  252. package/dist/web/__tests__/webmcp-ssr.fixture.svelte.d.ts.map +0 -1
  253. package/dist/web/__tests__/webmcp-ui.test.js +0 -723
  254. package/dist/web/__tests__/webmcp.test.js +0 -234
@@ -1,651 +0,0 @@
1
- import { createDataSurfaceRegistry, createDataTableController, } from '@happyvertical/smrt-ui/data';
2
- import { describe, expect, it, vi } from 'vitest';
3
- import { mountListDataSurface, } from '../list-data-surface.svelte.js';
4
- const identity = {
5
- surfaceId: 'custom-list',
6
- kind: 'list',
7
- };
8
- function descriptor(overrides = {}) {
9
- return {
10
- version: 1,
11
- identity,
12
- schemaVersion: 1,
13
- label: 'Custom list',
14
- rowKey: 'id',
15
- columns: [
16
- { id: 'id', label: 'ID', capabilities: ['read', 'project'] },
17
- {
18
- id: 'title',
19
- label: 'Title',
20
- capabilities: ['read', 'search', 'filter', 'sort', 'project'],
21
- operators: { filter: ['contains'] },
22
- },
23
- ],
24
- query: {
25
- modes: ['rows', 'count'],
26
- projectableColumnIds: ['id', 'title'],
27
- filterableColumnIds: ['title'],
28
- sortableColumnIds: ['title'],
29
- },
30
- controls: [
31
- { id: 'set-filters', label: 'Filter' },
32
- { id: 'refresh', label: 'Refresh' },
33
- { id: 'retry', label: 'Retry' },
34
- { id: 'focus', label: 'Focus' },
35
- { id: 'reveal', label: 'Reveal' },
36
- { id: 'highlight', label: 'Highlight' },
37
- { id: 'star', label: 'Star selected' },
38
- ],
39
- actions: [],
40
- limits: { maxQueryRows: 50, maxQueryBytes: 10_000, maxSelectionSize: 2 },
41
- ...overrides,
42
- };
43
- }
44
- function context(overrides = {}) {
45
- return {
46
- totalRows: 2,
47
- queryFingerprint: 'query-1',
48
- ...overrides,
49
- };
50
- }
51
- describe('mountListDataSurface', () => {
52
- it('registers a custom list, mirrors controller commands, and unregisters on destroy', async () => {
53
- const registry = createDataSurfaceRegistry();
54
- const controller = createDataTableController();
55
- const handle = mountListDataSurface({
56
- registry,
57
- descriptor: descriptor(),
58
- controller,
59
- context: context(),
60
- });
61
- const before = registry.inspect(identity);
62
- expect(before?.state.totalRows).toBe(2);
63
- const result = await registry.execute({
64
- version: 1,
65
- commandId: 'set-filter-1',
66
- identity,
67
- expectedRevision: before?.revision ?? 0,
68
- controlId: 'set-filters',
69
- payload: {
70
- filters: [{ columnId: 'title', operator: 'contains', value: 'x' }],
71
- },
72
- });
73
- expect(result.ok).toBe(true);
74
- expect(controller.getState().filters).toEqual([
75
- { columnId: 'title', operator: 'contains', value: 'x' },
76
- ]);
77
- expect(result.revision).toBeGreaterThan(before?.revision ?? -1);
78
- handle.destroy();
79
- expect(registry.inspect(identity)).toBeUndefined();
80
- });
81
- it('routes the fixed refresh/retry/focus/reveal/highlight controls to page callbacks', async () => {
82
- const registry = createDataSurfaceRegistry();
83
- const controller = createDataTableController();
84
- const refresh = vi.fn().mockResolvedValue(true);
85
- const focus = vi.fn();
86
- const handle = mountListDataSurface({
87
- registry,
88
- descriptor: descriptor(),
89
- controller,
90
- context: context(),
91
- refresh,
92
- focus,
93
- });
94
- await expect(registry.execute({
95
- version: 1,
96
- commandId: 'do-refresh',
97
- identity,
98
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
99
- controlId: 'refresh',
100
- })).resolves.toMatchObject({ ok: true });
101
- expect(refresh).toHaveBeenCalledOnce();
102
- await expect(registry.execute({
103
- version: 1,
104
- commandId: 'do-focus',
105
- identity,
106
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
107
- controlId: 'focus',
108
- })).resolves.toMatchObject({ ok: true });
109
- expect(focus).toHaveBeenCalledOnce();
110
- // retry has no callback wired: denied rather than throwing.
111
- await expect(registry.execute({
112
- version: 1,
113
- commandId: 'do-retry',
114
- identity,
115
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
116
- controlId: 'retry',
117
- })).resolves.toMatchObject({ ok: false });
118
- handle.destroy();
119
- });
120
- it('dispatches unrecognized controls through onControl, denying by default', async () => {
121
- const registry = createDataSurfaceRegistry();
122
- const controller = createDataTableController();
123
- const onControl = vi.fn().mockResolvedValue(true);
124
- const handle = mountListDataSurface({
125
- registry,
126
- descriptor: descriptor(),
127
- controller,
128
- context: context(),
129
- onControl,
130
- });
131
- await expect(registry.execute({
132
- version: 1,
133
- commandId: 'star-1',
134
- identity,
135
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
136
- controlId: 'star',
137
- payload: { rowId: 'a' },
138
- })).resolves.toMatchObject({ ok: true });
139
- expect(onControl).toHaveBeenCalledWith('star', { rowId: 'a' });
140
- const registryTwo = createDataSurfaceRegistry();
141
- const controllerTwo = createDataTableController();
142
- const noHandler = mountListDataSurface({
143
- registry: registryTwo,
144
- descriptor: descriptor({
145
- identity: { surfaceId: 'no-handler', kind: 'list' },
146
- }),
147
- controller: controllerTwo,
148
- context: context(),
149
- });
150
- await expect(registryTwo.execute({
151
- version: 1,
152
- commandId: 'star-2',
153
- identity: { surfaceId: 'no-handler', kind: 'list' },
154
- expectedRevision: registryTwo.inspect({ surfaceId: 'no-handler', kind: 'list' })
155
- ?.revision ?? 0,
156
- controlId: 'star',
157
- })).resolves.toMatchObject({ ok: false });
158
- handle.destroy();
159
- noHandler.destroy();
160
- });
161
- it('treats a void onControl return as success and passes the raw payload through', async () => {
162
- const registry = createDataSurfaceRegistry();
163
- const controller = createDataTableController();
164
- let received;
165
- const onControl = vi.fn((_controlId, payload) => {
166
- received = payload;
167
- // Intentionally no return (void) — must still count as success.
168
- });
169
- const handle = mountListDataSurface({
170
- registry,
171
- descriptor: descriptor(),
172
- controller,
173
- context: context(),
174
- onControl,
175
- });
176
- await expect(registry.execute({
177
- version: 1,
178
- commandId: 'star-void',
179
- identity,
180
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
181
- controlId: 'star',
182
- payload: ['a', 'b'],
183
- })).resolves.toMatchObject({ ok: true });
184
- expect(received).toEqual(['a', 'b']);
185
- handle.destroy();
186
- });
187
- it('denies a fixed control with no page callback even when onControl is supplied', async () => {
188
- const registry = createDataSurfaceRegistry();
189
- const controller = createDataTableController();
190
- const onControl = vi.fn().mockResolvedValue(true);
191
- const handle = mountListDataSurface({
192
- registry,
193
- descriptor: descriptor(),
194
- controller,
195
- context: context(),
196
- onControl,
197
- // No `refresh` callback wired.
198
- });
199
- await expect(registry.execute({
200
- version: 1,
201
- commandId: 'refresh-no-callback',
202
- identity,
203
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
204
- controlId: 'refresh',
205
- })).resolves.toMatchObject({ ok: false });
206
- expect(onControl).not.toHaveBeenCalled();
207
- handle.destroy();
208
- });
209
- it('rejects the reserved "table" context key at mount and on update()', () => {
210
- const registry = createDataSurfaceRegistry();
211
- const controller = createDataTableController();
212
- expect(() => mountListDataSurface({
213
- registry,
214
- descriptor: descriptor(),
215
- controller,
216
- context: context({ table: 'nope' }),
217
- })).toThrow(/reserved key "table"/);
218
- const handle = mountListDataSurface({
219
- registry,
220
- descriptor: descriptor(),
221
- controller,
222
- context: context(),
223
- });
224
- expect(() => handle.update(context({ table: 'nope' }))).toThrow(/reserved key "table"/);
225
- handle.destroy();
226
- });
227
- it('denies a declared table control whose payload fails to translate, never reaching onControl', async () => {
228
- const registry = createDataSurfaceRegistry();
229
- const controller = createDataTableController();
230
- const onControl = vi.fn().mockResolvedValue(true);
231
- const handle = mountListDataSurface({
232
- registry,
233
- descriptor: descriptor(),
234
- controller,
235
- context: context(),
236
- onControl,
237
- });
238
- // `filters` must be an array; a string fails
239
- // `dataTableCommandFromDataSurfaceCommand`'s translation and returns null.
240
- await expect(registry.execute({
241
- version: 1,
242
- commandId: 'malformed-filters',
243
- identity,
244
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
245
- controlId: 'set-filters',
246
- payload: { filters: 'not-an-array' },
247
- })).resolves.toMatchObject({ ok: false });
248
- expect(onControl).not.toHaveBeenCalled();
249
- expect(controller.getState().filters).toEqual([]);
250
- handle.destroy();
251
- });
252
- it('does not leak a controller subscription or corrupt the shared revision counter when register() throws', async () => {
253
- const registry = createDataSurfaceRegistry();
254
- const controller = createDataTableController();
255
- // A duplicate identity makes registry.register() throw synchronously.
256
- const first = mountListDataSurface({
257
- registry,
258
- descriptor: descriptor({ identity: { surfaceId: 'dup', kind: 'list' } }),
259
- controller: createDataTableController(),
260
- context: context(),
261
- });
262
- expect(() => mountListDataSurface({
263
- registry,
264
- descriptor: descriptor({
265
- identity: { surfaceId: 'dup', kind: 'list' },
266
- }),
267
- controller,
268
- context: context(),
269
- })).toThrow();
270
- // The failed mount must not have subscribed to this controller: dispatching
271
- // through it must not advance any registry-visible revision for 'dup'.
272
- const before = registry.inspect({
273
- surfaceId: 'dup',
274
- kind: 'list',
275
- })?.revision;
276
- controller.dispatch({ type: 'setSearch', search: 'x' });
277
- const after = registry.inspect({
278
- surfaceId: 'dup',
279
- kind: 'list',
280
- })?.revision;
281
- expect(after).toBe(before);
282
- // A later, successful mount of the same identity starts strictly above
283
- // the last revision the first (still-live) registration published.
284
- first.destroy();
285
- const rebound = mountListDataSurface({
286
- registry,
287
- descriptor: descriptor({ identity: { surfaceId: 'dup', kind: 'list' } }),
288
- controller,
289
- context: context(),
290
- });
291
- const reboundRevision = registry.inspect({
292
- surfaceId: 'dup',
293
- kind: 'list',
294
- })?.revision;
295
- expect(reboundRevision).toBeGreaterThan(before ?? -1);
296
- rebound.destroy();
297
- });
298
- it('rejects a registry-forbidden boundary key at update() instead of deferring the failure to a later read', async () => {
299
- const registry = createDataSurfaceRegistry();
300
- const controller = createDataTableController();
301
- const handle = mountListDataSurface({
302
- registry,
303
- descriptor: descriptor(),
304
- controller,
305
- context: context(),
306
- });
307
- const before = registry.inspect(identity);
308
- // `tenantId` is one of the registry's own boundary-forbidden keys
309
- // (FORBIDDEN_BOUNDARY_KEYS in @happyvertical/smrt-ui/data) — not
310
- // something this module hand-copies. update() must throw here, not
311
- // leave the surface poisoned for a later inspect()/execute().
312
- expect(() => handle.update(context({ tenantId: 'nope' }))).toThrow();
313
- // The surface must be unaffected: still readable, at the same revision.
314
- const after = registry.inspect(identity);
315
- expect(after).toEqual(before);
316
- handle.destroy();
317
- });
318
- it('denies a table command on a controlled controller when the settled state does not converge', async () => {
319
- const registry = createDataSurfaceRegistry();
320
- const controlledState = { filters: [] };
321
- const controller = createDataTableController({
322
- state: controlledState,
323
- onStateChange: () => {
324
- // Simulate a host that never feeds the proposed state back.
325
- },
326
- });
327
- const handle = mountListDataSurface({
328
- registry,
329
- descriptor: descriptor(),
330
- controller,
331
- context: context(),
332
- // No applyControlledState supplied — the proposal is never settled.
333
- });
334
- await expect(registry.execute({
335
- version: 1,
336
- commandId: 'controlled-unsettled',
337
- identity,
338
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
339
- controlId: 'set-filters',
340
- payload: {
341
- filters: [{ columnId: 'title', operator: 'contains', value: 'x' }],
342
- },
343
- })).resolves.toMatchObject({ ok: false });
344
- // The controller's own state must be unaffected — dispatch on a
345
- // controlled controller never applies state on its own.
346
- expect(controller.getState().filters).toEqual([]);
347
- handle.destroy();
348
- });
349
- it('acknowledges a table command on a controlled controller once applyControlledState settles it', async () => {
350
- const registry = createDataSurfaceRegistry();
351
- const controller = createDataTableController({
352
- state: { filters: [] },
353
- });
354
- const handle = mountListDataSurface({
355
- registry,
356
- descriptor: descriptor(),
357
- controller,
358
- context: context(),
359
- applyControlledState: (state) => {
360
- // Simulate a host that immediately accepts and feeds back the
361
- // proposed state.
362
- return state;
363
- },
364
- });
365
- await expect(registry.execute({
366
- version: 1,
367
- commandId: 'controlled-settled',
368
- identity,
369
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
370
- controlId: 'set-filters',
371
- payload: {
372
- filters: [{ columnId: 'title', operator: 'contains', value: 'x' }],
373
- },
374
- })).resolves.toMatchObject({ ok: true });
375
- expect(controller.getState().filters).toEqual([
376
- { columnId: 'title', operator: 'contains', value: 'x' },
377
- ]);
378
- handle.destroy();
379
- });
380
- it('merges update() context over the previous published state instead of replacing it', () => {
381
- const registry = createDataSurfaceRegistry();
382
- const controller = createDataTableController();
383
- const handle = mountListDataSurface({
384
- registry,
385
- descriptor: descriptor(),
386
- controller,
387
- context: context({ totalRows: 2, queryFingerprint: 'query-1' }),
388
- });
389
- handle.update({ totalRows: 5 });
390
- const after = registry.inspect(identity);
391
- expect(after?.state.totalRows).toBe(5);
392
- // queryFingerprint was not mentioned in this update() call and must be
393
- // retained, not dropped.
394
- expect(after?.state.queryFingerprint).toBe('query-1');
395
- handle.update({ queryFingerprint: undefined });
396
- const cleared = registry.inspect(identity);
397
- expect(cleared?.state.queryFingerprint).toBeUndefined();
398
- expect(cleared?.state.totalRows).toBe(5);
399
- handle.destroy();
400
- });
401
- it('bumps the revision when app-owned context changes via update()', async () => {
402
- const registry = createDataSurfaceRegistry();
403
- const controller = createDataTableController();
404
- const handle = mountListDataSurface({
405
- registry,
406
- descriptor: descriptor(),
407
- controller,
408
- context: context(),
409
- });
410
- const before = registry.inspect(identity)?.revision ?? 0;
411
- handle.update(context({ totalRows: 5 }));
412
- const after = registry.inspect(identity);
413
- expect(after?.revision).toBeGreaterThan(before);
414
- expect(after?.state.totalRows).toBe(5);
415
- handle.destroy();
416
- });
417
- it('ignores update() called after destroy() instead of corrupting a later mount of the same identity', () => {
418
- const registry = createDataSurfaceRegistry();
419
- const controllerA = createDataTableController();
420
- const surfaceIdentity = { surfaceId: 'reused', kind: 'list' };
421
- const first = mountListDataSurface({
422
- registry,
423
- descriptor: descriptor({ identity: surfaceIdentity }),
424
- controller: controllerA,
425
- context: context(),
426
- });
427
- first.destroy();
428
- const controllerB = createDataTableController();
429
- const second = mountListDataSurface({
430
- registry,
431
- descriptor: descriptor({ identity: surfaceIdentity }),
432
- controller: controllerB,
433
- context: context(),
434
- });
435
- const beforeStaleUpdate = registry.inspect(surfaceIdentity)?.revision ?? 0;
436
- // A callback captured by `first` (e.g. an async refresh) resolves late,
437
- // after `first.destroy()` and after `second` has already mounted the
438
- // same identity. It must be a no-op, not resurrect the shared revision
439
- // counter for an identity `first` no longer owns.
440
- expect(() => first.update(context({ totalRows: 999 }))).not.toThrow();
441
- expect(registry.inspect(surfaceIdentity)?.revision).toBe(beforeStaleUpdate);
442
- expect(registry.inspect(surfaceIdentity)?.state.totalRows).not.toBe(999);
443
- second.destroy();
444
- });
445
- describe('commandAllowed descriptor-driven refusal', () => {
446
- async function expectDeniedUnchanged(extra = {}, buildCommand) {
447
- const registry = createDataSurfaceRegistry();
448
- const controller = createDataTableController();
449
- const handle = mountListDataSurface({
450
- registry,
451
- descriptor: descriptor(),
452
- controller,
453
- context: context(),
454
- ...extra,
455
- });
456
- const before = controller.snapshot();
457
- const rev = registry.inspect(identity)?.revision ?? 0;
458
- const result = await registry.execute(buildCommand(rev));
459
- expect(result.ok).toBe(false);
460
- expect(controller.snapshot()).toEqual(before);
461
- handle.destroy();
462
- }
463
- it('denies set-filters on a column outside filterableColumnIds', async () => {
464
- await expectDeniedUnchanged({}, (rev) => ({
465
- version: 1,
466
- commandId: 'deny-1',
467
- identity,
468
- expectedRevision: rev,
469
- controlId: 'set-filters',
470
- payload: {
471
- filters: [{ columnId: 'id', operator: 'contains', value: 'x' }],
472
- },
473
- }));
474
- });
475
- it('denies set-filters with an operator outside the column allowlist', async () => {
476
- await expectDeniedUnchanged({}, (rev) => ({
477
- version: 1,
478
- commandId: 'deny-2',
479
- identity,
480
- expectedRevision: rev,
481
- controlId: 'set-filters',
482
- payload: {
483
- filters: [{ columnId: 'title', operator: 'notContains', value: 'x' }],
484
- },
485
- }));
486
- });
487
- it('denies set-sorting on a column outside sortableColumnIds', async () => {
488
- await expectDeniedUnchanged({}, (rev) => ({
489
- version: 1,
490
- commandId: 'deny-3',
491
- identity,
492
- expectedRevision: rev,
493
- controlId: 'set-sorting',
494
- payload: { sorting: [{ columnId: 'id', direction: 'asc' }] },
495
- }));
496
- });
497
- it('denies toggle-sorting on a column outside sortableColumnIds', async () => {
498
- await expectDeniedUnchanged({}, (rev) => ({
499
- version: 1,
500
- commandId: 'deny-4',
501
- identity,
502
- expectedRevision: rev,
503
- controlId: 'toggle-sorting',
504
- payload: { columnId: 'id' },
505
- }));
506
- });
507
- it('denies set-selected-rows over maxSelectionSize', async () => {
508
- await expectDeniedUnchanged({}, (rev) => ({
509
- version: 1,
510
- commandId: 'deny-5',
511
- identity,
512
- expectedRevision: rev,
513
- controlId: 'set-selected-rows',
514
- payload: { rowIds: ['a', 'b', 'c'] },
515
- }));
516
- });
517
- it('denies toggle-row-selection that would exceed maxSelectionSize', async () => {
518
- const registry = createDataSurfaceRegistry();
519
- const controller = createDataTableController();
520
- controller.dispatch({ type: 'setSelectedRows', rowIds: ['a', 'b'] });
521
- const handle = mountListDataSurface({
522
- registry,
523
- descriptor: descriptor(),
524
- controller,
525
- context: context(),
526
- });
527
- const before = controller.snapshot();
528
- await expect(registry.execute({
529
- version: 1,
530
- commandId: 'deny-6',
531
- identity,
532
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
533
- controlId: 'toggle-row-selection',
534
- payload: { rowId: 'c' },
535
- })).resolves.toMatchObject({ ok: false });
536
- expect(controller.snapshot()).toEqual(before);
537
- handle.destroy();
538
- });
539
- it('denies set-column-order naming an unreadable column', async () => {
540
- await expectDeniedUnchanged({}, (rev) => ({
541
- version: 1,
542
- commandId: 'deny-7',
543
- identity,
544
- expectedRevision: rev,
545
- controlId: 'set-column-order',
546
- payload: { columnIds: ['missing-column'] },
547
- }));
548
- });
549
- it('denies set-column-visibility naming an unreadable column', async () => {
550
- await expectDeniedUnchanged({}, (rev) => ({
551
- version: 1,
552
- commandId: 'deny-8',
553
- identity,
554
- expectedRevision: rev,
555
- controlId: 'set-column-visibility',
556
- payload: { columns: [{ columnId: 'missing-column', visible: false }] },
557
- }));
558
- });
559
- it('denies set-page-size of 0', async () => {
560
- await expectDeniedUnchanged({}, (rev) => ({
561
- version: 1,
562
- commandId: 'deny-9',
563
- identity,
564
- expectedRevision: rev,
565
- controlId: 'set-page-size',
566
- payload: { pageSize: 0 },
567
- }));
568
- });
569
- it('denies an otherwise-allowed table command when acceptsTableCommand refuses it', async () => {
570
- await expectDeniedUnchanged({ acceptsTableCommand: () => false }, (rev) => ({
571
- version: 1,
572
- commandId: 'deny-10',
573
- identity,
574
- expectedRevision: rev,
575
- controlId: 'set-filters',
576
- payload: {
577
- filters: [{ columnId: 'title', operator: 'contains', value: 'x' }],
578
- },
579
- }));
580
- });
581
- });
582
- it('routes reveal and highlight to their page callbacks', async () => {
583
- const registry = createDataSurfaceRegistry();
584
- const controller = createDataTableController();
585
- const reveal = vi.fn();
586
- const highlight = vi.fn();
587
- const handle = mountListDataSurface({
588
- registry,
589
- descriptor: descriptor(),
590
- controller,
591
- context: context(),
592
- reveal,
593
- highlight,
594
- });
595
- await expect(registry.execute({
596
- version: 1,
597
- commandId: 'do-reveal',
598
- identity,
599
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
600
- controlId: 'reveal',
601
- })).resolves.toMatchObject({ ok: true });
602
- expect(reveal).toHaveBeenCalledOnce();
603
- await expect(registry.execute({
604
- version: 1,
605
- commandId: 'do-highlight',
606
- identity,
607
- expectedRevision: registry.inspect(identity)?.revision ?? 0,
608
- controlId: 'highlight',
609
- })).resolves.toMatchObject({ ok: true });
610
- expect(highlight).toHaveBeenCalledOnce();
611
- handle.destroy();
612
- });
613
- it('seeds and reports the mounted revision via initialRevision/onRevision', () => {
614
- const registry = createDataSurfaceRegistry();
615
- const controller = createDataTableController();
616
- const onRevision = vi.fn();
617
- const handle = mountListDataSurface({
618
- registry,
619
- descriptor: descriptor(),
620
- controller,
621
- context: context(),
622
- initialRevision: 41,
623
- onRevision,
624
- });
625
- expect(registry.inspect(identity)?.revision).toBe(41);
626
- expect(onRevision).toHaveBeenCalledWith(41);
627
- handle.destroy();
628
- });
629
- it('keeps two mounted lists independently addressable', async () => {
630
- const registry = createDataSurfaceRegistry();
631
- const first = mountListDataSurface({
632
- registry,
633
- descriptor: descriptor({ identity: { surfaceId: 'a', kind: 'list' } }),
634
- controller: createDataTableController(),
635
- context: context(),
636
- });
637
- const second = mountListDataSurface({
638
- registry,
639
- descriptor: descriptor({ identity: { surfaceId: 'b', kind: 'list' } }),
640
- controller: createDataTableController(),
641
- context: context({ totalRows: 9 }),
642
- });
643
- expect(registry
644
- .list()
645
- .map((entry) => entry.identity.surfaceId)
646
- .sort()).toEqual(['a', 'b']);
647
- expect(registry.inspect({ surfaceId: 'b', kind: 'list' })?.state.totalRows).toBe(9);
648
- first.destroy();
649
- second.destroy();
650
- });
651
- });
@@ -1,41 +0,0 @@
1
- <script lang="ts">
2
- import type {
3
- DataSurfaceRegistry,
4
- ListSort,
5
- } from '@happyvertical/smrt-ui/data';
6
- import { useListSurface } from '../list-surface.svelte.js';
7
-
8
- let {
9
- registry,
10
- rows,
11
- sort = $bindable({ columnId: 'published', direction: 'desc' }),
12
- deny = false,
13
- }: {
14
- registry: DataSurfaceRegistry;
15
- rows: Array<{ id: string; title: string; published: string }>;
16
- sort?: ListSort;
17
- deny?: boolean;
18
- } = $props();
19
-
20
- useListSurface(
21
- () => ({
22
- surfaceId: 'stories-ui',
23
- label: 'Stories',
24
- description: 'Stories on this page',
25
- columns: [
26
- { id: 'id', label: 'ID', rowKey: true },
27
- { id: 'title', label: 'Title', sortable: true },
28
- { id: 'published', label: 'Published', sortable: true },
29
- ],
30
- rows,
31
- sort,
32
- onSort: (next) => {
33
- if (deny) return false;
34
- sort = next;
35
- },
36
- }),
37
- registry,
38
- );
39
- </script>
40
-
41
- <p data-testid="sort">{sort.columnId}:{sort.direction}</p>