@morscherlab/mint-sdk 1.0.0-beta.2 → 1.0.0-beta.3

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 (399) hide show
  1. package/README.md +218 -6
  2. package/dist/__tests__/components/ActionItem.test.d.ts +1 -0
  3. package/dist/__tests__/components/AppAvatarMenu.test.d.ts +1 -0
  4. package/dist/__tests__/components/AppPageSelector.test.d.ts +1 -0
  5. package/dist/__tests__/components/AppPillNav.test.d.ts +1 -0
  6. package/dist/__tests__/components/AppPluginSwitcher.test.d.ts +1 -0
  7. package/dist/__tests__/components/AppToastContainer.test.d.ts +1 -0
  8. package/dist/__tests__/components/BaseRadioGroup.test.d.ts +1 -0
  9. package/dist/__tests__/components/BaseSelect.test.d.ts +1 -0
  10. package/dist/__tests__/components/BaseTabs.test.d.ts +1 -0
  11. package/dist/__tests__/components/BatchProgressList.test.d.ts +1 -0
  12. package/dist/__tests__/components/BioTemplateExperimentWorkspaceView.test.d.ts +1 -0
  13. package/dist/__tests__/components/BioTemplatePackWorkspaceView.test.d.ts +1 -0
  14. package/dist/__tests__/components/BioTemplatePresetWorkspaceView.test.d.ts +1 -0
  15. package/dist/__tests__/components/BioTemplateRenderer.test.d.ts +1 -0
  16. package/dist/__tests__/components/Breadcrumb.test.d.ts +1 -0
  17. package/dist/__tests__/components/CalendarGridPanel.test.d.ts +1 -0
  18. package/dist/__tests__/components/ConcentrationInput.test.d.ts +1 -0
  19. package/dist/__tests__/components/ControlWorkspaceView.test.d.ts +1 -0
  20. package/dist/__tests__/components/DatePicker.test.d.ts +1 -0
  21. package/dist/__tests__/components/DateTimePicker.test.d.ts +1 -0
  22. package/dist/__tests__/components/EmptyState.test.d.ts +1 -0
  23. package/dist/__tests__/components/ExperimentPopover.test.d.ts +1 -0
  24. package/dist/__tests__/components/FormBuilder.test.d.ts +1 -0
  25. package/dist/__tests__/components/FormCompatibility.test.d.ts +1 -0
  26. package/dist/__tests__/components/GroupAssigner.test.d.ts +1 -0
  27. package/dist/__tests__/components/GroupingModal.test.d.ts +1 -0
  28. package/dist/__tests__/components/MultiSelect.test.d.ts +1 -0
  29. package/dist/__tests__/components/ProtocolStepEditor.test.d.ts +1 -0
  30. package/dist/__tests__/components/ReagentList.test.d.ts +1 -0
  31. package/dist/__tests__/components/SampleHierarchyTree.test.d.ts +1 -0
  32. package/dist/__tests__/components/SampleSelector.test.d.ts +1 -0
  33. package/dist/__tests__/components/SegmentedControl.test.d.ts +1 -0
  34. package/dist/__tests__/components/SettingsButton.test.d.ts +1 -0
  35. package/dist/__tests__/components/SettingsModal.test.d.ts +1 -0
  36. package/dist/__tests__/components/TagsInput.test.d.ts +1 -0
  37. package/dist/__tests__/components/ThemeToggle.test.d.ts +1 -0
  38. package/dist/__tests__/components/TimePicker.test.d.ts +1 -0
  39. package/dist/__tests__/composables/useBioTemplatePackWorkspace.test.d.ts +1 -0
  40. package/dist/__tests__/composables/useBioTemplatePresetWorkspace.test.d.ts +1 -0
  41. package/dist/__tests__/composables/useBioTemplateWorkspace.test.d.ts +1 -0
  42. package/dist/__tests__/composables/useCalendarGrid.test.d.ts +1 -0
  43. package/dist/__tests__/composables/useControlSchema.test.d.ts +1 -0
  44. package/dist/__tests__/composables/useDebouncedWatch.test.d.ts +1 -0
  45. package/dist/__tests__/composables/useDropdownState.test.d.ts +1 -0
  46. package/dist/__tests__/composables/useEventListener.test.d.ts +1 -0
  47. package/dist/__tests__/composables/useExpansionSet.test.d.ts +1 -0
  48. package/dist/__tests__/composables/useExperimentData.test.d.ts +1 -0
  49. package/dist/__tests__/composables/useExperimentSelector.test.d.ts +1 -0
  50. package/dist/__tests__/composables/useGroupAssignment.test.d.ts +1 -0
  51. package/dist/__tests__/composables/useListSelection.test.d.ts +1 -0
  52. package/dist/__tests__/composables/usePluginClient.test.d.ts +1 -0
  53. package/dist/__tests__/composables/usePluginConfig.test.d.ts +1 -0
  54. package/dist/__tests__/composables/useRequestSyncState.test.d.ts +1 -0
  55. package/dist/__tests__/composables/useSampleGroups.test.d.ts +1 -0
  56. package/dist/__tests__/composables/useSelectionLimit.test.d.ts +1 -0
  57. package/dist/__tests__/composables/useSortedItems.test.d.ts +1 -0
  58. package/dist/__tests__/composables/useTemplateCollection.test.d.ts +1 -0
  59. package/dist/__tests__/composables/useTextSearch.test.d.ts +1 -0
  60. package/dist/__tests__/composables/useTheme.test.d.ts +1 -0
  61. package/dist/__tests__/composables/useTimeUtils.test.d.ts +1 -0
  62. package/dist/__tests__/docs/frontendDocsCatalog.test.d.ts +1 -0
  63. package/dist/__tests__/templates/templates.test.d.ts +1 -0
  64. package/dist/{auth-DsI0rQ7_.js → auth-QQj2kkze.js} +12 -5
  65. package/dist/auth-QQj2kkze.js.map +1 -0
  66. package/dist/components/ActionItem.vue.d.ts +32 -0
  67. package/dist/components/AppAvatarMenu.vue.d.ts +2 -7
  68. package/dist/components/AppPageSelector.vue.d.ts +3 -6
  69. package/dist/components/AppPillNav.vue.d.ts +2 -2
  70. package/dist/components/AppSidebar.vue.d.ts +56 -3
  71. package/dist/components/AppToastContainer.vue.d.ts +2 -0
  72. package/dist/components/AppTopBar.vue.d.ts +41 -10
  73. package/dist/components/BaseButton.vue.d.ts +1 -1
  74. package/dist/components/BaseCheckbox.vue.d.ts +1 -1
  75. package/dist/components/BaseInput.vue.d.ts +2 -2
  76. package/dist/components/BasePill.vue.d.ts +2 -2
  77. package/dist/components/BaseRadioGroup.vue.d.ts +3 -3
  78. package/dist/components/BaseSelect.vue.d.ts +3 -3
  79. package/dist/components/BaseTabs.vue.d.ts +2 -2
  80. package/dist/components/BaseTextarea.vue.d.ts +1 -1
  81. package/dist/components/BaseToggle.vue.d.ts +1 -1
  82. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +117 -0
  83. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +92 -0
  84. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +82 -0
  85. package/dist/components/BioTemplateRenderer.vue.d.ts +29 -0
  86. package/dist/components/Breadcrumb.vue.d.ts +2 -2
  87. package/dist/components/Calendar.vue.d.ts +1 -1
  88. package/dist/components/CalendarGridPanel.vue.d.ts +25 -0
  89. package/dist/components/CollapsibleCard.vue.d.ts +1 -1
  90. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  91. package/dist/components/ConfirmDialog.vue.d.ts +2 -2
  92. package/dist/components/ControlWorkspaceView.vue.d.ts +130 -0
  93. package/dist/components/DatePicker.vue.d.ts +1 -1
  94. package/dist/components/DateTimePicker.vue.d.ts +3 -3
  95. package/dist/components/Divider.vue.d.ts +1 -1
  96. package/dist/components/DropdownButton.vue.d.ts +3 -3
  97. package/dist/components/EmptyState.vue.d.ts +1 -2
  98. package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
  99. package/dist/components/ExperimentTimeline.vue.d.ts +2 -2
  100. package/dist/components/FileUploader.vue.d.ts +1 -1
  101. package/dist/components/FitPanel.vue.d.ts +1 -1
  102. package/dist/components/FormActions.vue.d.ts +4 -4
  103. package/dist/components/FormBuilder.vue.d.ts +22 -8
  104. package/dist/components/FormFieldRenderer.vue.d.ts +7 -10
  105. package/dist/components/FormSection.vue.d.ts +11 -24
  106. package/dist/components/FormulaInput.vue.d.ts +2 -2
  107. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  108. package/dist/components/MultiSelect.vue.d.ts +3 -3
  109. package/dist/components/NumberInput.vue.d.ts +1 -1
  110. package/dist/components/ProgressBar.vue.d.ts +1 -1
  111. package/dist/components/ProtocolStepEditor.vue.d.ts +3 -1
  112. package/dist/components/RackEditor.vue.d.ts +2 -2
  113. package/dist/components/SampleLegend.vue.d.ts +2 -2
  114. package/dist/components/ScheduleCalendar.vue.d.ts +2 -2
  115. package/dist/components/SegmentedControl.vue.d.ts +2 -2
  116. package/dist/components/SequenceInput.vue.d.ts +3 -3
  117. package/dist/components/SettingsButton.vue.d.ts +2 -2
  118. package/dist/components/SettingsModal.vue.d.ts +13 -5
  119. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  120. package/dist/components/TagsInput.vue.d.ts +3 -2
  121. package/dist/components/TimePicker.vue.d.ts +3 -3
  122. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  123. package/dist/components/UnitInput.vue.d.ts +2 -2
  124. package/dist/components/WellPlate.vue.d.ts +8 -8
  125. package/dist/components/index.d.ts +11 -1
  126. package/dist/components/index.js +3 -3
  127. package/dist/components/internal/FormFieldRendererInternal.vue.d.ts +31 -0
  128. package/dist/components/internal/FormSectionRenderer.vue.d.ts +43 -0
  129. package/dist/{components-_XqPEhP9.js → components-D_Sr0adg.js} +7290 -6518
  130. package/dist/components-D_Sr0adg.js.map +1 -0
  131. package/dist/composables/index.d.ts +21 -2
  132. package/dist/composables/index.js +4 -3
  133. package/dist/composables/platformContextHelpers.d.ts +14 -0
  134. package/dist/composables/useBioTemplateComponents.d.ts +20 -0
  135. package/dist/composables/useBioTemplateControls.d.ts +6 -0
  136. package/dist/composables/useBioTemplatePackWorkspace.d.ts +45 -0
  137. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +74 -0
  138. package/dist/composables/useBioTemplateWorkspace.d.ts +50 -0
  139. package/dist/composables/useCalendarGrid.d.ts +26 -0
  140. package/dist/composables/useControlSchema.d.ts +321 -0
  141. package/dist/composables/useDebouncedWatch.d.ts +20 -0
  142. package/dist/composables/useDropdownState.d.ts +19 -0
  143. package/dist/composables/useEventListener.d.ts +13 -0
  144. package/dist/composables/useExpansionSet.d.ts +21 -0
  145. package/dist/composables/useExperimentData.d.ts +10 -0
  146. package/dist/composables/useExperimentSave.d.ts +31 -2
  147. package/dist/composables/useExperimentSelector.d.ts +20 -0
  148. package/dist/composables/useForm.d.ts +2 -0
  149. package/dist/composables/useGroupAssignment.d.ts +31 -0
  150. package/dist/composables/useListSelection.d.ts +35 -0
  151. package/dist/composables/usePlatformContext.d.ts +21 -3
  152. package/dist/composables/usePluginApi.d.ts +7 -14
  153. package/dist/composables/usePluginClient.d.ts +109 -0
  154. package/dist/composables/usePluginConfig.d.ts +12 -0
  155. package/dist/composables/useRequestSyncState.d.ts +34 -0
  156. package/dist/composables/useSampleGroups.d.ts +32 -0
  157. package/dist/composables/useSelectionLimit.d.ts +17 -0
  158. package/dist/composables/useSortedItems.d.ts +32 -0
  159. package/dist/composables/useTemplateCollection.d.ts +58 -0
  160. package/dist/composables/useTextSearch.d.ts +18 -0
  161. package/dist/composables/useTimeUtils.d.ts +8 -0
  162. package/dist/{composables-tiZqLu1M.js → composables-C3dpXQN5.js} +228 -146
  163. package/dist/composables-C3dpXQN5.js.map +1 -0
  164. package/dist/index.d.ts +12 -3
  165. package/dist/index.js +6 -5
  166. package/dist/install.d.ts +7 -2
  167. package/dist/install.js +2 -2
  168. package/dist/install.js.map +1 -1
  169. package/dist/stores/index.js +1 -1
  170. package/dist/stores/settings.d.ts +4 -1
  171. package/dist/styles.css +5235 -5977
  172. package/dist/templates/adapters.d.ts +43 -0
  173. package/dist/templates/builders.d.ts +63 -0
  174. package/dist/templates/catalog.d.ts +188 -0
  175. package/dist/templates/componentBindings.d.ts +58 -0
  176. package/dist/templates/controlSchemas.d.ts +25 -0
  177. package/dist/templates/index.d.ts +15 -0
  178. package/dist/templates/index.js +2 -0
  179. package/dist/templates/lookup.d.ts +4 -0
  180. package/dist/templates/packs.d.ts +18 -0
  181. package/dist/templates/presets.d.ts +90 -0
  182. package/dist/templates/types.d.ts +531 -0
  183. package/dist/templates-50NPjaxL.js +9333 -0
  184. package/dist/templates-50NPjaxL.js.map +1 -0
  185. package/dist/types/components.d.ts +26 -4
  186. package/dist/types/form-builder.d.ts +6 -8
  187. package/dist/types/index.d.ts +2 -2
  188. package/dist/types/platform.d.ts +7 -1
  189. package/dist/useScheduleDrag-D4oWdh41.js +4371 -0
  190. package/dist/useScheduleDrag-D4oWdh41.js.map +1 -0
  191. package/dist/utils/formModelSync.d.ts +5 -0
  192. package/dist/utils/items.d.ts +8 -0
  193. package/dist/utils/options.d.ts +6 -0
  194. package/dist/utils/pluginIcon.d.ts +9 -0
  195. package/package.json +7 -2
  196. package/src/__tests__/components/ActionItem.test.ts +99 -0
  197. package/src/__tests__/components/AppAvatarMenu.test.ts +27 -0
  198. package/src/__tests__/components/AppPageSelector.test.ts +134 -0
  199. package/src/__tests__/components/AppPillNav.test.ts +78 -0
  200. package/src/__tests__/components/AppPluginSwitcher.test.ts +44 -0
  201. package/src/__tests__/components/AppSidebar.test.ts +370 -0
  202. package/src/__tests__/components/AppToastContainer.test.ts +48 -0
  203. package/src/__tests__/components/AppTopBar.test.ts +383 -0
  204. package/src/__tests__/components/BaseRadioGroup.test.ts +25 -0
  205. package/src/__tests__/components/BaseSelect.test.ts +21 -0
  206. package/src/__tests__/components/BaseTabs.test.ts +25 -0
  207. package/src/__tests__/components/BatchProgressList.test.ts +52 -0
  208. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +153 -0
  209. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +161 -0
  210. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +281 -0
  211. package/src/__tests__/components/BioTemplateRenderer.test.ts +71 -0
  212. package/src/__tests__/components/Breadcrumb.test.ts +23 -0
  213. package/src/__tests__/components/CalendarGridPanel.test.ts +36 -0
  214. package/src/__tests__/components/ConcentrationInput.test.ts +45 -0
  215. package/src/__tests__/components/ControlWorkspaceView.test.ts +1031 -0
  216. package/src/__tests__/components/DataFrame.test.ts +11 -0
  217. package/src/__tests__/components/DatePicker.test.ts +45 -0
  218. package/src/__tests__/components/DateTimePicker.test.ts +48 -0
  219. package/src/__tests__/components/DropdownButton.test.ts +23 -0
  220. package/src/__tests__/components/EmptyState.test.ts +23 -0
  221. package/src/__tests__/components/ExperimentPopover.test.ts +56 -0
  222. package/src/__tests__/components/FormBuilder.test.ts +296 -0
  223. package/src/__tests__/components/FormCompatibility.test.ts +94 -0
  224. package/src/__tests__/components/GroupAssigner.test.ts +30 -0
  225. package/src/__tests__/components/GroupingModal.test.ts +73 -0
  226. package/src/__tests__/components/MultiSelect.test.ts +48 -0
  227. package/src/__tests__/components/ProtocolStepEditor.test.ts +33 -0
  228. package/src/__tests__/components/ReagentList.test.ts +82 -0
  229. package/src/__tests__/components/SampleHierarchyTree.test.ts +53 -0
  230. package/src/__tests__/components/SampleSelector.test.ts +60 -0
  231. package/src/__tests__/components/SegmentedControl.test.ts +24 -0
  232. package/src/__tests__/components/SettingsButton.test.ts +44 -0
  233. package/src/__tests__/components/SettingsModal.test.ts +296 -0
  234. package/src/__tests__/components/TagsInput.test.ts +75 -0
  235. package/src/__tests__/components/ThemeToggle.test.ts +47 -0
  236. package/src/__tests__/components/TimePicker.test.ts +38 -0
  237. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +122 -0
  238. package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +199 -0
  239. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +99 -0
  240. package/src/__tests__/composables/useCalendarGrid.test.ts +38 -0
  241. package/src/__tests__/composables/useControlSchema.test.ts +919 -0
  242. package/src/__tests__/composables/useDebouncedWatch.test.ts +93 -0
  243. package/src/__tests__/composables/useDropdownState.test.ts +95 -0
  244. package/src/__tests__/composables/useEventListener.test.ts +116 -0
  245. package/src/__tests__/composables/useExpansionSet.test.ts +62 -0
  246. package/src/__tests__/composables/useExperimentData.test.ts +4 -0
  247. package/src/__tests__/composables/useExperimentSave.test.ts +203 -8
  248. package/src/__tests__/composables/useExperimentSelector.test.ts +164 -0
  249. package/src/__tests__/composables/useForm.test.ts +58 -0
  250. package/src/__tests__/composables/useFormBuilder.test.ts +77 -0
  251. package/src/__tests__/composables/useGroupAssignment.test.ts +73 -0
  252. package/src/__tests__/composables/useListSelection.test.ts +66 -0
  253. package/src/__tests__/composables/usePluginClient.test.ts +444 -0
  254. package/src/__tests__/composables/usePluginConfig.test.ts +5 -0
  255. package/src/__tests__/composables/useRequestSyncState.test.ts +92 -0
  256. package/src/__tests__/composables/useSampleGroups.test.ts +66 -0
  257. package/src/__tests__/composables/useSelectionLimit.test.ts +41 -0
  258. package/src/__tests__/composables/useSortedItems.test.ts +87 -0
  259. package/src/__tests__/composables/useTemplateCollection.test.ts +147 -0
  260. package/src/__tests__/composables/useTextSearch.test.ts +55 -0
  261. package/src/__tests__/composables/useTheme.test.ts +91 -0
  262. package/src/__tests__/composables/useTimeUtils.test.ts +35 -0
  263. package/src/__tests__/docs/frontendDocsCatalog.test.ts +229 -0
  264. package/src/__tests__/fixtures/templates/dose-response.json +81 -0
  265. package/src/__tests__/fixtures/templates/plate-map.json +54 -0
  266. package/src/__tests__/fixtures/templates/qpcr-plate.json +96 -0
  267. package/src/__tests__/fixtures/templates/sample-sheet.json +71 -0
  268. package/src/__tests__/templates/templates.test.ts +1043 -0
  269. package/src/components/ActionItem.vue +82 -0
  270. package/src/components/AppAvatarMenu.vue +15 -69
  271. package/src/components/AppLayout.story.vue +25 -25
  272. package/src/components/AppPageSelector.vue +63 -94
  273. package/src/components/AppPillNav.vue +44 -39
  274. package/src/components/AppPluginSwitcher.vue +41 -145
  275. package/src/components/AppSidebar.story.vue +94 -0
  276. package/src/components/AppSidebar.vue +187 -12
  277. package/src/components/{ToastNotification.story.vue → AppToastContainer.story.vue} +6 -6
  278. package/src/components/AppToastContainer.vue +62 -0
  279. package/src/components/AppTopBar.story.vue +7 -30
  280. package/src/components/AppTopBar.vue +251 -57
  281. package/src/components/BaseModal.vue +3 -5
  282. package/src/components/BaseRadioGroup.vue +7 -3
  283. package/src/components/BaseSelect.vue +11 -7
  284. package/src/components/BaseTabs.vue +6 -4
  285. package/src/components/BatchProgressList.vue +5 -8
  286. package/src/components/BioTemplateExperimentWorkspaceView.story.vue +123 -0
  287. package/src/components/BioTemplateExperimentWorkspaceView.vue +337 -0
  288. package/src/components/BioTemplatePackWorkspaceView.story.vue +107 -0
  289. package/src/components/BioTemplatePackWorkspaceView.vue +176 -0
  290. package/src/components/BioTemplatePresetWorkspaceView.story.vue +151 -0
  291. package/src/components/BioTemplatePresetWorkspaceView.vue +392 -0
  292. package/src/components/BioTemplateRenderer.story.vue +57 -0
  293. package/src/components/BioTemplateRenderer.vue +269 -0
  294. package/src/components/Breadcrumb.vue +14 -8
  295. package/src/components/CalendarGridPanel.vue +120 -0
  296. package/src/components/ConcentrationInput.vue +27 -64
  297. package/src/components/ControlWorkspaceView.story.vue +336 -0
  298. package/src/components/ControlWorkspaceView.vue +347 -0
  299. package/src/components/DataFrame.vue +34 -50
  300. package/src/components/DatePicker.vue +59 -192
  301. package/src/components/DateTimePicker.vue +50 -171
  302. package/src/components/DropdownButton.vue +14 -32
  303. package/src/components/EmptyState.vue +4 -2
  304. package/src/components/ExperimentPopover.vue +5 -22
  305. package/src/components/FormBuilder.vue +124 -27
  306. package/src/components/FormFieldRenderer.vue +15 -38
  307. package/src/components/FormSection.vue +20 -73
  308. package/src/components/GroupAssigner.vue +24 -56
  309. package/src/components/GroupingModal.story.vue +3 -3
  310. package/src/components/GroupingModal.vue +30 -391
  311. package/src/components/MultiSelect.vue +17 -12
  312. package/src/components/PlateMapEditor.vue +3 -8
  313. package/src/components/PluginIcon.vue +2 -22
  314. package/src/components/ProtocolStepEditor.vue +13 -22
  315. package/src/components/ReagentList.vue +25 -33
  316. package/src/components/SampleHierarchyTree.vue +12 -23
  317. package/src/components/SampleSelector.vue +42 -122
  318. package/src/components/SegmentedControl.vue +7 -3
  319. package/src/components/SettingsButton.story.vue +1 -1
  320. package/src/components/SettingsButton.vue +15 -27
  321. package/src/components/SettingsModal.story.vue +1 -1
  322. package/src/components/SettingsModal.vue +120 -29
  323. package/src/components/TagsInput.vue +29 -14
  324. package/src/components/ThemeToggle.vue +9 -7
  325. package/src/components/TimePicker.vue +19 -41
  326. package/src/components/ToastNotification.vue +4 -57
  327. package/src/components/Tooltip.vue +7 -12
  328. package/src/components/WellEditPopup.vue +3 -8
  329. package/src/components/WellPlate.vue +4 -10
  330. package/src/components/index.ts +11 -1
  331. package/src/components/internal/FormFieldRendererInternal.vue +50 -0
  332. package/src/components/internal/FormSectionRenderer.vue +78 -0
  333. package/src/composables/index.ts +212 -0
  334. package/src/composables/platformContextHelpers.ts +74 -0
  335. package/src/composables/useBioTemplateComponents.ts +93 -0
  336. package/src/composables/useBioTemplateControls.ts +41 -0
  337. package/src/composables/useBioTemplatePackWorkspace.ts +181 -0
  338. package/src/composables/useBioTemplatePresetWorkspace.ts +337 -0
  339. package/src/composables/useBioTemplateWorkspace.ts +139 -0
  340. package/src/composables/useCalendarGrid.ts +140 -0
  341. package/src/composables/useControlSchema.ts +1274 -0
  342. package/src/composables/useDebouncedWatch.ts +119 -0
  343. package/src/composables/useDropdownState.ts +83 -0
  344. package/src/composables/useEventListener.ts +111 -0
  345. package/src/composables/useExpansionSet.ts +117 -0
  346. package/src/composables/useExperimentData.ts +20 -11
  347. package/src/composables/useExperimentSave.ts +202 -50
  348. package/src/composables/useExperimentSelector.ts +86 -72
  349. package/src/composables/useForm.ts +49 -4
  350. package/src/composables/useFormBuilder.ts +93 -42
  351. package/src/composables/useGroupAssignment.ts +148 -0
  352. package/src/composables/useListSelection.ts +158 -0
  353. package/src/composables/usePluginApi.ts +7 -14
  354. package/src/composables/usePluginClient.ts +425 -0
  355. package/src/composables/usePluginConfig.ts +34 -13
  356. package/src/composables/useRequestSyncState.ts +126 -0
  357. package/src/composables/useSampleGroups.ts +126 -0
  358. package/src/composables/useSelectionLimit.ts +57 -0
  359. package/src/composables/useSortedItems.ts +118 -0
  360. package/src/composables/useTemplateCollection.ts +229 -0
  361. package/src/composables/useTextSearch.ts +60 -0
  362. package/src/composables/useTheme.ts +2 -28
  363. package/src/composables/useTimeUtils.ts +26 -2
  364. package/src/composables/useWellPlateEditor.ts +13 -9
  365. package/src/index.ts +224 -4
  366. package/src/install.ts +11 -4
  367. package/src/stores/settings.ts +13 -9
  368. package/src/styles/components/app-page-selector.css +23 -0
  369. package/src/styles/components/app-pill-nav.css +7 -0
  370. package/src/styles/components/app-top-bar.css +34 -0
  371. package/src/styles/components/concentration-input.css +3 -142
  372. package/src/styles/components/empty-state.css +0 -16
  373. package/src/styles/components/settings-button.css +3 -66
  374. package/src/styles/components/theme-toggle.css +3 -66
  375. package/src/styles/index.css +0 -1
  376. package/src/templates/adapters.ts +785 -0
  377. package/src/templates/builders.ts +2149 -0
  378. package/src/templates/catalog.ts +245 -0
  379. package/src/templates/componentBindings.ts +615 -0
  380. package/src/templates/controlSchemas.ts +718 -0
  381. package/src/templates/index.ts +314 -0
  382. package/src/templates/lookup.ts +18 -0
  383. package/src/templates/packs.ts +156 -0
  384. package/src/templates/presets.ts +146 -0
  385. package/src/templates/types.ts +668 -0
  386. package/src/types/components.ts +41 -4
  387. package/src/types/form-builder.ts +7 -2
  388. package/src/types/index.ts +14 -0
  389. package/src/types/platform.ts +7 -1
  390. package/src/utils/formModelSync.ts +52 -0
  391. package/src/utils/items.ts +28 -0
  392. package/src/utils/options.ts +23 -0
  393. package/src/utils/pluginIcon.ts +30 -0
  394. package/dist/auth-DsI0rQ7_.js.map +0 -1
  395. package/dist/components-_XqPEhP9.js.map +0 -1
  396. package/dist/composables-tiZqLu1M.js.map +0 -1
  397. package/dist/useScheduleDrag-CA9sGNJG.js +0 -7181
  398. package/dist/useScheduleDrag-CA9sGNJG.js.map +0 -1
  399. package/src/styles/components/grouping-modal.css +0 -323
@@ -4,6 +4,7 @@ import { computed, ref } from 'vue'
4
4
  import { createPinia } from 'pinia'
5
5
  import AppTopBar from '../../components/AppTopBar.vue'
6
6
  import { usePlatformContext } from '../../composables/usePlatformContext'
7
+ import { controlsToTopBarTabs, defineControlModel, defineControls } from '../../composables/useControlSchema'
7
8
  import ThemeToggle from '../../components/ThemeToggle.vue'
8
9
  import SettingsModal from '../../components/SettingsModal.vue'
9
10
  import type { TopBarSettingsConfig } from '../../types/components'
@@ -223,6 +224,123 @@ describe('AppTopBar', () => {
223
224
  expect(settingsModal.props('tabs')).toEqual(settingsConfig.tabs)
224
225
  })
225
226
 
227
+ it('should normalize shorthand settings tabs for SettingsModal', () => {
228
+ const wrapper = createWrapper({
229
+ title: 'Test App',
230
+ showSettings: true,
231
+ settingsConfig: {
232
+ tabs: ['General', 'Advanced'],
233
+ showAppearance: false,
234
+ },
235
+ })
236
+
237
+ const settingsModal = wrapper.findComponent(SettingsModal)
238
+ expect(settingsModal.props('tabs')).toEqual([
239
+ { id: 'General', label: 'General' },
240
+ { id: 'Advanced', label: 'Advanced' },
241
+ ])
242
+ })
243
+
244
+ it('should sync external schema values into SettingsModal fields', async () => {
245
+ const schema = {
246
+ groups: [
247
+ {
248
+ id: 'general',
249
+ label: 'General',
250
+ fields: [
251
+ { name: 'threshold', label: 'Threshold', type: 'number' as const },
252
+ {
253
+ name: 'method',
254
+ label: 'Method',
255
+ type: 'select' as const,
256
+ props: {
257
+ options: [
258
+ { value: 'linear', label: 'Linear' },
259
+ { value: 'logistic', label: 'Logistic' },
260
+ ],
261
+ },
262
+ },
263
+ ],
264
+ },
265
+ ],
266
+ }
267
+ const settingsConfig: TopBarSettingsConfig = {
268
+ showAppearance: false,
269
+ schema,
270
+ values: { threshold: 0.25, method: 'linear', externalContext: 'experiment-1' },
271
+ }
272
+
273
+ const wrapper = createWrapper({
274
+ title: 'Test App',
275
+ showSettings: true,
276
+ settingsConfig,
277
+ })
278
+
279
+ await wrapper.setProps({
280
+ settingsConfig: {
281
+ ...settingsConfig,
282
+ values: { threshold: 0.75, method: 'logistic', externalContext: 'experiment-2' },
283
+ },
284
+ })
285
+
286
+ expect((wrapper.find('input').element as HTMLInputElement).value).toBe('0.75')
287
+ expect((wrapper.find('select').element as HTMLSelectElement).value).toBe('logistic')
288
+ })
289
+
290
+ it('should render compact controls in SettingsModal', () => {
291
+ const controls = defineControls({
292
+ threshold: {
293
+ label: 'Threshold',
294
+ default: 0.25,
295
+ section: 'parameters',
296
+ sectionLabel: 'Parameters',
297
+ },
298
+ })
299
+ const settingsConfig: TopBarSettingsConfig = {
300
+ showAppearance: false,
301
+ controls,
302
+ }
303
+
304
+ const wrapper = createWrapper({
305
+ title: 'Test App',
306
+ showSettings: true,
307
+ settingsConfig,
308
+ })
309
+
310
+ const settingsModal = wrapper.findComponent(SettingsModal)
311
+ expect(settingsModal.props('controls')).toStrictEqual(controls)
312
+ expect(wrapper.text()).toContain('Threshold')
313
+ expect((wrapper.find('input').element as HTMLInputElement).value).toBe('0.25')
314
+ })
315
+
316
+ it('should pass control model to SettingsModal', () => {
317
+ const model = defineControlModel({
318
+ controls: {
319
+ threshold: {
320
+ label: 'Threshold',
321
+ default: 0.25,
322
+ section: 'parameters',
323
+ sectionLabel: 'Parameters',
324
+ },
325
+ },
326
+ })
327
+ const settingsConfig: TopBarSettingsConfig = {
328
+ showAppearance: false,
329
+ model,
330
+ }
331
+
332
+ const wrapper = createWrapper({
333
+ title: 'Test App',
334
+ showSettings: true,
335
+ settingsConfig,
336
+ })
337
+
338
+ const settingsModal = wrapper.findComponent(SettingsModal)
339
+ expect(settingsModal.props('model')).toStrictEqual(model)
340
+ expect(wrapper.text()).toContain('Threshold')
341
+ expect((wrapper.find('input').element as HTMLInputElement).value).toBe('0.25')
342
+ })
343
+
226
344
  it('should pass showAppearance to SettingsModal', () => {
227
345
  const settingsConfig: TopBarSettingsConfig = {
228
346
  showAppearance: false,
@@ -294,6 +412,22 @@ describe('AppTopBar', () => {
294
412
  expect(settingsModal.vm.$slots['tab-general']).toBeDefined()
295
413
  })
296
414
 
415
+ it('should forward shorthand settings-tab slots to SettingsModal', () => {
416
+ const wrapper = createWrapper({
417
+ title: 'Test App',
418
+ showSettings: true,
419
+ settingsConfig: {
420
+ tabs: ['General'],
421
+ showAppearance: false,
422
+ },
423
+ }, {
424
+ 'settings-tab-General': '<div>General Content</div>',
425
+ })
426
+
427
+ const settingsModal = wrapper.findComponent(SettingsModal)
428
+ expect(settingsModal.vm.$slots['tab-General']).toBeDefined()
429
+ })
430
+
297
431
  it('should forward multiple settings-tab slots', () => {
298
432
  const settingsConfig: TopBarSettingsConfig = {
299
433
  tabs: [
@@ -492,6 +626,100 @@ describe('AppTopBar', () => {
492
626
  expect(wrapper.findComponent(ThemeToggle).exists()).toBe(true)
493
627
  expect(wrapper.find('.mint-topbar__settings-btn').exists()).toBe(true)
494
628
  })
629
+
630
+ it('renders tabs generated from a compact control schema', async () => {
631
+ const runIcon = 'M8 5v14l11-7z'
632
+ const controls = defineControls({
633
+ threshold: {
634
+ default: 0.05,
635
+ section: 'parameters',
636
+ view: 'analysis',
637
+ },
638
+ chartScale: {
639
+ default: 'linear',
640
+ section: 'display',
641
+ view: 'results',
642
+ },
643
+ })
644
+ const wrapper = createWrapper({
645
+ tabs: controlsToTopBarTabs(controls, {
646
+ views: {
647
+ analysis: { label: 'Run', icon: runIcon },
648
+ results: { label: 'Results' },
649
+ },
650
+ }),
651
+ currentTabId: 'analysis',
652
+ })
653
+
654
+ const tabs = wrapper.findAll('.mint-topbar-tab')
655
+ expect(tabs.map(tab => tab.text())).toEqual(['Run', 'Results'])
656
+ expect(tabs[0].classes()).toContain('mint-topbar-tab--active')
657
+ expect(tabs[0].get('.mint-topbar-tab-icon path').attributes('d')).toBe(runIcon)
658
+ expect(tabs[1].find('.mint-topbar-tab-icon').exists()).toBe(false)
659
+
660
+ await tabs[1].trigger('click')
661
+ expect(wrapper.emitted('tab-select')?.[0][0]).toMatchObject({ id: 'results', label: 'Results' })
662
+ })
663
+
664
+ it('uses shared dropdown state for the legacy page menu', async () => {
665
+ const wrapper = createWrapper({
666
+ pluginName: 'Test Plugin',
667
+ pages: [
668
+ { id: 'dashboard', label: 'Dashboard' },
669
+ { id: 'settings', label: 'Settings' },
670
+ ],
671
+ })
672
+ const dropdown = () => wrapper.get('.mint-topbar-dropdown')
673
+
674
+ expect(dropdown().attributes('style')).toContain('display: none')
675
+
676
+ await wrapper.get('.mint-topbar-plugin-name').trigger('click')
677
+ expect(dropdown().attributes('style') ?? '').not.toContain('display: none')
678
+
679
+ document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }))
680
+ await wrapper.vm.$nextTick()
681
+ expect(dropdown().attributes('style')).toContain('display: none')
682
+
683
+ await wrapper.get('.mint-topbar-plugin-name').trigger('click')
684
+ expect(dropdown().attributes('style') ?? '').not.toContain('display: none')
685
+
686
+ document.body.dispatchEvent(new MouseEvent('click', { bubbles: true }))
687
+ await wrapper.vm.$nextTick()
688
+ expect(dropdown().attributes('style')).toContain('display: none')
689
+
690
+ wrapper.unmount()
691
+ })
692
+
693
+ it('renders PluginIcon-compatible metadata icons in the legacy page menu', async () => {
694
+ const iconPath = 'M4 19h16M7 16V8m5 8V4m5 12v-6'
695
+ const pngIcon = 'data:image/png;base64,iVBORw0KGgo='
696
+ const wrapper = createWrapper({
697
+ pluginName: 'Test Plugin',
698
+ pages: [
699
+ { id: 'dashboard', label: 'Dashboard', to: '/', icon: iconPath, description: 'Overview' },
700
+ { id: 'image', label: 'Image', to: '/image', icon: pngIcon },
701
+ { id: 'legacy', label: 'Legacy', icon: 'home' },
702
+ ],
703
+ currentPageId: 'dashboard',
704
+ })
705
+
706
+ await wrapper.get('.mint-topbar-plugin-name').trigger('click')
707
+
708
+ const icons = wrapper.findAllComponents({ name: 'PluginIcon' })
709
+ expect(icons).toHaveLength(2)
710
+ expect(icons[0].props('icon')).toBe(iconPath)
711
+ expect(icons[1].props('icon')).toBe(pngIcon)
712
+ expect(icons.every((icon) => icon.props('variant') === 'tinted')).toBe(true)
713
+
714
+ const pathIcons = wrapper.findAll('.mint-plugin-icon__svg')
715
+ expect(pathIcons).toHaveLength(1)
716
+ expect(pathIcons[0].find('path').attributes('d')).toBe(iconPath)
717
+
718
+ const imageIcons = wrapper.findAll('.mint-plugin-icon__img')
719
+ expect(imageIcons).toHaveLength(1)
720
+ expect(imageIcons[0].attributes('src')).toBe(pngIcon)
721
+ expect(wrapper.find('.mint-topbar-dropdown-item__description').text()).toBe('Overview')
722
+ })
495
723
  })
496
724
 
497
725
  describe('edge cases', () => {
@@ -697,5 +925,160 @@ describe('AppTopBar', () => {
697
925
  expect(wrapper.find('.mint-topbar__logo-text').exists()).toBe(true)
698
926
  expect(wrapper.find('.mint-topbar__logo-text').text()).toBe('M')
699
927
  })
928
+
929
+ it('uses platform plugin nav metadata when pluginName and pages are omitted', async () => {
930
+ const previousPath = window.location.pathname
931
+ window.history.pushState({}, '', '/test/analysis')
932
+ try {
933
+ const pluginIcon = 'M13 10V3L4 14h7v7l9-11h-7z'
934
+ const analysisIcon = 'M4 19h16M7 16V8m5 8V4m5 12v-6'
935
+ mockPlatformCtx({
936
+ isIntegrated: true,
937
+ plugin: {
938
+ id: 'test',
939
+ name: 'Dose Designer',
940
+ version: '1.0',
941
+ icon: pluginIcon,
942
+ route_prefix: '/test',
943
+ api_prefix: '/api/test',
944
+ nav_items: [
945
+ { path: '/', label: 'Dashboard' },
946
+ { path: '/analysis', label: 'Analysis', icon: analysisIcon },
947
+ ],
948
+ },
949
+ })
950
+
951
+ const wrapper = createWrapper()
952
+ await wrapper.get('.mint-topbar-plugin-name').trigger('click')
953
+
954
+ expect(wrapper.get('.mint-topbar-plugin-name').text()).toContain('Dose Designer')
955
+ expect(wrapper.get('.mint-topbar-current-page').text()).toBe('Analysis')
956
+ expect(wrapper.findAll('.mint-topbar-dropdown-item__label').map(item => item.text())).toEqual([
957
+ 'Dashboard',
958
+ 'Analysis',
959
+ ])
960
+ const dropdownIcons = wrapper
961
+ .findAllComponents({ name: 'PluginIcon' })
962
+ .filter(icon => icon.classes().includes('mint-topbar-dropdown-item__icon'))
963
+ expect(dropdownIcons.map(icon => icon.props('icon'))).toEqual([pluginIcon, analysisIcon])
964
+ expect(wrapper.findAll('.mint-topbar-dropdown-item')[1].classes()).toContain('mint-topbar-dropdown-item--active')
965
+ } finally {
966
+ window.history.pushState({}, '', previousPath)
967
+ }
968
+ })
969
+
970
+ it('keeps explicit legacy pages ahead of platform plugin nav metadata', async () => {
971
+ mockPlatformCtx({
972
+ isIntegrated: true,
973
+ plugin: {
974
+ id: 'test',
975
+ name: 'Dose Designer',
976
+ version: '1.0',
977
+ route_prefix: '/test',
978
+ api_prefix: '/api/test',
979
+ nav_items: [{ path: '/stale', label: 'Stale' }],
980
+ },
981
+ })
982
+
983
+ const wrapper = createWrapper({
984
+ pluginName: 'Explicit Plugin',
985
+ pages: [{ id: 'overview', label: 'Overview', to: '/' }],
986
+ })
987
+ await wrapper.get('.mint-topbar-plugin-name').trigger('click')
988
+
989
+ expect(wrapper.get('.mint-topbar-plugin-name').text()).toContain('Explicit Plugin')
990
+ expect(wrapper.findAll('.mint-topbar-dropdown-item__label').map(item => item.text())).toEqual(['Overview'])
991
+ })
992
+ })
993
+
994
+ describe('shorthand navigation items', () => {
995
+ it('accepts string shorthand for pageSelector', async () => {
996
+ const wrapper = createWrapper({
997
+ currentPageSelectorId: 'Workspace',
998
+ pageSelector: ['Workspace', 'Results'],
999
+ })
1000
+
1001
+ expect(wrapper.find('.mint-page-selector__label').text()).toBe('Workspace')
1002
+
1003
+ await wrapper.get('.mint-page-selector__trigger').trigger('click')
1004
+ await wrapper.findAll('.mint-page-selector__item')[1].trigger('click')
1005
+
1006
+ expect(wrapper.emitted('page-selector-select')).toEqual([
1007
+ [{ id: 'Results', label: 'Results' }],
1008
+ ])
1009
+ })
1010
+
1011
+ it('accepts string shorthand for pillNav', async () => {
1012
+ const wrapper = createWrapper({
1013
+ currentPillId: 'Run',
1014
+ pillNav: ['Run', 'Results'],
1015
+ })
1016
+
1017
+ expect(wrapper.findAll('.mint-pill-nav__item').map(item => item.text())).toEqual([
1018
+ 'Run',
1019
+ 'Results',
1020
+ ])
1021
+
1022
+ await wrapper.findAll('.mint-pill-nav__item')[1].trigger('click')
1023
+
1024
+ expect(wrapper.emitted('pill-select')).toEqual([
1025
+ [{ id: 'Results', label: 'Results' }],
1026
+ ])
1027
+ })
1028
+
1029
+ it('accepts string shorthand for legacy pages', async () => {
1030
+ const wrapper = createWrapper({
1031
+ pluginName: 'Analysis',
1032
+ currentPageId: 'Overview',
1033
+ pages: ['Overview', 'Settings'],
1034
+ })
1035
+
1036
+ await wrapper.get('.mint-topbar-plugin-name').trigger('click')
1037
+ await wrapper.findAll('.mint-topbar-dropdown-item')[1].trigger('click')
1038
+
1039
+ expect(wrapper.emitted('page-select')).toEqual([
1040
+ [{ id: 'Settings', label: 'Settings' }],
1041
+ ])
1042
+ })
1043
+
1044
+ it('accepts string shorthand for legacy tabs', async () => {
1045
+ const wrapper = createWrapper({
1046
+ currentTabId: 'Run',
1047
+ tabs: ['Run', 'Results'],
1048
+ })
1049
+
1050
+ expect(wrapper.findAll('.mint-topbar-tab').map(tab => tab.text())).toEqual([
1051
+ 'Run',
1052
+ 'Results',
1053
+ ])
1054
+
1055
+ await wrapper.findAll('.mint-topbar-tab')[1].trigger('click')
1056
+
1057
+ expect(wrapper.emitted('tab-select')).toEqual([
1058
+ [{ id: 'Results', label: 'Results' }],
1059
+ ])
1060
+ })
1061
+
1062
+ it('accepts string shorthand for legacy tab dropdown options', async () => {
1063
+ const wrapper = createWrapper({
1064
+ currentTabId: 'Summary',
1065
+ tabs: [
1066
+ { id: 'analysis', label: 'Analysis', children: ['Summary', 'Details'] },
1067
+ ],
1068
+ })
1069
+
1070
+ await wrapper.get('.mint-topbar-tab').trigger('click')
1071
+ expect(wrapper.findAll('.mint-topbar-dropdown-item').map(item => item.text())).toEqual([
1072
+ 'Summary',
1073
+ 'Details',
1074
+ ])
1075
+
1076
+ await wrapper.findAll('.mint-topbar-dropdown-item')[1].trigger('click')
1077
+
1078
+ expect(wrapper.emitted('tab-option-select')?.[0]?.[0]).toEqual({
1079
+ id: 'Details',
1080
+ label: 'Details',
1081
+ })
1082
+ })
700
1083
  })
701
1084
  })
@@ -0,0 +1,25 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vitest'
3
+ import BaseRadioGroup from '../../components/BaseRadioGroup.vue'
4
+
5
+ describe('BaseRadioGroup', () => {
6
+ it('accepts string and number shorthand options', async () => {
7
+ const wrapper = mount(BaseRadioGroup, {
8
+ props: {
9
+ name: 'plate-format',
10
+ modelValue: 96,
11
+ options: [96, 384, { value: 'custom', label: 'Custom' }],
12
+ },
13
+ })
14
+
15
+ expect(wrapper.findAll('.mint-radio-option__label').map(label => label.text())).toEqual([
16
+ '96',
17
+ '384',
18
+ 'Custom',
19
+ ])
20
+
21
+ await wrapper.findAll('input')[1].setValue(true)
22
+
23
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([384])
24
+ })
25
+ })
@@ -0,0 +1,21 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vitest'
3
+ import BaseSelect from '../../components/BaseSelect.vue'
4
+
5
+ describe('BaseSelect', () => {
6
+ it('accepts string and number shorthand options', async () => {
7
+ const wrapper = mount(BaseSelect, {
8
+ props: {
9
+ modelValue: 'linear',
10
+ options: ['linear', 96, { value: 'log', label: 'Log scale' }],
11
+ },
12
+ })
13
+
14
+ const options = wrapper.findAll('option')
15
+ expect(options.map(option => option.text())).toEqual(['linear', '96', 'Log scale'])
16
+
17
+ await wrapper.get('select').setValue('96')
18
+
19
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([96])
20
+ })
21
+ })
@@ -0,0 +1,25 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vitest'
3
+ import BaseTabs from '../../components/BaseTabs.vue'
4
+
5
+ describe('BaseTabs', () => {
6
+ it('accepts string shorthand tabs', async () => {
7
+ const wrapper = mount(BaseTabs, {
8
+ props: {
9
+ modelValue: 'Run',
10
+ tabs: ['Run', 'Results', { id: 'settings', label: 'Settings', disabled: true }],
11
+ },
12
+ })
13
+
14
+ expect(wrapper.findAll('.mint-tab').map(tab => tab.text())).toEqual([
15
+ 'Run',
16
+ 'Results',
17
+ 'Settings',
18
+ ])
19
+
20
+ await wrapper.findAll('.mint-tab')[1].trigger('click')
21
+ await wrapper.findAll('.mint-tab')[2].trigger('click')
22
+
23
+ expect(wrapper.emitted('update:modelValue')).toEqual([['Results']])
24
+ })
25
+ })
@@ -0,0 +1,52 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vitest'
3
+ import BatchProgressList from '../../components/BatchProgressList.vue'
4
+ import type { BatchItem } from '../../types'
5
+
6
+ const items: BatchItem[] = [
7
+ { id: 'a', label: 'Sample A', status: 'completed' },
8
+ { id: 'b', label: 'Sample B', status: 'error', message: 'Import failed' },
9
+ { id: 'c', label: 'Sample C', status: 'processing', progress: 50 },
10
+ ]
11
+
12
+ describe('BatchProgressList', () => {
13
+ it('summarizes progress and emits row actions', async () => {
14
+ const wrapper = mount(BatchProgressList, {
15
+ props: {
16
+ title: 'Import',
17
+ items,
18
+ autoScroll: false,
19
+ },
20
+ })
21
+
22
+ expect(wrapper.find('.mint-batch-progress__percent').text()).toBe('33%')
23
+ expect(wrapper.text()).toContain('1 completed')
24
+ expect(wrapper.text()).toContain('1 failed')
25
+ expect(wrapper.text()).toContain('1 processing')
26
+
27
+ await wrapper.get('.mint-batch-progress__retry-btn').trigger('click')
28
+ await wrapper.get('.mint-batch-progress__cancel-btn').trigger('click')
29
+
30
+ expect(wrapper.emitted('retry')).toEqual([[items[1].id]])
31
+ expect(wrapper.emitted('cancel')).toEqual([[items[2].id]])
32
+ })
33
+
34
+ it('uses shared expansion state for error messages', async () => {
35
+ const wrapper = mount(BatchProgressList, {
36
+ props: {
37
+ items,
38
+ autoScroll: false,
39
+ },
40
+ })
41
+
42
+ expect(wrapper.find('.mint-batch-progress__item-message').exists()).toBe(false)
43
+
44
+ await wrapper.get('.mint-batch-progress__error-toggle').trigger('click')
45
+ expect(wrapper.get('.mint-batch-progress__error-toggle').text()).toBe('Hide error')
46
+ expect(wrapper.get('.mint-batch-progress__item-message').text()).toBe('Import failed')
47
+
48
+ await wrapper.get('.mint-batch-progress__error-toggle').trigger('click')
49
+ expect(wrapper.get('.mint-batch-progress__error-toggle').text()).toBe('Show error')
50
+ expect(wrapper.find('.mint-batch-progress__item-message').exists()).toBe(false)
51
+ })
52
+ })
@@ -0,0 +1,153 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { h } from 'vue'
3
+ import { describe, expect, it, vi } from 'vitest'
4
+ import BioTemplateExperimentWorkspaceView from '../../components/BioTemplateExperimentWorkspaceView.vue'
5
+ import {
6
+ createPlateMapTemplate,
7
+ createWellPlateScreenCollection,
8
+ } from '../../templates'
9
+
10
+ describe('BioTemplateExperimentWorkspaceView', () => {
11
+ it('renders load/save/reset controls and a template preview', async () => {
12
+ const wrapper = mount(BioTemplateExperimentWorkspaceView, {
13
+ props: {
14
+ target: createPlateMapTemplate(),
15
+ label: 'Plate Map',
16
+ hasExperiment: true,
17
+ currentExperimentId: 7,
18
+ },
19
+ global: {
20
+ stubs: {
21
+ PlateMapEditor: true,
22
+ WellPlate: true,
23
+ },
24
+ },
25
+ })
26
+
27
+ expect(wrapper.text()).toContain('Plate Map template data saves')
28
+ expect(wrapper.text()).toContain('experiment: 7')
29
+ expect(wrapper.find('.mint-bio-template-renderer').exists()).toBe(true)
30
+
31
+ const buttons = wrapper.findAll('button')
32
+ await buttons[0].trigger('click')
33
+ await buttons[1].trigger('click')
34
+ await buttons[2].trigger('click')
35
+
36
+ expect(wrapper.emitted('load')).toHaveLength(1)
37
+ expect(wrapper.emitted('reset')).toHaveLength(1)
38
+ expect(wrapper.emitted('save')).toHaveLength(1)
39
+ })
40
+
41
+ it('renders collection summaries and disables experiment actions without an experiment', () => {
42
+ const wrapper = mount(BioTemplateExperimentWorkspaceView, {
43
+ props: {
44
+ target: createWellPlateScreenCollection(),
45
+ label: 'Wellplate Screen',
46
+ kind: 'collection',
47
+ showTemplateSummary: true,
48
+ hasExperiment: false,
49
+ },
50
+ global: {
51
+ stubs: {
52
+ DataFrame: true,
53
+ DoseCalculator: true,
54
+ PlateMapEditor: true,
55
+ SampleSelector: true,
56
+ WellPlate: true,
57
+ },
58
+ },
59
+ })
60
+
61
+ expect(wrapper.text()).toContain('Wellplate Screen saves the full template collection')
62
+ expect(wrapper.text()).toContain('plate-map')
63
+ expect(wrapper.text()).toContain('dose-response')
64
+ expect(wrapper.findAll('button')[0].attributes('disabled')).toBeDefined()
65
+ expect(wrapper.findAll('button')[2].attributes('disabled')).toBeDefined()
66
+ })
67
+
68
+ it('uses grouped status and actions when provided', async () => {
69
+ const load = vi.fn()
70
+ const reset = vi.fn()
71
+ const save = vi.fn()
72
+ const lastLoadedAt = new Date('2026-01-02T03:04:05Z')
73
+ const wrapper = mount(BioTemplateExperimentWorkspaceView, {
74
+ props: {
75
+ target: createPlateMapTemplate(),
76
+ label: 'Plate Map',
77
+ status: {
78
+ loading: false,
79
+ saving: false,
80
+ error: 'Template request failed.',
81
+ hasExperiment: true,
82
+ currentExperimentId: 23,
83
+ lastLoadedAt,
84
+ lastSavedAt: null,
85
+ },
86
+ actions: { load, reset, save },
87
+ },
88
+ global: {
89
+ stubs: {
90
+ PlateMapEditor: true,
91
+ WellPlate: true,
92
+ },
93
+ },
94
+ })
95
+
96
+ expect(wrapper.text()).toContain('Template request failed.')
97
+ expect(wrapper.text()).toContain('experiment: 23')
98
+
99
+ const buttons = wrapper.findAll('button')
100
+ expect(buttons[0].attributes('disabled')).toBeUndefined()
101
+ await buttons[0].trigger('click')
102
+ await buttons[1].trigger('click')
103
+ await buttons[2].trigger('click')
104
+
105
+ expect(load).toHaveBeenCalledTimes(1)
106
+ expect(reset).toHaveBeenCalledTimes(1)
107
+ expect(save).toHaveBeenCalledTimes(1)
108
+ expect(wrapper.emitted('load')).toBeUndefined()
109
+ expect(wrapper.emitted('reset')).toBeUndefined()
110
+ expect(wrapper.emitted('save')).toBeUndefined()
111
+ })
112
+
113
+ it('exposes target component props to the default slot', () => {
114
+ const wrapper = mount(BioTemplateExperimentWorkspaceView, {
115
+ props: {
116
+ target: createWellPlateScreenCollection(),
117
+ label: 'Wellplate Screen',
118
+ kind: 'collection',
119
+ },
120
+ slots: {
121
+ default: ({ bindings, componentPropsById, getComponentProps }) => h(
122
+ 'pre',
123
+ { class: 'component-props' },
124
+ JSON.stringify({
125
+ doseMode: componentPropsById['dose-response:DoseCalculator'].mode,
126
+ bindingDoseMode: bindings.componentPropsById['dose-response:DoseCalculator'].mode,
127
+ hasPlateWells: Boolean(componentPropsById['plate-map:WellPlate'].wells),
128
+ doseModeFromGetter: getComponentProps('DoseCalculator')?.mode,
129
+ bindingDoseModeFromGetter: bindings.getComponentProps('DoseCalculator')?.mode,
130
+ }),
131
+ ),
132
+ },
133
+ global: {
134
+ stubs: {
135
+ DataFrame: true,
136
+ DoseCalculator: true,
137
+ PlateMapEditor: true,
138
+ SampleSelector: true,
139
+ WellPlate: true,
140
+ },
141
+ },
142
+ })
143
+
144
+ expect(JSON.parse(wrapper.find('.component-props').text())).toEqual({
145
+ doseMode: 'serial',
146
+ bindingDoseMode: 'serial',
147
+ hasPlateWells: true,
148
+ doseModeFromGetter: 'serial',
149
+ bindingDoseModeFromGetter: 'serial',
150
+ })
151
+ expect(wrapper.find('.mint-bio-template-renderer').exists()).toBe(false)
152
+ })
153
+ })