@happyvertical/smrt-ui 0.51.39 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/AGENTS.md +25 -3
  2. package/README.md +558 -8
  3. package/dist/actions/__tests__/swipe-dismiss.test.js +69 -0
  4. package/dist/actions/swipe-dismiss.d.ts +42 -0
  5. package/dist/actions/swipe-dismiss.d.ts.map +1 -0
  6. package/dist/actions/swipe-dismiss.js +69 -0
  7. package/dist/components/calendar/Calendar.svelte +5 -0
  8. package/dist/components/calendar/Calendar.svelte.d.ts.map +1 -1
  9. package/dist/components/calendar/CalendarView.svelte +1250 -0
  10. package/dist/components/calendar/CalendarView.svelte.d.ts +66 -0
  11. package/dist/components/calendar/CalendarView.svelte.d.ts.map +1 -0
  12. package/dist/components/calendar/DayView.svelte +5 -0
  13. package/dist/components/calendar/DayView.svelte.d.ts.map +1 -1
  14. package/dist/components/calendar/__tests__/CalendarView.test.js +226 -0
  15. package/dist/components/calendar/__tests__/calendar-model.test.js +138 -0
  16. package/dist/components/calendar/calendar-model.d.ts +178 -0
  17. package/dist/components/calendar/calendar-model.d.ts.map +1 -0
  18. package/dist/components/calendar/calendar-model.js +362 -0
  19. package/dist/components/calendar/index.d.ts +12 -1
  20. package/dist/components/calendar/index.d.ts.map +1 -1
  21. package/dist/components/calendar/index.js +11 -1
  22. package/dist/components/data/CollectionList.svelte +23 -5
  23. package/dist/components/data/CollectionList.svelte.d.ts +8 -2
  24. package/dist/components/data/CollectionList.svelte.d.ts.map +1 -1
  25. package/dist/components/data/DataTable.svelte +178 -55
  26. package/dist/components/data/DataTable.svelte.d.ts.map +1 -1
  27. package/dist/components/data/DataTableController.d.ts +4 -0
  28. package/dist/components/data/DataTableController.d.ts.map +1 -1
  29. package/dist/components/data/DataTableController.js +11 -4
  30. package/dist/components/data/DataTableResponsive.d.ts +4 -0
  31. package/dist/components/data/DataTableResponsive.d.ts.map +1 -0
  32. package/dist/components/data/DataTableResponsive.js +22 -0
  33. package/dist/components/data/ListSortSelect.svelte +121 -0
  34. package/dist/components/data/ListSortSelect.svelte.d.ts +22 -0
  35. package/dist/components/data/ListSortSelect.svelte.d.ts.map +1 -0
  36. package/dist/components/data/SortableHeader.svelte +169 -0
  37. package/dist/components/data/SortableHeader.svelte.d.ts +30 -0
  38. package/dist/components/data/SortableHeader.svelte.d.ts.map +1 -0
  39. package/dist/components/data/__tests__/CollectionList.test.js +37 -0
  40. package/dist/components/data/__tests__/DataTable.test.js +76 -0
  41. package/dist/components/data/__tests__/DataTableController.test.js +35 -0
  42. package/dist/components/data/__tests__/DataTableResponsive.test.js +80 -0
  43. package/dist/components/data/__tests__/SortableHeader.test.js +101 -0
  44. package/dist/components/data/__tests__/link-step-surface.test.js +172 -0
  45. package/dist/components/data/__tests__/list-sort-state.svelte.test.js +71 -0
  46. package/dist/components/data/__tests__/list-sort.test.js +91 -0
  47. package/dist/components/data/__tests__/responsive-table.fixture.svelte +29 -0
  48. package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts +7 -0
  49. package/dist/components/data/__tests__/responsive-table.fixture.svelte.d.ts.map +1 -0
  50. package/dist/components/data/__tests__/surface-navigation.test.js +121 -0
  51. package/dist/components/data/data-surface.d.ts +1 -0
  52. package/dist/components/data/data-surface.d.ts.map +1 -1
  53. package/dist/components/data/data-surface.js +1 -0
  54. package/dist/components/data/index.d.ts +6 -0
  55. package/dist/components/data/index.d.ts.map +1 -1
  56. package/dist/components/data/index.js +6 -0
  57. package/dist/components/data/link-surface.d.ts +79 -0
  58. package/dist/components/data/link-surface.d.ts.map +1 -0
  59. package/dist/components/data/link-surface.js +155 -0
  60. package/dist/components/data/list-sort-state.svelte.d.ts +39 -0
  61. package/dist/components/data/list-sort-state.svelte.d.ts.map +1 -0
  62. package/dist/components/data/list-sort-state.svelte.js +47 -0
  63. package/dist/components/data/list-sort.d.ts +94 -0
  64. package/dist/components/data/list-sort.d.ts.map +1 -0
  65. package/dist/components/data/list-sort.js +182 -0
  66. package/dist/components/data/step-surface.d.ts +73 -0
  67. package/dist/components/data/step-surface.d.ts.map +1 -0
  68. package/dist/components/data/step-surface.js +177 -0
  69. package/dist/components/data/surface-navigation.d.ts +49 -0
  70. package/dist/components/data/surface-navigation.d.ts.map +1 -0
  71. package/dist/components/data/surface-navigation.js +100 -0
  72. package/dist/components/data/types.d.ts +27 -0
  73. package/dist/components/data/types.d.ts.map +1 -1
  74. package/dist/components/display/DateDisplay.svelte +76 -14
  75. package/dist/components/display/DateDisplay.svelte.d.ts +2 -0
  76. package/dist/components/display/DateDisplay.svelte.d.ts.map +1 -1
  77. package/dist/components/display/Icon.svelte +3 -16
  78. package/dist/components/display/Icon.svelte.d.ts +0 -9
  79. package/dist/components/display/Icon.svelte.d.ts.map +1 -1
  80. package/dist/components/display/StatusBadge.svelte +29 -2
  81. package/dist/components/display/StatusBadge.svelte.d.ts +4 -2
  82. package/dist/components/display/StatusBadge.svelte.d.ts.map +1 -1
  83. package/dist/components/display/__tests__/DateDisplay.test.js +82 -1
  84. package/dist/components/display/__tests__/Icon.test.js +132 -2
  85. package/dist/components/display/__tests__/StatusBadge.test.js +78 -0
  86. package/dist/components/display/icons.svelte.d.ts +11 -0
  87. package/dist/components/display/icons.svelte.d.ts.map +1 -0
  88. package/dist/components/display/icons.svelte.js +60 -0
  89. package/dist/components/display/index.d.ts +3 -1
  90. package/dist/components/display/index.d.ts.map +1 -1
  91. package/dist/components/display/index.js +1 -0
  92. package/dist/components/display/types.d.ts +2 -0
  93. package/dist/components/display/types.d.ts.map +1 -1
  94. package/dist/components/feedback/ConfirmDialog.svelte +59 -48
  95. package/dist/components/feedback/ConfirmDialog.svelte.d.ts +14 -2
  96. package/dist/components/feedback/ConfirmDialog.svelte.d.ts.map +1 -1
  97. package/dist/components/feedback/PhoneSheet.svelte +224 -0
  98. package/dist/components/feedback/PhoneSheet.svelte.d.ts +35 -0
  99. package/dist/components/feedback/PhoneSheet.svelte.d.ts.map +1 -0
  100. package/dist/components/feedback/ToastViewport.svelte +156 -8
  101. package/dist/components/feedback/ToastViewport.svelte.d.ts +5 -1
  102. package/dist/components/feedback/ToastViewport.svelte.d.ts.map +1 -1
  103. package/dist/components/feedback/WorkingStrip.svelte +426 -0
  104. package/dist/components/feedback/WorkingStrip.svelte.d.ts +37 -0
  105. package/dist/components/feedback/WorkingStrip.svelte.d.ts.map +1 -0
  106. package/dist/components/feedback/__tests__/ConfirmDialog.test.js +38 -20
  107. package/dist/components/feedback/__tests__/PhoneSheet.test.js +101 -0
  108. package/dist/components/feedback/__tests__/WorkingStrip.test.js +172 -0
  109. package/dist/components/feedback/__tests__/confirm-message.fixture.svelte +9 -0
  110. package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts +19 -0
  111. package/dist/components/feedback/__tests__/confirm-message.fixture.svelte.d.ts.map +1 -0
  112. package/dist/components/feedback/__tests__/toast.test.js +142 -1
  113. package/dist/components/feedback/index.d.ts +5 -1
  114. package/dist/components/feedback/index.d.ts.map +1 -1
  115. package/dist/components/feedback/index.js +5 -0
  116. package/dist/components/feedback/toast.d.ts +13 -1
  117. package/dist/components/feedback/toast.d.ts.map +1 -1
  118. package/dist/components/feedback/toast.js +51 -10
  119. package/dist/components/feedback/working-status.d.ts +34 -0
  120. package/dist/components/feedback/working-status.d.ts.map +1 -0
  121. package/dist/components/feedback/working-status.js +5 -0
  122. package/dist/components/forms/CameraCapture.svelte +619 -0
  123. package/dist/components/forms/CameraCapture.svelte.d.ts +42 -0
  124. package/dist/components/forms/CameraCapture.svelte.d.ts.map +1 -0
  125. package/dist/components/forms/Checkbox.svelte +34 -3
  126. package/dist/components/forms/Checkbox.svelte.d.ts +2 -0
  127. package/dist/components/forms/Checkbox.svelte.d.ts.map +1 -1
  128. package/dist/components/forms/Combobox.svelte +108 -12
  129. package/dist/components/forms/Combobox.svelte.d.ts +6 -0
  130. package/dist/components/forms/Combobox.svelte.d.ts.map +1 -1
  131. package/dist/components/forms/DictationButton.svelte +128 -0
  132. package/dist/components/forms/DictationButton.svelte.d.ts +19 -0
  133. package/dist/components/forms/DictationButton.svelte.d.ts.map +1 -0
  134. package/dist/components/forms/DictationStatus.svelte +164 -0
  135. package/dist/components/forms/DictationStatus.svelte.d.ts +11 -0
  136. package/dist/components/forms/DictationStatus.svelte.d.ts.map +1 -0
  137. package/dist/components/forms/Fieldset.svelte +5 -1
  138. package/dist/components/forms/Fieldset.svelte.d.ts +2 -0
  139. package/dist/components/forms/Fieldset.svelte.d.ts.map +1 -1
  140. package/dist/components/forms/Form.svelte +5 -0
  141. package/dist/components/forms/Form.svelte.d.ts +1 -0
  142. package/dist/components/forms/Form.svelte.d.ts.map +1 -1
  143. package/dist/components/forms/FormActionBar.svelte +84 -0
  144. package/dist/components/forms/FormActionBar.svelte.d.ts +23 -0
  145. package/dist/components/forms/FormActionBar.svelte.d.ts.map +1 -0
  146. package/dist/components/forms/Input.svelte +17 -1
  147. package/dist/components/forms/Input.svelte.d.ts +2 -0
  148. package/dist/components/forms/Input.svelte.d.ts.map +1 -1
  149. package/dist/components/forms/Listbox.svelte +39 -1
  150. package/dist/components/forms/Listbox.svelte.d.ts.map +1 -1
  151. package/dist/components/forms/MultiSelect.svelte +41 -1
  152. package/dist/components/forms/MultiSelect.svelte.d.ts.map +1 -1
  153. package/dist/components/forms/Radio.svelte +16 -4
  154. package/dist/components/forms/RadioGroup.svelte +7 -1
  155. package/dist/components/forms/RadioGroup.svelte.d.ts +2 -0
  156. package/dist/components/forms/RadioGroup.svelte.d.ts.map +1 -1
  157. package/dist/components/forms/SearchInput.svelte +207 -0
  158. package/dist/components/forms/SearchInput.svelte.d.ts +22 -0
  159. package/dist/components/forms/SearchInput.svelte.d.ts.map +1 -0
  160. package/dist/components/forms/SegmentedControl.svelte +152 -11
  161. package/dist/components/forms/SegmentedControl.svelte.d.ts +4 -0
  162. package/dist/components/forms/SegmentedControl.svelte.d.ts.map +1 -1
  163. package/dist/components/forms/Select.svelte +17 -1
  164. package/dist/components/forms/Select.svelte.d.ts +2 -0
  165. package/dist/components/forms/Select.svelte.d.ts.map +1 -1
  166. package/dist/components/forms/SignaturePad.svelte +436 -0
  167. package/dist/components/forms/SignaturePad.svelte.d.ts +32 -0
  168. package/dist/components/forms/SignaturePad.svelte.d.ts.map +1 -0
  169. package/dist/components/forms/StagedControlReview.svelte +21 -12
  170. package/dist/components/forms/StagedControlReview.svelte.d.ts.map +1 -1
  171. package/dist/components/forms/Switch.svelte +18 -5
  172. package/dist/components/forms/Switch.svelte.d.ts +2 -0
  173. package/dist/components/forms/Switch.svelte.d.ts.map +1 -1
  174. package/dist/components/forms/TagsInput.svelte +8 -1
  175. package/dist/components/forms/Textarea.svelte +16 -1
  176. package/dist/components/forms/Textarea.svelte.d.ts +2 -0
  177. package/dist/components/forms/Textarea.svelte.d.ts.map +1 -1
  178. package/dist/components/forms/__tests__/CameraCapture.test.js +504 -0
  179. package/dist/components/forms/__tests__/Combobox.test.js +354 -0
  180. package/dist/components/forms/__tests__/ControlHitArea.test.js +73 -0
  181. package/dist/components/forms/__tests__/DictationButton.test.js +176 -0
  182. package/dist/components/forms/__tests__/Fieldset.test.js +37 -0
  183. package/dist/components/forms/__tests__/Form.test.js +46 -0
  184. package/dist/components/forms/__tests__/FormActionBar.test.js +22 -0
  185. package/dist/components/forms/__tests__/Listbox.test.js +303 -0
  186. package/dist/components/forms/__tests__/MultiSelect.test.js +281 -0
  187. package/dist/components/forms/__tests__/SearchInput.test.js +92 -0
  188. package/dist/components/forms/__tests__/SignaturePad.test.js +338 -0
  189. package/dist/components/forms/__tests__/StagedControlReview.test.js +37 -3
  190. package/dist/components/forms/__tests__/camera-capture-session.test.js +207 -0
  191. package/dist/components/forms/__tests__/capture-form-retry.test.js +166 -0
  192. package/dist/components/forms/__tests__/capture-form.fixture.svelte +38 -0
  193. package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts +10 -0
  194. package/dist/components/forms/__tests__/capture-form.fixture.svelte.d.ts.map +1 -0
  195. package/dist/components/forms/__tests__/capture-test-env.d.ts +70 -0
  196. package/dist/components/forms/__tests__/capture-test-env.d.ts.map +1 -0
  197. package/dist/components/forms/__tests__/capture-test-env.js +122 -0
  198. package/dist/components/forms/__tests__/capture-touch.fixture.svelte +15 -0
  199. package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts +8 -0
  200. package/dist/components/forms/__tests__/capture-touch.fixture.svelte.d.ts.map +1 -0
  201. package/dist/components/forms/__tests__/combobox-form.fixture.svelte +29 -0
  202. package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts +14 -0
  203. package/dist/components/forms/__tests__/combobox-form.fixture.svelte.d.ts.map +1 -0
  204. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte +19 -0
  205. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts +11 -0
  206. package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts.map +1 -0
  207. package/dist/components/forms/__tests__/composite-registration.fixture.svelte +29 -0
  208. package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts +8 -0
  209. package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts.map +1 -0
  210. package/dist/components/forms/__tests__/control-interaction.test.js +19 -1
  211. package/dist/components/forms/__tests__/control-proposals.test.js +63 -0
  212. package/dist/components/forms/__tests__/dictation-recording.test.js +582 -0
  213. package/dist/components/forms/__tests__/dictation.test.js +486 -0
  214. package/dist/components/forms/__tests__/form-attachment.fixture.svelte +28 -0
  215. package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts +11 -0
  216. package/dist/components/forms/__tests__/form-attachment.fixture.svelte.d.ts.map +1 -0
  217. package/dist/components/forms/__tests__/form-retry-form.fixture.svelte +31 -0
  218. package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts +13 -0
  219. package/dist/components/forms/__tests__/form-retry-form.fixture.svelte.d.ts.map +1 -0
  220. package/dist/components/forms/__tests__/form-retry-form.test.js +107 -0
  221. package/dist/components/forms/__tests__/hidden-controls-form-retry.test.js +69 -0
  222. package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte +29 -0
  223. package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts +9 -0
  224. package/dist/components/forms/__tests__/hidden-controls-form.fixture.svelte.d.ts.map +1 -0
  225. package/dist/components/forms/__tests__/listbox-form.fixture.svelte +27 -0
  226. package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts +13 -0
  227. package/dist/components/forms/__tests__/listbox-form.fixture.svelte.d.ts.map +1 -0
  228. package/dist/components/forms/__tests__/long-press.test.js +175 -0
  229. package/dist/components/forms/__tests__/multiselect-form.fixture.svelte +27 -0
  230. package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts +13 -0
  231. package/dist/components/forms/__tests__/multiselect-form.fixture.svelte.d.ts.map +1 -0
  232. package/dist/components/forms/__tests__/native-file-field.test.js +177 -0
  233. package/dist/components/forms/__tests__/place-picker.fixture.svelte +33 -0
  234. package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts +12 -0
  235. package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts.map +1 -0
  236. package/dist/components/forms/__tests__/segmented-posting.fixture.svelte +28 -0
  237. package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts +12 -0
  238. package/dist/components/forms/__tests__/segmented-posting.fixture.svelte.d.ts.map +1 -0
  239. package/dist/components/forms/__tests__/segmented-posting.test.js +92 -0
  240. package/dist/components/forms/__tests__/signature-pad-logic.test.js +54 -0
  241. package/dist/components/forms/__tests__/touch-density.fixture.svelte +34 -0
  242. package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts +8 -0
  243. package/dist/components/forms/__tests__/touch-density.fixture.svelte.d.ts.map +1 -0
  244. package/dist/components/forms/__tests__/touch-density.test.js +66 -0
  245. package/dist/components/forms/audio-capture.d.ts +69 -0
  246. package/dist/components/forms/audio-capture.d.ts.map +1 -0
  247. package/dist/components/forms/audio-capture.js +193 -0
  248. package/dist/components/forms/camera-capture-session.d.ts +81 -0
  249. package/dist/components/forms/camera-capture-session.d.ts.map +1 -0
  250. package/dist/components/forms/camera-capture-session.js +130 -0
  251. package/dist/components/forms/control-interaction.d.ts +6 -0
  252. package/dist/components/forms/control-interaction.d.ts.map +1 -1
  253. package/dist/components/forms/control-interaction.js +12 -1
  254. package/dist/components/forms/control-proposals.d.ts +55 -0
  255. package/dist/components/forms/control-proposals.d.ts.map +1 -0
  256. package/dist/components/forms/control-proposals.js +188 -0
  257. package/dist/components/forms/dictation-transcribe.d.ts +36 -0
  258. package/dist/components/forms/dictation-transcribe.d.ts.map +1 -0
  259. package/dist/components/forms/dictation-transcribe.js +65 -0
  260. package/dist/components/forms/dictation.svelte.d.ts +200 -0
  261. package/dist/components/forms/dictation.svelte.d.ts.map +1 -0
  262. package/dist/components/forms/dictation.svelte.js +686 -0
  263. package/dist/components/forms/form-retry/__tests__/boundaries.test.js +23 -0
  264. package/dist/components/forms/form-retry/__tests__/content-digest.test.js +70 -0
  265. package/dist/components/forms/form-retry/__tests__/controller.test.js +802 -0
  266. package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts +82 -0
  267. package/dist/components/forms/form-retry/__tests__/fake-kit.d.ts.map +1 -0
  268. package/dist/components/forms/form-retry/__tests__/fake-kit.js +180 -0
  269. package/dist/components/forms/form-retry/__tests__/form-draft.test.js +169 -0
  270. package/dist/components/forms/form-retry/__tests__/ssr.test.js +37 -0
  271. package/dist/components/forms/form-retry/__tests__/storage.d.ts +26 -0
  272. package/dist/components/forms/form-retry/__tests__/storage.d.ts.map +1 -0
  273. package/dist/components/forms/form-retry/__tests__/storage.js +63 -0
  274. package/dist/components/forms/form-retry/__tests__/submission-key.test.js +236 -0
  275. package/dist/components/forms/form-retry/__tests__/submitted-draft.test.js +334 -0
  276. package/dist/components/forms/form-retry/content-digest.d.ts +54 -0
  277. package/dist/components/forms/form-retry/content-digest.d.ts.map +1 -0
  278. package/dist/components/forms/form-retry/content-digest.js +169 -0
  279. package/dist/components/forms/form-retry/controller.d.ts +214 -0
  280. package/dist/components/forms/form-retry/controller.d.ts.map +1 -0
  281. package/dist/components/forms/form-retry/controller.js +404 -0
  282. package/dist/components/forms/form-retry/form-draft.d.ts +68 -0
  283. package/dist/components/forms/form-retry/form-draft.d.ts.map +1 -0
  284. package/dist/components/forms/form-retry/form-draft.js +161 -0
  285. package/dist/components/forms/form-retry/index.d.ts +19 -0
  286. package/dist/components/forms/form-retry/index.d.ts.map +1 -0
  287. package/dist/components/forms/form-retry/index.js +16 -0
  288. package/dist/components/forms/form-retry/submission-key.d.ts +244 -0
  289. package/dist/components/forms/form-retry/submission-key.d.ts.map +1 -0
  290. package/dist/components/forms/form-retry/submission-key.js +342 -0
  291. package/dist/components/forms/form-retry/submitted-draft.d.ts +112 -0
  292. package/dist/components/forms/form-retry/submitted-draft.d.ts.map +1 -0
  293. package/dist/components/forms/form-retry/submitted-draft.js +270 -0
  294. package/dist/components/forms/form-retry/types.d.ts +84 -0
  295. package/dist/components/forms/form-retry/types.d.ts.map +1 -0
  296. package/dist/components/forms/form-retry/types.js +13 -0
  297. package/dist/components/forms/index.d.ts +21 -2
  298. package/dist/components/forms/index.d.ts.map +1 -1
  299. package/dist/components/forms/index.js +22 -1
  300. package/dist/components/forms/insert-text.d.ts +12 -0
  301. package/dist/components/forms/insert-text.d.ts.map +1 -0
  302. package/dist/components/forms/insert-text.js +38 -0
  303. package/dist/components/forms/long-press.d.ts +88 -0
  304. package/dist/components/forms/long-press.d.ts.map +1 -0
  305. package/dist/components/forms/long-press.js +213 -0
  306. package/dist/components/forms/native-file-field.d.ts +94 -0
  307. package/dist/components/forms/native-file-field.d.ts.map +1 -0
  308. package/dist/components/forms/native-file-field.js +147 -0
  309. package/dist/components/forms/ready-beep.d.ts +24 -0
  310. package/dist/components/forms/ready-beep.d.ts.map +1 -0
  311. package/dist/components/forms/ready-beep.js +82 -0
  312. package/dist/components/forms/signature-pad-logic.d.ts +50 -0
  313. package/dist/components/forms/signature-pad-logic.d.ts.map +1 -0
  314. package/dist/components/forms/signature-pad-logic.js +48 -0
  315. package/dist/components/forms/types.d.ts +41 -0
  316. package/dist/components/forms/types.d.ts.map +1 -1
  317. package/dist/components/layout/PageHeader.svelte +151 -17
  318. package/dist/components/layout/PageHeader.svelte.d.ts +47 -7
  319. package/dist/components/layout/PageHeader.svelte.d.ts.map +1 -1
  320. package/dist/components/layout/__tests__/PageHeader.test.js +87 -1
  321. package/dist/components/layout/index.d.ts +1 -0
  322. package/dist/components/layout/index.d.ts.map +1 -1
  323. package/dist/components/layout/index.js +1 -0
  324. package/dist/components/layout/page-header-context.d.ts +25 -0
  325. package/dist/components/layout/page-header-context.d.ts.map +1 -0
  326. package/dist/components/layout/page-header-context.js +16 -0
  327. package/dist/components/nav/FilterChips.svelte +8 -1
  328. package/dist/components/nav/FilterChips.svelte.d.ts +2 -0
  329. package/dist/components/nav/FilterChips.svelte.d.ts.map +1 -1
  330. package/dist/components/nav/Tabs.svelte +241 -0
  331. package/dist/components/nav/Tabs.svelte.d.ts +15 -0
  332. package/dist/components/nav/Tabs.svelte.d.ts.map +1 -1
  333. package/dist/components/nav/__tests__/LinkTabs.test.js +117 -0
  334. package/dist/components/nav/index.d.ts +1 -0
  335. package/dist/components/nav/index.d.ts.map +1 -1
  336. package/dist/components/nav/index.js +1 -0
  337. package/dist/components/nav/tabs-overflow.d.ts +12 -0
  338. package/dist/components/nav/tabs-overflow.d.ts.map +1 -0
  339. package/dist/components/nav/tabs-overflow.js +17 -0
  340. package/dist/components/nav/types.d.ts +8 -0
  341. package/dist/components/nav/types.d.ts.map +1 -1
  342. package/dist/components/ui/Button.svelte +16 -2
  343. package/dist/components/ui/Button.svelte.d.ts +2 -0
  344. package/dist/components/ui/Button.svelte.d.ts.map +1 -1
  345. package/dist/components/ui/Card.svelte +14 -5
  346. package/dist/components/ui/Dropdown.svelte +55 -1
  347. package/dist/components/ui/Dropdown.svelte.d.ts +8 -0
  348. package/dist/components/ui/Dropdown.svelte.d.ts.map +1 -1
  349. package/dist/components/ui/IconToggle.svelte +139 -0
  350. package/dist/components/ui/IconToggle.svelte.d.ts +32 -0
  351. package/dist/components/ui/IconToggle.svelte.d.ts.map +1 -0
  352. package/dist/components/ui/IconToggleGroup.svelte +56 -0
  353. package/dist/components/ui/IconToggleGroup.svelte.d.ts +21 -0
  354. package/dist/components/ui/IconToggleGroup.svelte.d.ts.map +1 -0
  355. package/dist/components/ui/Pagination.svelte +1 -1
  356. package/dist/components/ui/Popover.svelte +17 -1
  357. package/dist/components/ui/Popover.svelte.d.ts +8 -0
  358. package/dist/components/ui/Popover.svelte.d.ts.map +1 -1
  359. package/dist/components/ui/__tests__/IconToggle.test.js +59 -0
  360. package/dist/components/ui/__tests__/IconToggleGroup.test.js +30 -0
  361. package/dist/components/ui/__tests__/Popover.test.js +44 -0
  362. package/dist/components/ui/__tests__/dropdown-popover-icon.test.js +125 -0
  363. package/dist/components/ui/__tests__/phone-touch-targets.test.js +39 -0
  364. package/dist/components/ui/index.d.ts +2 -0
  365. package/dist/components/ui/index.d.ts.map +1 -1
  366. package/dist/components/ui/index.js +2 -0
  367. package/dist/i18n/strings.ui.d.ts +65 -0
  368. package/dist/i18n/strings.ui.d.ts.map +1 -1
  369. package/dist/i18n/strings.ui.js +72 -0
  370. package/dist/styles/tokens.css +2 -0
  371. package/dist/svelte/__tests__/playground.test.js +3 -1
  372. package/dist/svelte/playground/CalendarPreview.svelte +140 -0
  373. package/dist/svelte/playground/CalendarPreview.svelte.d.ts +4 -0
  374. package/dist/svelte/playground/CalendarPreview.svelte.d.ts.map +1 -0
  375. package/dist/svelte/playground/CapturePreview.svelte +56 -0
  376. package/dist/svelte/playground/CapturePreview.svelte.d.ts +4 -0
  377. package/dist/svelte/playground/CapturePreview.svelte.d.ts.map +1 -0
  378. package/dist/svelte/playground.d.ts.map +1 -1
  379. package/dist/svelte/playground.js +20 -0
  380. package/dist/themes/ThemeProvider.svelte +9 -0
  381. package/dist/themes/ThemeProvider.svelte.d.ts +2 -0
  382. package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -1
  383. package/dist/themes/css-generator.d.ts.map +1 -1
  384. package/dist/themes/css-generator.js +3 -0
  385. package/dist/themes/styles/glass.css +4 -0
  386. package/dist/themes/styles/happyvertical.css +4 -0
  387. package/dist/themes/styles/material.css +4 -0
  388. package/dist/themes/styles/smrt.css +4 -0
  389. package/dist/themes/styles/studio.css +4 -0
  390. package/dist/types-generic.d.ts +2 -0
  391. package/dist/types-generic.d.ts.map +1 -1
  392. package/package.json +9 -3
@@ -0,0 +1,92 @@
1
+ import { render, screen } from '@testing-library/svelte';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { expectNoA11yViolations } from '../../../test-support/a11y';
5
+ import SearchInput from '../SearchInput.svelte';
6
+ describe('SearchInput', () => {
7
+ beforeEach(() => vi.useFakeTimers({ shouldAdvanceTime: true }));
8
+ afterEach(() => vi.useRealTimers());
9
+ it('is a labelled search field in a search landmark', () => {
10
+ render(SearchInput, {
11
+ props: { value: '', onsearch: vi.fn(), placeholder: 'Search by title' },
12
+ });
13
+ expect(screen.getByRole('search')).toBeInTheDocument();
14
+ expect(screen.getByRole('searchbox', { name: 'Search' })).toHaveAttribute('placeholder', 'Search by title');
15
+ });
16
+ it('searches once, trimmed and single-line, after the last key', async () => {
17
+ const onsearch = vi.fn();
18
+ const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
19
+ render(SearchInput, { props: { value: '', onsearch, debounceMs: 300 } });
20
+ await user.type(screen.getByRole('searchbox'), ' council hall ');
21
+ expect(onsearch).not.toHaveBeenCalled();
22
+ vi.advanceTimersByTime(300);
23
+ expect(onsearch).toHaveBeenCalledTimes(1);
24
+ expect(onsearch).toHaveBeenCalledWith('council hall');
25
+ });
26
+ it('searches at once on Enter', async () => {
27
+ const onsearch = vi.fn();
28
+ const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
29
+ render(SearchInput, { props: { value: '', onsearch } });
30
+ await user.type(screen.getByRole('searchbox'), 'roads{Enter}');
31
+ expect(onsearch).toHaveBeenCalledWith('roads');
32
+ });
33
+ it('does not search for the search it is already showing', async () => {
34
+ const onsearch = vi.fn();
35
+ const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
36
+ render(SearchInput, { props: { value: 'roads', onsearch } });
37
+ await user.type(screen.getByRole('searchbox'), '{Enter}');
38
+ expect(onsearch).not.toHaveBeenCalled();
39
+ });
40
+ it('clears with the x and focuses the field', async () => {
41
+ const onsearch = vi.fn();
42
+ const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
43
+ render(SearchInput, { props: { value: 'roads', onsearch } });
44
+ const field = screen.getByRole('searchbox');
45
+ expect(field).toHaveValue('roads');
46
+ await user.click(screen.getByRole('button', { name: 'Clear search' }));
47
+ expect(onsearch).toHaveBeenCalledWith('');
48
+ expect(field).toHaveValue('');
49
+ expect(field).toHaveFocus();
50
+ expect(screen.queryByRole('button', { name: 'Clear search' })).toBeNull();
51
+ });
52
+ it('follows the applied search when it changes from outside', async () => {
53
+ const { rerender } = render(SearchInput, {
54
+ props: { value: 'roads', onsearch: vi.fn() },
55
+ });
56
+ await rerender({ value: 'parks', onsearch: vi.fn() });
57
+ expect(screen.getByRole('searchbox')).toHaveValue('parks');
58
+ });
59
+ it('inside a host form, Enter searches and never submits the host form', async () => {
60
+ const onsearch = vi.fn();
61
+ const onsubmit = vi.fn((event) => event.preventDefault());
62
+ const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
63
+ const host = document.createElement('form');
64
+ host.addEventListener('submit', onsubmit);
65
+ document.body.append(host);
66
+ render(SearchInput, { target: host, props: { value: '', onsearch } });
67
+ expect(host.querySelector('form')).toBeNull();
68
+ await user.type(screen.getByRole('searchbox'), 'roads{Enter}');
69
+ expect(onsearch).toHaveBeenCalledWith('roads');
70
+ expect(onsubmit).not.toHaveBeenCalled();
71
+ host.remove();
72
+ });
73
+ it('a value the host normalized differently does not swallow a later one', async () => {
74
+ const onsearch = vi.fn();
75
+ const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
76
+ const view = render(SearchInput, { props: { value: '', onsearch } });
77
+ await user.type(screen.getByRole('searchbox'), 'Roads{Enter}');
78
+ expect(onsearch).toHaveBeenCalledWith('Roads');
79
+ // The host applies its own normalization, then later navigates to the
80
+ // exact text the box once sent: the box must follow both.
81
+ await view.rerender({ value: 'roads', onsearch });
82
+ expect(screen.getByRole('searchbox')).toHaveValue('roads');
83
+ await view.rerender({ value: 'Roads', onsearch });
84
+ expect(screen.getByRole('searchbox')).toHaveValue('Roads');
85
+ });
86
+ it('is axe-clean', async () => {
87
+ const { container } = render(SearchInput, {
88
+ props: { value: 'roads', onsearch: vi.fn() },
89
+ });
90
+ await expectNoA11yViolations(container);
91
+ });
92
+ });
@@ -0,0 +1,338 @@
1
+ /**
2
+ * SignaturePad rendered states, pointer gating, PNG output, and native form
3
+ * posting (smrt#3290).
4
+ */
5
+ import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
6
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import { expectNoA11yViolations } from '../../../test-support/a11y.js';
8
+ import SignaturePad from '../SignaturePad.svelte';
9
+ import { SIGNATURE_INK_COLOR, SIGNATURE_PAPER_COLOR, } from '../signature-pad-logic.js';
10
+ import CaptureFormFixture from './capture-form.fixture.svelte';
11
+ import { stubCanvas, submittedEntries, useFileFieldStrategy, } from './capture-test-env.js';
12
+ let context;
13
+ beforeEach(() => {
14
+ context = stubCanvas();
15
+ useFileFieldStrategy('data-transfer');
16
+ });
17
+ afterEach(() => {
18
+ vi.restoreAllMocks();
19
+ vi.unstubAllGlobals();
20
+ });
21
+ function canvas(container) {
22
+ return container.querySelector('canvas');
23
+ }
24
+ function root(container) {
25
+ return container.querySelector('.signature-pad');
26
+ }
27
+ async function stroke(target, pointerType = 'mouse', pointerId = 1) {
28
+ await fireEvent.pointerDown(target, {
29
+ pointerType,
30
+ pointerId,
31
+ clientX: 10,
32
+ clientY: 10,
33
+ });
34
+ await fireEvent.pointerMove(target, {
35
+ pointerType,
36
+ pointerId,
37
+ clientX: 60,
38
+ clientY: 40,
39
+ });
40
+ await fireEvent.pointerUp(target, { pointerType, pointerId });
41
+ }
42
+ const useButton = () => screen.getByRole('button', { name: 'Use signature' });
43
+ describe('SignaturePad states', () => {
44
+ it('starts empty on white paper with Use signature disabled', async () => {
45
+ const { container } = render(SignaturePad);
46
+ expect(root(container)).toHaveAttribute('data-state', 'empty');
47
+ expect(useButton()).toBeDisabled();
48
+ expect(screen.getByRole('button', { name: 'Clear' })).toBeEnabled();
49
+ expect(canvas(container)).toHaveAttribute('aria-label', 'Signature area, not yet signed');
50
+ expect(screen.getByText('Sign with a stylus, your finger, or the mouse.')).toBeInTheDocument();
51
+ // Paper is painted into the bitmap and shown behind it regardless of theme.
52
+ expect(context.fillStyle).toBe(SIGNATURE_PAPER_COLOR);
53
+ expect(context.fillRect).toHaveBeenCalledWith(0, 0, 600, 240);
54
+ expect(canvas(container).style.backgroundColor).toBe('rgb(255, 255, 255)');
55
+ await expectNoA11yViolations(container);
56
+ });
57
+ it('enables Use signature once an accepted stroke exists, inked dark', async () => {
58
+ const { container } = render(SignaturePad);
59
+ await stroke(canvas(container));
60
+ expect(root(container)).toHaveAttribute('data-state', 'signed');
61
+ expect(canvas(container)).toHaveAttribute('aria-label', 'Signature area, signed');
62
+ expect(useButton()).toBeEnabled();
63
+ expect(context.stroke).toHaveBeenCalled();
64
+ expect(context.strokeStyle).toBe(SIGNATURE_INK_COLOR);
65
+ await expectNoA11yViolations(container);
66
+ });
67
+ it('ignores finger and mouse under stylusOnly, and accepts the pen', async () => {
68
+ const { container } = render(SignaturePad, { props: { stylusOnly: true } });
69
+ expect(screen.getByText('Sign with the stylus.')).toBeInTheDocument();
70
+ await stroke(canvas(container), 'touch');
71
+ await stroke(canvas(container), 'mouse', 2);
72
+ expect(root(container)).toHaveAttribute('data-state', 'empty');
73
+ expect(useButton()).toBeDisabled();
74
+ expect(context.stroke).not.toHaveBeenCalled();
75
+ await stroke(canvas(container), 'pen', 3);
76
+ expect(useButton()).toBeEnabled();
77
+ });
78
+ it('ignores a second pointer that did not start the stroke', async () => {
79
+ const { container } = render(SignaturePad);
80
+ await fireEvent.pointerMove(canvas(container), {
81
+ pointerType: 'mouse',
82
+ pointerId: 9,
83
+ });
84
+ expect(root(container)).toHaveAttribute('data-state', 'empty');
85
+ });
86
+ it('is inert when disabled', async () => {
87
+ const { container } = render(SignaturePad, { props: { disabled: true } });
88
+ await stroke(canvas(container));
89
+ expect(root(container)).toHaveAttribute('data-state', 'empty');
90
+ expect(useButton()).toBeDisabled();
91
+ expect(screen.getByRole('button', { name: 'Clear' })).toBeDisabled();
92
+ });
93
+ it('returns a PNG through onCapture and locks the pad until cleared', async () => {
94
+ const onCapture = vi.fn();
95
+ const onClear = vi.fn();
96
+ const { container } = render(SignaturePad, {
97
+ props: { onCapture, onClear },
98
+ });
99
+ await stroke(canvas(container));
100
+ await fireEvent.click(useButton());
101
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
102
+ const [{ blob, dataUrl }] = onCapture.mock.calls[0];
103
+ expect(blob.type).toBe('image/png');
104
+ expect(dataUrl).toMatch(/^data:image\/png/);
105
+ expect(screen.getByRole('status')).toHaveTextContent('Signature attached.');
106
+ expect(useButton()).toBeDisabled();
107
+ context.stroke.mockClear();
108
+ await stroke(canvas(container));
109
+ expect(context.stroke).not.toHaveBeenCalled();
110
+ await fireEvent.click(screen.getByRole('button', { name: 'Clear' }));
111
+ expect(onClear).toHaveBeenCalledTimes(1);
112
+ expect(root(container)).toHaveAttribute('data-state', 'empty');
113
+ });
114
+ it('locks the pad while the PNG encodes so it commits once and matches the canvas', async () => {
115
+ let finish;
116
+ vi.mocked(HTMLCanvasElement.prototype.toBlob).mockImplementation((callback, type) => {
117
+ finish = () => callback(new Blob(['pixels'], { type }));
118
+ });
119
+ const onCapture = vi.fn();
120
+ const { container } = render(SignaturePad, { props: { onCapture } });
121
+ await stroke(canvas(container));
122
+ await fireEvent.click(useButton());
123
+ expect(useButton()).toBeDisabled();
124
+ await fireEvent.click(useButton());
125
+ context.stroke.mockClear();
126
+ await stroke(canvas(container), 'mouse', 2);
127
+ expect(context.stroke).not.toHaveBeenCalled();
128
+ finish();
129
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
130
+ expect(onCapture).toHaveBeenCalledTimes(1);
131
+ });
132
+ it('discards an encoding superseded by Clear', async () => {
133
+ let finish;
134
+ vi.mocked(HTMLCanvasElement.prototype.toBlob).mockImplementation((callback, type) => {
135
+ finish = () => callback(new Blob(['pixels'], { type }));
136
+ });
137
+ const onCapture = vi.fn();
138
+ const { container } = render(SignaturePad, { props: { onCapture } });
139
+ await stroke(canvas(container));
140
+ await fireEvent.click(useButton());
141
+ await fireEvent.click(screen.getByRole('button', { name: 'Clear' }));
142
+ finish();
143
+ await Promise.resolve();
144
+ expect(onCapture).not.toHaveBeenCalled();
145
+ expect(root(container)).toHaveAttribute('data-state', 'empty');
146
+ });
147
+ it('keeps a committed signature across prop updates and resets only on a size change', async () => {
148
+ const onClear = vi.fn();
149
+ const props = { name: 'signature', onClear };
150
+ const { container, rerender } = render(SignaturePad, { props });
151
+ await stroke(canvas(container));
152
+ await fireEvent.click(useButton());
153
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
154
+ // The harness replaces the whole props object, as a parent re-spreading a
155
+ // fresh object does; width and height are unchanged.
156
+ await rerender({ ...props, disabled: true });
157
+ await rerender({ ...props, disabled: false });
158
+ expect(root(container)).toHaveAttribute('data-state', 'committed');
159
+ expect(onClear).not.toHaveBeenCalled();
160
+ await rerender({ ...props, width: 300 });
161
+ expect(root(container)).toHaveAttribute('data-state', 'empty');
162
+ expect(onClear).toHaveBeenCalledTimes(1);
163
+ });
164
+ it('accepts label overrides', () => {
165
+ render(SignaturePad, {
166
+ props: { labels: { useSignature: 'Signer', clear: 'Effacer' } },
167
+ });
168
+ expect(screen.getByRole('button', { name: 'Signer' })).toBeInTheDocument();
169
+ expect(screen.getByRole('button', { name: 'Effacer' })).toBeInTheDocument();
170
+ });
171
+ });
172
+ describe('SignaturePad native form post', () => {
173
+ it('posts the committed signature as signature.png and removes it on Clear', async () => {
174
+ const { container } = render(CaptureFormFixture, {
175
+ props: { kind: 'signature', name: 'signature' },
176
+ });
177
+ const form = container.querySelector('form');
178
+ expect(root(container)).toHaveAttribute('data-smrt-file-field', 'data-transfer');
179
+ expect(submittedEntries(form).get('signature').size).toBe(0);
180
+ await stroke(canvas(container));
181
+ await fireEvent.click(useButton());
182
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
183
+ const posted = submittedEntries(form).getAll('signature');
184
+ expect(posted).toHaveLength(1);
185
+ expect(posted[0].name).toBe('signature.png');
186
+ expect(posted[0].type).toBe('image/png');
187
+ expect(submittedEntries(form).get('note')).toBe('hazard');
188
+ await fireEvent.click(screen.getByRole('button', { name: 'Clear' }));
189
+ expect(submittedEntries(form).get('signature').size).toBe(0);
190
+ });
191
+ it('falls back to the formdata event when DataTransfer is unavailable', async () => {
192
+ useFileFieldStrategy('formdata-event');
193
+ const { container } = render(CaptureFormFixture, {
194
+ props: { kind: 'signature', name: 'sig', fileName: 'flha-signature.png' },
195
+ });
196
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-smrt-file-field', 'formdata-event'));
197
+ await stroke(canvas(container));
198
+ await fireEvent.click(useButton());
199
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
200
+ const posted = submittedEntries(container.querySelector('form')).getAll('sig');
201
+ expect(posted).toHaveLength(1);
202
+ expect(posted[0].name).toBe('flha-signature.png');
203
+ });
204
+ });
205
+ describe('SignaturePad in a disabled fieldset', () => {
206
+ it.each([
207
+ 'data-transfer',
208
+ 'formdata-event',
209
+ ])('posts nothing, committed or empty, like a disabled native control (%s)', async (strategy) => {
210
+ useFileFieldStrategy(strategy);
211
+ const { container } = render(CaptureFormFixture, {
212
+ props: { kind: 'signature', name: 'signature', inFieldset: true },
213
+ });
214
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-smrt-file-field', strategy));
215
+ const form = container.querySelector('form');
216
+ const fieldset = form.querySelector('fieldset');
217
+ fieldset.disabled = true;
218
+ expect(submittedEntries(form).has('signature')).toBe(false);
219
+ fieldset.disabled = false;
220
+ await stroke(canvas(container));
221
+ await fireEvent.click(useButton());
222
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
223
+ expect(submittedEntries(form).getAll('signature')).toHaveLength(1);
224
+ fieldset.disabled = true;
225
+ expect(submittedEntries(form).has('signature')).toBe(false);
226
+ expect(submittedEntries(form).get('note')).toBe('hazard');
227
+ fieldset.disabled = false;
228
+ expect(submittedEntries(form).getAll('signature')).toHaveLength(1);
229
+ });
230
+ });
231
+ /**
232
+ * A reset of the owning form (smrt#3290): SvelteKit `enhance`'s `update()`
233
+ * after a success, `createFormRetry()`'s conditional reset, a reset button and
234
+ * `form.reset()` all empty a native file input, so the pad must empty with it.
235
+ * jsdom's reset does not empty file inputs (browsers do), so these assertions
236
+ * prove the component empties its own field.
237
+ */
238
+ describe('SignaturePad form reset', () => {
239
+ function form(container) {
240
+ return container.querySelector('form');
241
+ }
242
+ async function commit(container) {
243
+ await stroke(canvas(container));
244
+ await fireEvent.click(useButton());
245
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'committed'));
246
+ }
247
+ function postedSignatures(container) {
248
+ return submittedEntries(form(container)).getAll('signature');
249
+ }
250
+ it.each([
251
+ 'data-transfer',
252
+ 'formdata-event',
253
+ ])('discards a committed signature like Clear (%s)', async (strategy) => {
254
+ useFileFieldStrategy(strategy);
255
+ const onClear = vi.fn();
256
+ const { container } = render(CaptureFormFixture, {
257
+ props: { kind: 'signature', name: 'signature', onClear },
258
+ });
259
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-smrt-file-field', strategy));
260
+ await commit(container);
261
+ expect(postedSignatures(container)[0].name).toBe('signature.png');
262
+ context.fillRect.mockClear();
263
+ form(container).reset();
264
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'empty'));
265
+ expect(onClear).toHaveBeenCalledTimes(1);
266
+ expect(context.fillRect).toHaveBeenCalled();
267
+ expect(context.fillStyle).toBe(SIGNATURE_PAPER_COLOR);
268
+ expect(screen.getByRole('status')).toHaveTextContent('');
269
+ const posted = postedSignatures(container);
270
+ expect(posted).toHaveLength(1);
271
+ expect(posted[0].size).toBe(0);
272
+ expect(posted[0].name).toBe('');
273
+ // Unlocked: the next entry can be signed and committed.
274
+ await commit(container);
275
+ expect(postedSignatures(container)[0].name).toBe('signature.png');
276
+ });
277
+ it('a reset button discards ink not yet committed, without onClear', async () => {
278
+ const onClear = vi.fn();
279
+ const { container } = render(CaptureFormFixture, {
280
+ props: { kind: 'signature', name: 'signature', onClear },
281
+ });
282
+ await stroke(canvas(container));
283
+ expect(useButton()).toBeEnabled();
284
+ await fireEvent.click(screen.getByRole('button', { name: 'Reset form' }));
285
+ expect(root(container)).toHaveAttribute('data-state', 'empty');
286
+ expect(useButton()).toBeDisabled();
287
+ expect(onClear).not.toHaveBeenCalled();
288
+ });
289
+ it('clears a committed signature even while disabled', async () => {
290
+ const onClear = vi.fn();
291
+ const { container, rerender } = render(CaptureFormFixture, {
292
+ props: { kind: 'signature', name: 'signature', onClear },
293
+ });
294
+ await commit(container);
295
+ await rerender({
296
+ kind: 'signature',
297
+ name: 'signature',
298
+ onClear,
299
+ disabled: true,
300
+ });
301
+ form(container).reset();
302
+ await waitFor(() => expect(root(container)).toHaveAttribute('data-state', 'empty'));
303
+ expect(onClear).toHaveBeenCalledTimes(1);
304
+ expect(postedSignatures(container)[0].size).toBe(0);
305
+ });
306
+ it('supersedes a signature still encoding', async () => {
307
+ let finish;
308
+ vi.mocked(HTMLCanvasElement.prototype.toBlob).mockImplementation((callback, type) => {
309
+ finish = () => callback(new Blob(['pixels'], { type }));
310
+ });
311
+ const onCapture = vi.fn();
312
+ const { container } = render(CaptureFormFixture, {
313
+ props: { kind: 'signature', name: 'signature', onCapture },
314
+ });
315
+ await stroke(canvas(container));
316
+ await fireEvent.click(useButton());
317
+ form(container).reset();
318
+ finish();
319
+ await Promise.resolve();
320
+ expect(onCapture).not.toHaveBeenCalled();
321
+ expect(root(container)).toHaveAttribute('data-state', 'empty');
322
+ expect(postedSignatures(container)[0].size).toBe(0);
323
+ });
324
+ it('ignores the reset of another form', async () => {
325
+ const onClear = vi.fn();
326
+ const { container } = render(CaptureFormFixture, {
327
+ props: { kind: 'signature', name: 'signature', onClear },
328
+ });
329
+ await commit(container);
330
+ const other = document.createElement('form');
331
+ document.body.append(other);
332
+ other.reset();
333
+ expect(root(container)).toHaveAttribute('data-state', 'committed');
334
+ expect(onClear).not.toHaveBeenCalled();
335
+ expect(postedSignatures(container)[0].name).toBe('signature.png');
336
+ other.remove();
337
+ });
338
+ });
@@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event';
3
3
  import { describe, expect, it, vi } from 'vitest';
4
4
  import { expectNoA11yViolations } from '../../../test-support/a11y';
5
5
  import { createControlInteractionRegistry, executeLocalControlBatch, executeLocalControlCommand, } from '../control-interaction.js';
6
+ import StagedControlReview from '../StagedControlReview.svelte';
6
7
  import CompositeUserEditFixture from './composite-user-edit.fixture.svelte';
7
8
  import SelectFixture from './select-interaction.fixture.svelte';
8
9
  import Fixture from './staged-review.fixture.svelte';
@@ -77,7 +78,8 @@ describe('StagedControlReview', () => {
77
78
  await waitFor(() => expect(field).toHaveAttribute('data-smrt-staged', 'true'));
78
79
  expect(screen.getByRole('region', { name: 'Review proposed changes' })).toBeInTheDocument();
79
80
  expect(screen.getByText(/Proposed by assistant/)).toBeInTheDocument();
80
- expect(screen.getByText('profile/display-name')).toBeInTheDocument();
81
+ // Plain field labels only: registry addressing never shows (#forms-qa).
82
+ expect(screen.queryByText(/profile\/display-name/)).not.toBeInTheDocument();
81
83
  const proposal = screen.getByRole('textbox', {
82
84
  name: 'Edit proposed value for Display name',
83
85
  });
@@ -93,6 +95,38 @@ describe('StagedControlReview', () => {
93
95
  expect(field).toHaveFocus();
94
96
  await expectNoA11yViolations(container);
95
97
  });
98
+ it('shows plain field labels, never registry ids, for unlabeled controls', async () => {
99
+ const registry = createReviewRegistry();
100
+ let town = '';
101
+ registry.register({
102
+ identity: {
103
+ formId: 'setup-network',
104
+ controlId: 'setup-network/town_name',
105
+ subject: { type: 'site', id: 'site-1', label: 'Lacombe News' },
106
+ },
107
+ metadata: { kind: 'text' },
108
+ getValue: () => town,
109
+ setValue: (next) => {
110
+ town = String(next);
111
+ },
112
+ });
113
+ render(StagedControlReview, {
114
+ props: { registry, formId: 'setup-network' },
115
+ });
116
+ await registry.execute({
117
+ action: 'stage',
118
+ identity: {
119
+ formId: 'setup-network',
120
+ controlId: 'setup-network/town_name',
121
+ subject: { type: 'site', id: 'site-1' },
122
+ },
123
+ value: 'Lacombe',
124
+ }, { source: 'agent' });
125
+ await waitFor(() => expect(screen.getByText('Town name')).toBeVisible());
126
+ expect(screen.getByText('Lacombe News')).toBeVisible();
127
+ expect(screen.queryByText(/setup-network/)).not.toBeInTheDocument();
128
+ expect(screen.queryByText(/site-1/)).not.toBeInTheDocument();
129
+ });
96
130
  it('keeps Enter in the proposal editor from submitting the form', async () => {
97
131
  const registry = createReviewRegistry();
98
132
  render(Fixture, { props: { registry } });
@@ -594,8 +628,8 @@ describe('StagedControlReview', () => {
594
628
  value: 'Grace',
595
629
  }, { source: 'agent' });
596
630
  await waitFor(() => expect(renderedFirst).toHaveAttribute('data-smrt-staged', 'true'));
597
- expect(screen.getByText('profile/display-name · record:one')).toBeVisible();
598
- expect(screen.getByText('profile/display-name · record:two')).toBeVisible();
631
+ expect(screen.queryByText(/profile\/display-name/)).not.toBeInTheDocument();
632
+ expect(screen.queryByText(/record:one/)).not.toBeInTheDocument();
599
633
  for (const subjectId of ['one', 'two']) {
600
634
  const actionName = `Display name record:${subjectId} [identity:["profile","display-name","record","${subjectId}"]]`;
601
635
  expect(screen.getByRole('textbox', {