@morscherlab/mint-sdk 1.0.0-rc.4 → 1.0.0-rc.6

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 (324) hide show
  1. package/dist/__tests__/components/AppTopBar.navigation.test.d.ts +1 -0
  2. package/dist/__tests__/components/DoseCalculatorVolumeField.test.d.ts +1 -0
  3. package/dist/__tests__/components/PlateMapEditorToolbarInternal.test.d.ts +1 -0
  4. package/dist/__tests__/components/PluginWorkspaceView.controls.test.d.ts +1 -0
  5. package/dist/__tests__/components/PluginWorkspaceView.navigation.test.d.ts +1 -0
  6. package/dist/__tests__/components/PluginWorkspaceView.shell.test.d.ts +1 -0
  7. package/dist/__tests__/components/ProtocolStep.presentation.test.d.ts +1 -0
  8. package/dist/__tests__/components/ProtocolStepEditor.state.test.d.ts +1 -0
  9. package/dist/__tests__/components/ProtocolStepParameterField.test.d.ts +1 -0
  10. package/dist/__tests__/components/ReagentList.presentation.test.d.ts +1 -0
  11. package/dist/__tests__/components/SampleSelector.colors.test.d.ts +1 -0
  12. package/dist/__tests__/components/SampleSelector.drag.test.d.ts +1 -0
  13. package/dist/__tests__/components/SampleSelector.groups.test.d.ts +1 -0
  14. package/dist/__tests__/components/SampleSelector.selection.test.d.ts +1 -0
  15. package/dist/__tests__/components/SampleSelectorSampleRow.test.d.ts +1 -0
  16. package/dist/__tests__/components/ScheduleCalendar.test.d.ts +1 -0
  17. package/dist/__tests__/components/SettingsModal.schema.test.d.ts +1 -0
  18. package/dist/__tests__/components/WellPlate.colors.test.d.ts +1 -0
  19. package/dist/__tests__/components/WellPlate.conditions.test.d.ts +1 -0
  20. package/dist/__tests__/components/WellPlate.geometry.test.d.ts +1 -0
  21. package/dist/__tests__/components/WellPlate.interaction.test.d.ts +1 -0
  22. package/dist/__tests__/components/WellPlate.legend.test.d.ts +1 -0
  23. package/dist/__tests__/components/WellPlate.rendering.test.d.ts +1 -0
  24. package/dist/__tests__/components/WellPlate.sampleDrop.test.d.ts +1 -0
  25. package/dist/__tests__/composables/autoGroup/classify.test.d.ts +1 -0
  26. package/dist/__tests__/composables/autoGroup/columns.test.d.ts +1 -0
  27. package/dist/__tests__/composables/autoGroup/compose.test.d.ts +1 -0
  28. package/dist/__tests__/composables/autoGroup/cooccurrence.test.d.ts +1 -0
  29. package/dist/__tests__/composables/autoGroup/fingerprint.test.d.ts +1 -0
  30. package/dist/__tests__/composables/autoGroup/integration.test.d.ts +1 -0
  31. package/dist/__tests__/composables/autoGroup/template.test.d.ts +1 -0
  32. package/dist/__tests__/composables/autoGroup/tokenize.test.d.ts +1 -0
  33. package/dist/__tests__/composables/useAutoGroupInputSources.test.d.ts +1 -0
  34. package/dist/__tests__/composables/useScheduleCalendarLayout.test.d.ts +1 -0
  35. package/dist/__tests__/docs/extractDocsComponents.test.d.ts +1 -0
  36. package/dist/__tests__/docs/extractDocsExports.test.d.ts +1 -0
  37. package/dist/__tests__/docs/extractDocsParsing.test.d.ts +1 -0
  38. package/dist/__tests__/docs/extractDocsTemplates.test.d.ts +1 -0
  39. package/dist/__tests__/docs/extractDocsTheme.test.d.ts +1 -0
  40. package/dist/components/AppSidebar.vue.d.ts +9 -6
  41. package/dist/components/AppTopBar.navigation.d.ts +11 -0
  42. package/dist/components/BaseButton.vue.d.ts +1 -1
  43. package/dist/components/BaseCheckbox.vue.d.ts +7 -2
  44. package/dist/components/BaseInput.vue.d.ts +2 -2
  45. package/dist/components/BasePill.vue.d.ts +2 -2
  46. package/dist/components/BaseRadioGroup.vue.d.ts +2 -2
  47. package/dist/components/BaseSelect.vue.d.ts +1 -1
  48. package/dist/components/BaseSlider.vue.d.ts +2 -2
  49. package/dist/components/BaseTextarea.vue.d.ts +2 -2
  50. package/dist/components/BaseToggle.vue.d.ts +7 -2
  51. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
  52. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -1
  53. package/dist/components/CollapsibleCard.vue.d.ts +9 -0
  54. package/dist/components/ColorSlider.vue.d.ts +2 -2
  55. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  56. package/dist/components/ControlWorkspaceView.vue.d.ts +4 -4
  57. package/dist/components/DatePicker.vue.d.ts +1 -1
  58. package/dist/components/DateTimePicker.vue.d.ts +2 -2
  59. package/dist/components/DoseCalculatorVolumeField.vue.d.ts +15 -0
  60. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +2 -2
  61. package/dist/components/DropdownButton.vue.d.ts +1 -1
  62. package/dist/components/FileUploader.vue.d.ts +2 -2
  63. package/dist/components/FormulaInput.vue.d.ts +2 -2
  64. package/dist/components/IconButton.vue.d.ts +1 -1
  65. package/dist/components/LoadingSpinner.vue.d.ts +1 -1
  66. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  67. package/dist/components/MultiSelect.vue.d.ts +1 -1
  68. package/dist/components/NumberInput.vue.d.ts +1 -1
  69. package/dist/components/PlateMapEditor.vue.d.ts +6 -6
  70. package/dist/components/PluginWorkspaceView.controls.d.ts +28 -0
  71. package/dist/components/PluginWorkspaceView.navigation.d.ts +29 -0
  72. package/dist/components/PluginWorkspaceView.props.d.ts +151 -0
  73. package/dist/components/PluginWorkspaceView.shell.d.ts +19 -0
  74. package/dist/components/PluginWorkspaceView.vue.d.ts +51 -196
  75. package/dist/components/ProgressBar.vue.d.ts +1 -1
  76. package/dist/components/ProtocolStep.presentation.d.ts +4 -0
  77. package/dist/components/ProtocolStepEditor.state.d.ts +18 -0
  78. package/dist/components/ProtocolStepParameterField.vue.d.ts +12 -0
  79. package/dist/components/ReagentList.presentation.d.ts +16 -0
  80. package/dist/components/ResourceCard.vue.d.ts +1 -1
  81. package/dist/components/SampleSelector.colors.d.ts +13 -0
  82. package/dist/components/SampleSelector.drag.d.ts +24 -0
  83. package/dist/components/SampleSelector.groups.d.ts +15 -0
  84. package/dist/components/SampleSelector.selection.d.ts +26 -0
  85. package/dist/components/SampleSelector.vue.d.ts +4 -1
  86. package/dist/components/SampleSelectorSampleRow.vue.d.ts +21 -0
  87. package/dist/components/SegmentedControl.vue.d.ts +1 -1
  88. package/dist/components/SequenceInput.vue.d.ts +2 -2
  89. package/dist/components/SequenceProgressBar.vue.d.ts +1 -1
  90. package/dist/components/SettingsModal.schema.d.ts +9 -0
  91. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  92. package/dist/components/TagsInput.vue.d.ts +2 -2
  93. package/dist/components/TimePicker.vue.d.ts +2 -2
  94. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  95. package/dist/components/UnitInput.vue.d.ts +2 -2
  96. package/dist/components/WellPlate.colors.d.ts +9 -0
  97. package/dist/components/WellPlate.conditions.d.ts +26 -0
  98. package/dist/components/WellPlate.geometry.d.ts +23 -0
  99. package/dist/components/WellPlate.interaction.d.ts +71 -0
  100. package/dist/components/WellPlate.legend.d.ts +2 -0
  101. package/dist/components/WellPlate.rendering.d.ts +24 -0
  102. package/dist/components/WellPlate.sampleDrop.d.ts +8 -0
  103. package/dist/components/WellPlate.vue.d.ts +1 -1
  104. package/dist/components/index.js +2 -2
  105. package/dist/components/internal/ActionItemInternal.vue.d.ts +1 -1
  106. package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +28 -0
  107. package/dist/{components-DafPc4rM.js → components-Blx4MG--.js} +4073 -4222
  108. package/dist/components-Blx4MG--.js.map +1 -0
  109. package/dist/composables/autoGroup/classKey.d.ts +4 -0
  110. package/dist/composables/autoGroup/classify.d.ts +28 -0
  111. package/dist/composables/autoGroup/colors.d.ts +2 -0
  112. package/dist/composables/autoGroup/columns.d.ts +10 -0
  113. package/dist/composables/autoGroup/compose.d.ts +8 -0
  114. package/dist/composables/autoGroup/cooccurrence.d.ts +2 -0
  115. package/dist/composables/autoGroup/csv-shim.d.ts +2 -0
  116. package/dist/composables/autoGroup/fingerprint.d.ts +3 -0
  117. package/dist/composables/autoGroup/index.d.ts +16 -0
  118. package/dist/composables/autoGroup/replicatePreGroup.d.ts +38 -0
  119. package/dist/composables/autoGroup/template.d.ts +15 -0
  120. package/dist/composables/autoGroup/tokenize.d.ts +8 -0
  121. package/dist/composables/autoGroupConstants.d.ts +1 -0
  122. package/dist/composables/autoGroupGrouping.d.ts +3 -0
  123. package/dist/composables/controlComponentBindings.d.ts +1 -1
  124. package/dist/composables/controlSchemaAdapters.d.ts +1 -1
  125. package/dist/composables/controlSchemaDoseDesign.d.ts +11 -0
  126. package/dist/composables/controlSchemaLayout.d.ts +1 -1
  127. package/dist/composables/controlSchemaModel.d.ts +5 -0
  128. package/dist/composables/controlSchemaNormalize.d.ts +1 -1
  129. package/dist/composables/controlSchemaTypes.d.ts +311 -0
  130. package/dist/composables/controlWorkspaceOptions.d.ts +1 -1
  131. package/dist/composables/formBuilderSchema.d.ts +18 -0
  132. package/dist/composables/index.js +3 -3
  133. package/dist/composables/pluginEndpointBuilder.d.ts +13 -0
  134. package/dist/composables/protocolTemplateCatalog.d.ts +26 -0
  135. package/dist/composables/useAutoGroup.d.ts +61 -74
  136. package/dist/composables/useAutoGroupInputSources.d.ts +32 -0
  137. package/dist/composables/useBioTemplateControls.d.ts +1 -1
  138. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +1 -1
  139. package/dist/composables/useBioTemplateWorkspace.d.ts +1 -1
  140. package/dist/composables/useControlSchema.d.ts +4 -316
  141. package/dist/composables/useControlWorkspace.d.ts +1 -1
  142. package/dist/composables/useForm.d.ts +2 -33
  143. package/dist/composables/useFormBuilder.d.ts +2 -9
  144. package/dist/composables/useFormValidation.d.ts +34 -0
  145. package/dist/composables/usePluginClient.d.ts +1 -4
  146. package/dist/composables/useProtocolTemplates.d.ts +2 -24
  147. package/dist/composables/useScheduleCalendarLayout.d.ts +49 -0
  148. package/dist/{composables-BMkPQhVK.js → composables-CHDjDIQT.js} +33 -31
  149. package/dist/composables-CHDjDIQT.js.map +1 -0
  150. package/dist/index.js +4 -4
  151. package/dist/install.js +2 -2
  152. package/dist/styles.css +1432 -660
  153. package/dist/templates/controlSchemaTypes.d.ts +1 -1
  154. package/dist/templates/index.js +1 -1
  155. package/dist/templates/templateAdapterTypes.d.ts +48 -0
  156. package/dist/templates/templateCreateOptions.d.ts +165 -0
  157. package/dist/templates/templateQpcrTypes.d.ts +42 -0
  158. package/dist/templates/types.d.ts +5 -250
  159. package/dist/{templates-bUAWMn5L.js → templates-DSbHJC4v.js} +1536 -297
  160. package/dist/templates-DSbHJC4v.js.map +1 -0
  161. package/dist/types/auto-group.d.ts +79 -9
  162. package/dist/types/componentLabTypes.d.ts +161 -0
  163. package/dist/types/componentWorkflowTypes.d.ts +150 -0
  164. package/dist/types/components.d.ts +14 -311
  165. package/dist/types/form-builder.d.ts +3 -0
  166. package/dist/types/index.d.ts +1 -1
  167. package/dist/{useProtocolTemplates-QZtHFFH2.js → useProtocolTemplates-BbPOYPzO.js} +1220 -454
  168. package/dist/useProtocolTemplates-BbPOYPzO.js.map +1 -0
  169. package/package.json +1 -1
  170. package/src/__tests__/components/AppSidebar.test.ts +67 -0
  171. package/src/__tests__/components/AppTopBar.navigation.test.ts +70 -0
  172. package/src/__tests__/components/CollapsibleCard.test.ts +47 -0
  173. package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +53 -0
  174. package/src/__tests__/components/FormBuilder.test.ts +57 -0
  175. package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +54 -0
  176. package/src/__tests__/components/PluginWorkspaceView.controls.test.ts +156 -0
  177. package/src/__tests__/components/PluginWorkspaceView.navigation.test.ts +102 -0
  178. package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +43 -0
  179. package/src/__tests__/components/ProtocolStep.presentation.test.ts +31 -0
  180. package/src/__tests__/components/ProtocolStepEditor.state.test.ts +165 -0
  181. package/src/__tests__/components/ProtocolStepParameterField.test.ts +44 -0
  182. package/src/__tests__/components/ReagentList.presentation.test.ts +68 -0
  183. package/src/__tests__/components/SampleSelector.colors.test.ts +49 -0
  184. package/src/__tests__/components/SampleSelector.drag.test.ts +100 -0
  185. package/src/__tests__/components/SampleSelector.groups.test.ts +81 -0
  186. package/src/__tests__/components/SampleSelector.selection.test.ts +70 -0
  187. package/src/__tests__/components/SampleSelector.test.ts +32 -0
  188. package/src/__tests__/components/SampleSelectorSampleRow.test.ts +37 -0
  189. package/src/__tests__/components/ScheduleCalendar.test.ts +44 -0
  190. package/src/__tests__/components/SettingsModal.schema.test.ts +97 -0
  191. package/src/__tests__/components/WellPlate.colors.test.ts +28 -0
  192. package/src/__tests__/components/WellPlate.conditions.test.ts +68 -0
  193. package/src/__tests__/components/WellPlate.geometry.test.ts +54 -0
  194. package/src/__tests__/components/WellPlate.interaction.test.ts +171 -0
  195. package/src/__tests__/components/WellPlate.legend.test.ts +13 -0
  196. package/src/__tests__/components/WellPlate.rendering.test.ts +122 -0
  197. package/src/__tests__/components/WellPlate.sampleDrop.test.ts +70 -0
  198. package/src/__tests__/composables/autoGroup/classify.test.ts +107 -0
  199. package/src/__tests__/composables/autoGroup/columns.test.ts +135 -0
  200. package/src/__tests__/composables/autoGroup/compose.test.ts +227 -0
  201. package/src/__tests__/composables/autoGroup/cooccurrence.test.ts +91 -0
  202. package/src/__tests__/composables/autoGroup/fingerprint.test.ts +50 -0
  203. package/src/__tests__/composables/autoGroup/integration.test.ts +79 -0
  204. package/src/__tests__/composables/autoGroup/template.test.ts +70 -0
  205. package/src/__tests__/composables/autoGroup/tokenize.test.ts +33 -0
  206. package/src/__tests__/composables/useAutoGroup.test.ts +129 -625
  207. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +107 -0
  208. package/src/__tests__/composables/useControlSchema.test.ts +23 -0
  209. package/src/__tests__/composables/useScheduleCalendarLayout.test.ts +89 -0
  210. package/src/__tests__/docs/extractDocsComponents.test.ts +142 -0
  211. package/src/__tests__/docs/extractDocsExports.test.ts +77 -0
  212. package/src/__tests__/docs/extractDocsParsing.test.ts +69 -0
  213. package/src/__tests__/docs/extractDocsTemplates.test.ts +54 -0
  214. package/src/__tests__/docs/extractDocsTheme.test.ts +89 -0
  215. package/src/__tests__/docs/frontendDocsCatalog.test.ts +1 -1
  216. package/src/__tests__/fixtures/auto-group/mixed-lc-ms-batch.txt +187 -0
  217. package/src/components/AppSidebar.story.vue +79 -6
  218. package/src/components/AppSidebar.vue +74 -6
  219. package/src/components/AppTopBar.navigation.ts +62 -0
  220. package/src/components/AppTopBar.vue +17 -44
  221. package/src/components/AutoGroupModal.story.vue +50 -0
  222. package/src/components/AutoGroupModal.vue +441 -158
  223. package/src/components/BaseCheckbox.story.vue +27 -0
  224. package/src/components/BaseCheckbox.vue +63 -1
  225. package/src/components/BaseToggle.story.vue +27 -0
  226. package/src/components/BaseToggle.vue +66 -1
  227. package/src/components/CollapsibleCard.vue +123 -45
  228. package/src/components/ControlWorkspaceView.vue +2 -6
  229. package/src/components/DoseCalculator.vue +13 -73
  230. package/src/components/DoseCalculatorVolumeField.vue +61 -0
  231. package/src/components/ExperimentTimeline.vue +6 -31
  232. package/src/components/FormBuilder.story.vue +13 -0
  233. package/src/components/FormBuilder.vue +2 -7
  234. package/src/components/PlateMapEditor.vue +32 -106
  235. package/src/components/PluginWorkspaceView.controls.ts +182 -0
  236. package/src/components/PluginWorkspaceView.navigation.ts +106 -0
  237. package/src/components/PluginWorkspaceView.props.ts +174 -0
  238. package/src/components/PluginWorkspaceView.shell.ts +67 -0
  239. package/src/components/PluginWorkspaceView.vue +88 -404
  240. package/src/components/ProtocolStep.presentation.ts +31 -0
  241. package/src/components/ProtocolStepEditor.state.ts +104 -0
  242. package/src/components/ProtocolStepEditor.vue +48 -179
  243. package/src/components/ProtocolStepParameterField.vue +134 -0
  244. package/src/components/ReagentList.presentation.ts +105 -0
  245. package/src/components/ReagentList.vue +16 -79
  246. package/src/components/SampleSelector.colors.ts +43 -0
  247. package/src/components/SampleSelector.drag.ts +164 -0
  248. package/src/components/SampleSelector.groups.ts +109 -0
  249. package/src/components/SampleSelector.selection.ts +103 -0
  250. package/src/components/SampleSelector.vue +82 -349
  251. package/src/components/SampleSelectorSampleRow.vue +64 -0
  252. package/src/components/ScheduleCalendar.vue +44 -199
  253. package/src/components/SettingsModal.schema.ts +71 -0
  254. package/src/components/SettingsModal.vue +16 -46
  255. package/src/components/WellPlate.colors.ts +56 -0
  256. package/src/components/WellPlate.conditions.ts +100 -0
  257. package/src/components/WellPlate.geometry.ts +91 -0
  258. package/src/components/WellPlate.interaction.ts +272 -0
  259. package/src/components/WellPlate.legend.ts +8 -0
  260. package/src/components/WellPlate.rendering.ts +105 -0
  261. package/src/components/WellPlate.sampleDrop.ts +73 -0
  262. package/src/components/WellPlate.vue +102 -550
  263. package/src/components/internal/FormFieldRendererInternal.vue +23 -5
  264. package/src/components/internal/PlateMapEditorToolbarInternal.vue +128 -0
  265. package/src/composables/autoGroup/classKey.ts +5 -0
  266. package/src/composables/autoGroup/classify.ts +205 -0
  267. package/src/composables/autoGroup/colors.ts +6 -0
  268. package/src/composables/autoGroup/columns.ts +226 -0
  269. package/src/composables/autoGroup/compose.ts +156 -0
  270. package/src/composables/autoGroup/cooccurrence.ts +46 -0
  271. package/src/composables/autoGroup/csv-shim.ts +44 -0
  272. package/src/composables/autoGroup/fingerprint.ts +49 -0
  273. package/src/composables/autoGroup/index.ts +20 -0
  274. package/src/composables/autoGroup/replicatePreGroup.ts +90 -0
  275. package/src/composables/autoGroup/template.ts +126 -0
  276. package/src/composables/autoGroup/tokenize.ts +41 -0
  277. package/src/composables/autoGroup/vocab.json +67 -0
  278. package/src/composables/autoGroupConstants.ts +4 -0
  279. package/src/composables/autoGroupGrouping.ts +148 -0
  280. package/src/composables/controlComponentBindings.ts +1 -1
  281. package/src/composables/controlSchemaAdapters.ts +4 -1
  282. package/src/composables/controlSchemaDoseDesign.ts +215 -0
  283. package/src/composables/controlSchemaFormFields.ts +4 -1
  284. package/src/composables/controlSchemaLayout.ts +1 -1
  285. package/src/composables/controlSchemaModel.ts +163 -0
  286. package/src/composables/controlSchemaNormalize.ts +1 -1
  287. package/src/composables/controlSchemaTypes.ts +372 -0
  288. package/src/composables/controlWorkspaceOptions.ts +1 -1
  289. package/src/composables/formBuilderSchema.ts +153 -0
  290. package/src/composables/pluginEndpointBuilder.ts +203 -0
  291. package/src/composables/protocolTemplateCatalog.ts +325 -0
  292. package/src/composables/useAutoGroup.ts +395 -549
  293. package/src/composables/useAutoGroupInputSources.ts +147 -0
  294. package/src/composables/useBioTemplateControls.ts +1 -1
  295. package/src/composables/useBioTemplatePresetWorkspace.ts +1 -1
  296. package/src/composables/useBioTemplateWorkspace.ts +1 -1
  297. package/src/composables/useControlSchema.ts +21 -692
  298. package/src/composables/useControlWorkspace.ts +7 -13
  299. package/src/composables/useForm.ts +5 -187
  300. package/src/composables/useFormBuilder.ts +11 -153
  301. package/src/composables/useFormValidation.ts +154 -0
  302. package/src/composables/usePluginClient.ts +10 -193
  303. package/src/composables/useProtocolTemplates.ts +10 -328
  304. package/src/composables/useScheduleCalendarLayout.ts +287 -0
  305. package/src/styles/components/app-sidebar.css +134 -6
  306. package/src/styles/components/auto-group-modal.css +248 -310
  307. package/src/styles/components/checkbox.css +87 -0
  308. package/src/styles/components/collapsible-card.css +154 -14
  309. package/src/styles/components/toggle.css +80 -0
  310. package/src/templates/controlSchemaTypes.ts +1 -1
  311. package/src/templates/templateAdapterTypes.ts +58 -0
  312. package/src/templates/templateCreateOptions.ts +208 -0
  313. package/src/templates/templateQpcrTypes.ts +48 -0
  314. package/src/templates/types.ts +79 -275
  315. package/src/types/auto-group.ts +107 -9
  316. package/src/types/componentLabTypes.ts +235 -0
  317. package/src/types/componentWorkflowTypes.ts +190 -0
  318. package/src/types/components.ts +95 -424
  319. package/src/types/form-builder.ts +3 -0
  320. package/src/types/index.ts +2 -0
  321. package/dist/components-DafPc4rM.js.map +0 -1
  322. package/dist/composables-BMkPQhVK.js.map +0 -1
  323. package/dist/templates-bUAWMn5L.js.map +0 -1
  324. package/dist/useProtocolTemplates-QZtHFFH2.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morscherlab/mint-sdk",
3
- "version": "1.0.0-rc.4",
3
+ "version": "1.0.0-rc.6",
4
4
  "description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec INtegrated Toolkit.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -238,6 +238,46 @@ describe('AppSidebar', () => {
238
238
  expect(wrapper.findAllComponents(CollapsibleCard)).toHaveLength(0)
239
239
  })
240
240
 
241
+ it('renders a default collapsed rail from active sections', () => {
242
+ const wrapper = mount(AppSidebar, {
243
+ props: {
244
+ panels: {
245
+ analysis: [
246
+ {
247
+ id: 'filters',
248
+ label: 'Feature Filters',
249
+ icon: 'M3 4.5h18m-15 6h12m-9 6h6',
250
+ badge: 2,
251
+ badgeTone: 'warning',
252
+ },
253
+ ],
254
+ },
255
+ activeView: 'analysis',
256
+ collapsible: true,
257
+ collapsed: true,
258
+ },
259
+ })
260
+
261
+ expect(wrapper.find('.mint-sidebar__rail-item').exists()).toBe(true)
262
+ expect(wrapper.find('.mint-sidebar__rail-svg').exists()).toBe(true)
263
+ expect(wrapper.find('.mint-sidebar__rail-badge').text()).toBe('2')
264
+ expect(wrapper.find('.mint-sidebar__rail-badge').classes()).toContain('mint-sidebar__rail-badge--warning')
265
+ })
266
+
267
+ it('expands when a default collapsed rail item is clicked', async () => {
268
+ const wrapper = mount(AppSidebar, {
269
+ props: {
270
+ panels: samplePanels,
271
+ activeView: 'analysis',
272
+ collapsible: true,
273
+ defaultCollapsed: true,
274
+ },
275
+ })
276
+
277
+ await wrapper.get('.mint-sidebar__rail-item').trigger('click')
278
+ expect(wrapper.emitted('update:collapsed')).toEqual([[false]])
279
+ })
280
+
241
281
  it('renders an empty analysis shell for route-owned sidebar content', () => {
242
282
  const wrapper = mount(AppSidebar, {
243
283
  props: {
@@ -297,6 +337,33 @@ describe('AppSidebar', () => {
297
337
  expect(cards[0].props('icon')).toBe('⚙')
298
338
  })
299
339
 
340
+ it('passes section badges and actions to CollapsibleCard and re-emits actions', async () => {
341
+ const wrapper = mount(AppSidebar, {
342
+ props: {
343
+ panels: {
344
+ analysis: [
345
+ {
346
+ id: 'filters',
347
+ label: 'Feature Filters',
348
+ badge: 4,
349
+ badgeTone: 'cta',
350
+ actions: [{ id: 'reset', label: 'Reset filters' }],
351
+ },
352
+ ],
353
+ },
354
+ activeView: 'analysis',
355
+ },
356
+ global: { components: { CollapsibleCard } },
357
+ })
358
+
359
+ const card = wrapper.findComponent(CollapsibleCard)
360
+ expect(card.props('badge')).toBe(4)
361
+ expect(card.props('badgeTone')).toBe('cta')
362
+
363
+ await card.vm.$emit('action', 'reset')
364
+ expect(wrapper.emitted('section-action')).toEqual([['filters', 'reset']])
365
+ })
366
+
300
367
  it('should respect defaultOpen on sections', () => {
301
368
  const wrapper = mount(AppSidebar, {
302
369
  props: { panels: samplePanels, activeView: 'analysis' },
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ currentItemIdFromLocation,
4
+ currentPagePath,
5
+ normalizeNavPath,
6
+ pageIdFromPath,
7
+ pluginNavItemToPageSelectorItem,
8
+ } from '../../components/AppTopBar.navigation'
9
+
10
+ describe('AppTopBar navigation helpers', () => {
11
+ it('normalizes blank, relative, and trailing-slash paths', () => {
12
+ expect(normalizeNavPath()).toBe('/')
13
+ expect(normalizeNavPath('results')).toBe('/results')
14
+ expect(normalizeNavPath('/results/')).toBe('/results')
15
+ expect(normalizeNavPath(' /analysis/run/ ')).toBe('/analysis/run')
16
+ })
17
+
18
+ it('derives stable page ids from paths', () => {
19
+ expect(pageIdFromPath('/', 'fallback')).toBe('dashboard')
20
+ expect(pageIdFromPath('/analysis/run', 'fallback')).toBe('analysis-run')
21
+ expect(pageIdFromPath('', 'fallback')).toBe('dashboard')
22
+ })
23
+
24
+ it('maps plugin nav items to page selector items with plugin fallbacks', () => {
25
+ expect(
26
+ pluginNavItemToPageSelectorItem(
27
+ { label: 'Results', path: 'results' },
28
+ 1,
29
+ { pluginIcon: 'flask', pluginName: 'Assay Plugin' },
30
+ ),
31
+ ).toEqual({
32
+ id: 'results',
33
+ label: 'Results',
34
+ to: '/results',
35
+ icon: 'flask',
36
+ hint: 'Assay Plugin',
37
+ })
38
+ })
39
+
40
+ it('prefers explicit nav item id, icon, and description', () => {
41
+ expect(
42
+ pluginNavItemToPageSelectorItem(
43
+ {
44
+ id: 'custom',
45
+ label: 'Custom',
46
+ path: '/custom',
47
+ icon: 'sparkles',
48
+ description: 'Custom page',
49
+ },
50
+ 0,
51
+ { pluginIcon: 'flask', pluginName: 'Assay Plugin' },
52
+ ),
53
+ ).toMatchObject({
54
+ id: 'custom',
55
+ icon: 'sparkles',
56
+ hint: 'Custom page',
57
+ })
58
+ })
59
+
60
+ it('matches current route-relative page ids for integrated plugins', () => {
61
+ const pages = [
62
+ { id: 'overview', to: '/' },
63
+ { id: 'results', to: '/results' },
64
+ ]
65
+
66
+ expect(currentPagePath('/plugins/assay', '/plugins/assay')).toBe('/')
67
+ expect(currentPagePath('/plugins/assay/results', '/plugins/assay')).toBe('/results')
68
+ expect(currentItemIdFromLocation(pages, '/plugins/assay/results', '/plugins/assay')).toBe('results')
69
+ })
70
+ })
@@ -423,6 +423,53 @@ describe('CollapsibleCard', () => {
423
423
  })
424
424
  })
425
425
 
426
+ describe('badges and header actions', () => {
427
+ it('renders a compact badge in the header controls area', () => {
428
+ const wrapper = mount(CollapsibleCard, {
429
+ props: { title: 'Feature Filters', badge: 3, badgeTone: 'warning' },
430
+ })
431
+
432
+ const badge = wrapper.get('.mint-collapsible-card__badge')
433
+ expect(badge.text()).toBe('3')
434
+ expect(badge.classes()).toContain('mint-collapsible-card__badge--warning')
435
+ })
436
+
437
+ it('renders icon-only header actions and emits their ids', async () => {
438
+ const wrapper = mount(CollapsibleCard, {
439
+ props: {
440
+ title: 'Feature Filters',
441
+ actions: [
442
+ {
443
+ id: 'reset',
444
+ label: 'Reset filters',
445
+ icon: 'M16.023 9.348h4.992M2.985 19.644v-4.992',
446
+ tone: 'neutral',
447
+ },
448
+ ],
449
+ },
450
+ })
451
+
452
+ const action = wrapper.get('.mint-collapsible-card__action')
453
+ expect(action.attributes('aria-label')).toBe('Reset filters')
454
+ expect(wrapper.find('.mint-collapsible-card__action-icon').exists()).toBe(true)
455
+
456
+ await action.trigger('click')
457
+ expect(wrapper.emitted('action')).toEqual([['reset']])
458
+ })
459
+
460
+ it('does not emit disabled actions', async () => {
461
+ const wrapper = mount(CollapsibleCard, {
462
+ props: {
463
+ title: 'Feature Filters',
464
+ actions: [{ id: 'reset', label: 'Reset filters', disabled: true }],
465
+ },
466
+ })
467
+
468
+ await wrapper.get('.mint-collapsible-card__action').trigger('click')
469
+ expect(wrapper.emitted('action')).toBeUndefined()
470
+ })
471
+ })
472
+
426
473
  describe('content slot', () => {
427
474
  it('should render default slot content', () => {
428
475
  const wrapper = mount(CollapsibleCard, {
@@ -0,0 +1,53 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vitest'
3
+ import DoseCalculatorVolumeField from '../../components/DoseCalculatorVolumeField.vue'
4
+
5
+ describe('DoseCalculatorVolumeField', () => {
6
+ it('emits value updates while preserving the current unit', async () => {
7
+ const wrapper = mount(DoseCalculatorVolumeField, {
8
+ props: {
9
+ label: 'Final Volume',
10
+ modelValue: { value: 1, unit: 'mL' },
11
+ units: ['µL', 'mL', 'L'],
12
+ },
13
+ })
14
+
15
+ await wrapper.get('input').setValue('2.5')
16
+
17
+ expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([
18
+ { value: 2.5, unit: 'mL' },
19
+ ])
20
+ })
21
+
22
+ it('emits unit updates while preserving the current value', async () => {
23
+ const wrapper = mount(DoseCalculatorVolumeField, {
24
+ props: {
25
+ label: 'Volume per Well',
26
+ modelValue: { value: 100, unit: 'µL' },
27
+ units: ['µL', 'mL', 'L'],
28
+ },
29
+ })
30
+
31
+ await wrapper.get('select').setValue('mL')
32
+
33
+ expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([
34
+ { value: 100, unit: 'mL' },
35
+ ])
36
+ })
37
+
38
+ it('ignores non-numeric value input', async () => {
39
+ const wrapper = mount(DoseCalculatorVolumeField, {
40
+ props: {
41
+ label: 'Final Volume',
42
+ modelValue: { value: 1, unit: 'mL' },
43
+ units: ['µL', 'mL', 'L'],
44
+ },
45
+ })
46
+
47
+ const input = wrapper.get('input')
48
+ ;(input.element as HTMLInputElement).value = ''
49
+ await input.trigger('input')
50
+
51
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
52
+ })
53
+ })
@@ -3,6 +3,7 @@ import { nextTick } from 'vue'
3
3
  import { describe, expect, it } from 'vitest'
4
4
  import FormBuilder from '../../components/FormBuilder.vue'
5
5
  import FormActions from '../../components/FormActions.vue'
6
+ import Tooltip from '../../components/Tooltip.vue'
6
7
  import { defineControlModel, useControlWorkspace, type ControlSchema } from '../../composables/useControlSchema'
7
8
  import type { FormSchema } from '../../types/form-builder'
8
9
 
@@ -286,6 +287,62 @@ describe('FormBuilder', () => {
286
287
  expect(wrapper.find('.mint-form-actions').exists()).toBe(false)
287
288
  })
288
289
 
290
+ it('renders boolean fields as compact choice rows', async () => {
291
+ const schema: FormSchema = {
292
+ sections: [
293
+ {
294
+ id: 'display',
295
+ title: 'Display',
296
+ fields: [
297
+ {
298
+ name: 'showOutliers',
299
+ label: 'Show outliers',
300
+ type: 'toggle',
301
+ defaultValue: true,
302
+ hint: 'Keep flagged peaks visible',
303
+ icon: ['M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'],
304
+ },
305
+ {
306
+ name: 'includeMetadata',
307
+ label: 'Include metadata',
308
+ type: 'checkbox',
309
+ defaultValue: false,
310
+ hint: 'Add method context',
311
+ icon: ['M12 3c4.418 0 8 1.343 8 3s-3.582 3-8 3-8-1.343-8-3z'],
312
+ },
313
+ ],
314
+ },
315
+ ],
316
+ }
317
+
318
+ const wrapper = mount(FormBuilder, {
319
+ props: { schema, showActions: false },
320
+ })
321
+
322
+ const toggle = wrapper.get('.mint-toggle--row')
323
+ const checkbox = wrapper.get('.mint-checkbox--row')
324
+
325
+ expect(toggle.text()).toContain('Show outliers')
326
+ expect(toggle.text()).not.toContain('Keep flagged peaks visible')
327
+ expect(toggle.find('.mint-toggle__row-svg').exists()).toBe(true)
328
+ expect(wrapper.findAll('.mint-form-field__label')).toHaveLength(0)
329
+
330
+ expect(checkbox.text()).toContain('Include metadata')
331
+ expect(checkbox.text()).not.toContain('Add method context')
332
+ expect(checkbox.find('.mint-checkbox__row-svg').exists()).toBe(true)
333
+ expect(wrapper.findAllComponents(Tooltip).map(tooltip => tooltip.props('text'))).toEqual([
334
+ 'Keep flagged peaks visible',
335
+ 'Add method context',
336
+ ])
337
+
338
+ await toggle.trigger('click')
339
+
340
+ expect(wrapper.emitted('update:modelValue')?.at(-1)?.[0]).toMatchObject({
341
+ showOutliers: false,
342
+ includeMetadata: false,
343
+ })
344
+ })
345
+
289
346
  it('renders default actions by default', () => {
290
347
  const wrapper = mount(FormBuilder, {
291
348
  props: { controls },
@@ -0,0 +1,54 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+ import PlateMapEditorToolbarInternal from '../../components/internal/PlateMapEditorToolbarInternal.vue'
4
+ import type { PlateMap } from '../../types'
5
+
6
+ const plates: PlateMap[] = [
7
+ { id: 'plate-1', name: 'Plate 1', format: 96, wells: {} },
8
+ { id: 'plate-2', name: 'Plate 2', format: 96, wells: {} },
9
+ ]
10
+
11
+ function mountToolbar(props = {}) {
12
+ return mount(PlateMapEditorToolbarInternal, {
13
+ props: {
14
+ plates,
15
+ activePlateId: 'plate-1',
16
+ plateWellCounts: { 'plate-1': 3, 'plate-2': 0 },
17
+ canAddPlate: true,
18
+ canUndo: false,
19
+ canRedo: true,
20
+ slotColorForPlate: vi.fn((_plateId: string, index: number) => (index === 0 ? '#ef4444' : '#22c55e')),
21
+ ...props,
22
+ },
23
+ })
24
+ }
25
+
26
+ describe('PlateMapEditorToolbarInternal', () => {
27
+ it('renders plate tabs, active state, and well counts', () => {
28
+ const wrapper = mountToolbar()
29
+
30
+ const tabs = wrapper.findAll('.mint-plate-editor__tab')
31
+ expect(tabs).toHaveLength(2)
32
+ expect(tabs[0].classes()).toContain('mint-plate-editor__tab--active')
33
+ expect(wrapper.find('.mint-plate-editor__tab-count').text()).toBe('3')
34
+ expect(wrapper.find('.mint-plate-editor__add-plate').exists()).toBe(true)
35
+ })
36
+
37
+ it('emits tab and action events', async () => {
38
+ const wrapper = mountToolbar()
39
+
40
+ await wrapper.findAll('.mint-plate-editor__tab')[1].trigger('click')
41
+ await wrapper.findAll('.mint-plate-editor__tab-remove')[0].trigger('click')
42
+ await wrapper.find('.mint-plate-editor__add-plate').trigger('click')
43
+ await wrapper.find('[title="Redo (Ctrl+Shift+Z)"]').trigger('click')
44
+ await wrapper.find('[title="Import"]').trigger('click')
45
+ await wrapper.find('[title="Export JSON"]').trigger('click')
46
+
47
+ expect(wrapper.emitted('select-plate')?.[0]).toEqual(['plate-2'])
48
+ expect(wrapper.emitted('remove-plate')?.[0]).toEqual(['plate-1'])
49
+ expect(wrapper.emitted('add-plate')).toHaveLength(1)
50
+ expect(wrapper.emitted('redo')).toHaveLength(1)
51
+ expect(wrapper.emitted('import')).toHaveLength(1)
52
+ expect(wrapper.emitted('export-json')).toHaveLength(1)
53
+ })
54
+ })
@@ -0,0 +1,156 @@
1
+ import { nextTick, ref } from 'vue'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+ import { defineDoseDesignControlModel } from '../../composables/useControlSchema'
4
+ import { usePluginWorkspaceControls } from '../../components/PluginWorkspaceView.controls'
5
+ import type {
6
+ ControlComponentBindingsConfig,
7
+ ControlComponentPropsByIdMap,
8
+ ControlComponentPropsMap,
9
+ ControlSchema,
10
+ } from '../../composables/useControlSchema'
11
+
12
+ interface CreateControlsOptions {
13
+ controls?: ControlSchema
14
+ modelValue?: () => Record<string, unknown> | undefined
15
+ values?: () => Record<string, unknown> | undefined
16
+ componentProps?: () => ControlComponentPropsMap | undefined
17
+ componentPropsById?: () => ControlComponentPropsByIdMap | undefined
18
+ componentBindings?: () => ControlComponentBindingsConfig | undefined
19
+ emitValues?: (values: Record<string, unknown>) => void
20
+ }
21
+
22
+ function createControls({
23
+ controls,
24
+ modelValue,
25
+ values,
26
+ componentProps,
27
+ componentPropsById,
28
+ componentBindings,
29
+ emitValues = vi.fn(),
30
+ }: CreateControlsOptions = {}) {
31
+ return usePluginWorkspaceControls({
32
+ model: () => undefined,
33
+ controls: () => controls,
34
+ controlOptions: () => ({}),
35
+ modelValue: modelValue ?? (() => undefined),
36
+ values: values ?? (() => undefined),
37
+ componentBindings: componentBindings ?? (() => undefined),
38
+ componentProps: componentProps ?? (() => undefined),
39
+ componentPropsById: componentPropsById ?? (() => undefined),
40
+ emitValues,
41
+ })
42
+ }
43
+
44
+ describe('PluginWorkspaceView control state helpers', () => {
45
+ it('merges control defaults with external values and resyncs when values change', async () => {
46
+ const externalValues = ref<Record<string, unknown> | undefined>({ threshold: 0.2 })
47
+ const state = createControls({
48
+ controls: {
49
+ threshold: { label: 'Threshold', default: 0.05 },
50
+ mode: { label: 'Mode', default: 'fast' },
51
+ },
52
+ modelValue: () => externalValues.value,
53
+ })
54
+
55
+ expect(state.internalControlValues).toEqual({
56
+ threshold: 0.2,
57
+ mode: 'fast',
58
+ })
59
+
60
+ externalValues.value = { threshold: 0.8 }
61
+ await nextTick()
62
+
63
+ expect(state.internalControlValues).toEqual({
64
+ threshold: 0.8,
65
+ mode: 'fast',
66
+ })
67
+ })
68
+
69
+ it('emits updates from sidebar form values without dropping generated defaults', () => {
70
+ const emitValues = vi.fn()
71
+ const state = createControls({
72
+ controls: {
73
+ threshold: { label: 'Threshold', default: 0.05 },
74
+ mode: { label: 'Mode', default: 'fast' },
75
+ },
76
+ emitValues,
77
+ })
78
+
79
+ state.handleControlValuesUpdate({ threshold: 0.4 })
80
+
81
+ expect(state.internalControlValues).toEqual({
82
+ threshold: 0.4,
83
+ mode: 'fast',
84
+ })
85
+ expect(emitValues).toHaveBeenCalledWith({
86
+ threshold: 0.4,
87
+ mode: 'fast',
88
+ })
89
+ })
90
+
91
+ it('derives component props and bindings from one internal value model', () => {
92
+ const state = createControls({
93
+ controls: {
94
+ threshold: { label: 'Threshold', default: 0.05 },
95
+ mode: { label: 'Mode', default: 'fast' },
96
+ },
97
+ componentProps: () => ['threshold'],
98
+ componentPropsById: () => ({
99
+ chart: ['threshold', 'mode'],
100
+ }),
101
+ componentBindings: () => ({
102
+ chart: {
103
+ component: 'ChartContainer',
104
+ props: ['threshold'],
105
+ },
106
+ }),
107
+ })
108
+
109
+ expect(state.resolvedComponentProps.value).toEqual({ threshold: 0.05 })
110
+ expect(state.resolvedComponentPropsById.value).toEqual({
111
+ chart: { threshold: 0.05, mode: 'fast' },
112
+ })
113
+ expect(state.resolvedComponentBindingsById.value.chart).toMatchObject({
114
+ component: 'ChartContainer',
115
+ props: { threshold: 0.05 },
116
+ })
117
+ })
118
+
119
+ it('uses compact control model navigation and binding defaults', () => {
120
+ const model = defineDoseDesignControlModel({
121
+ selectedWells: ['A1', 'A2'],
122
+ includeMolecularWeight: true,
123
+ })
124
+ const state = usePluginWorkspaceControls({
125
+ model: () => model,
126
+ controls: () => undefined,
127
+ controlOptions: () => ({}),
128
+ modelValue: () => ({
129
+ selectedWells: ['C1'],
130
+ plateFormat: 384,
131
+ doseMode: 'dilution',
132
+ disabled: true,
133
+ molecularWeight: 450,
134
+ }),
135
+ values: () => undefined,
136
+ componentBindings: () => undefined,
137
+ componentProps: () => undefined,
138
+ componentPropsById: () => undefined,
139
+ emitValues: vi.fn(),
140
+ })
141
+
142
+ expect(state.generatedPillNav.value.map(item => item.id)).toEqual(['design'])
143
+ expect(state.generatedDefaultView.value).toBe('design')
144
+ expect(state.resolvedComponentBindingsById.value.plate.props).toMatchObject({
145
+ modelValue: ['C1'],
146
+ format: 384,
147
+ disabled: true,
148
+ })
149
+ expect(state.resolvedComponentPropsById.value.dose).toMatchObject({
150
+ mode: 'dilution',
151
+ targetWells: ['C1'],
152
+ disabled: true,
153
+ molecularWeight: 450,
154
+ })
155
+ })
156
+ })
@@ -0,0 +1,102 @@
1
+ import { ref } from 'vue'
2
+ import { describe, expect, it } from 'vitest'
3
+ import type {
4
+ PageSelectorItem,
5
+ PageSelectorItemInput,
6
+ PillNavItem,
7
+ PillNavItemInput,
8
+ } from '../../types'
9
+ import { usePluginWorkspaceNavigation } from '../../components/PluginWorkspaceView.navigation'
10
+
11
+ function makeNavigation(overrides: {
12
+ activeView?: string
13
+ defaultActiveView?: string
14
+ currentPillId?: string
15
+ currentPageSelectorId?: string
16
+ pillNav?: PillNavItemInput[]
17
+ pageSelector?: PageSelectorItemInput[]
18
+ panelIds?: string[]
19
+ generatedPillNav?: PillNavItem[]
20
+ generatedDefaultView?: string
21
+ } = {}) {
22
+ const activeView = ref(overrides.activeView)
23
+ const defaultActiveView = ref(overrides.defaultActiveView)
24
+ const currentPillId = ref(overrides.currentPillId)
25
+ const currentPageSelectorId = ref(overrides.currentPageSelectorId)
26
+ const pillNav = ref(overrides.pillNav)
27
+ const pageSelector = ref(overrides.pageSelector)
28
+ const panelIds = ref(overrides.panelIds ?? [])
29
+ const generatedPillNav = ref<PillNavItem[]>(overrides.generatedPillNav ?? [])
30
+ const generatedDefaultView = ref(overrides.generatedDefaultView ?? '')
31
+ const emitted = {
32
+ activeView: [] as string[],
33
+ page: [] as PageSelectorItem[],
34
+ pill: [] as PillNavItem[],
35
+ }
36
+
37
+ const navigation = usePluginWorkspaceNavigation({
38
+ activeView: () => activeView.value,
39
+ defaultActiveView: () => defaultActiveView.value,
40
+ currentPillId: () => currentPillId.value,
41
+ currentPageSelectorId: () => currentPageSelectorId.value,
42
+ pillNav: () => pillNav.value,
43
+ pageSelector: () => pageSelector.value,
44
+ panelIds: () => panelIds.value,
45
+ generatedPillNav,
46
+ generatedDefaultView,
47
+ emitActiveView: viewId => emitted.activeView.push(viewId),
48
+ emitPageSelectorSelect: page => emitted.page.push(page),
49
+ emitPillSelect: item => emitted.pill.push(item),
50
+ })
51
+
52
+ return { navigation, emitted, activeView, pillNav, generatedPillNav }
53
+ }
54
+
55
+ describe('usePluginWorkspaceNavigation', () => {
56
+ it('derives the initial view from explicit pill nav and syncs pill selection', () => {
57
+ const data = { id: 'data', label: 'Data' }
58
+ const results = { id: 'results', label: 'Results' }
59
+ const { navigation, emitted } = makeNavigation({
60
+ pillNav: [data, results],
61
+ panelIds: ['fallback'],
62
+ })
63
+
64
+ expect(navigation.resolvedPillNav.value).toEqual([data, results])
65
+ expect(navigation.resolvedActiveView.value).toBe('data')
66
+
67
+ navigation.handlePillSelect(results)
68
+
69
+ expect(navigation.resolvedActiveView.value).toBe('results')
70
+ expect(emitted.activeView).toEqual(['results'])
71
+ expect(emitted.pill).toEqual([results])
72
+ })
73
+
74
+ it('emits page selector changes while keeping controlled active view stable', () => {
75
+ const overview = { id: 'overview', label: 'Overview' }
76
+ const { navigation, emitted } = makeNavigation({
77
+ activeView: 'controlled',
78
+ pageSelector: [overview],
79
+ generatedDefaultView: 'generated',
80
+ })
81
+
82
+ navigation.handlePageSelectorSelect(overview)
83
+
84
+ expect(navigation.resolvedActiveView.value).toBe('controlled')
85
+ expect(navigation.resolvedCurrentPageSelectorId.value).toBe('controlled')
86
+ expect(emitted.activeView).toEqual(['overview'])
87
+ expect(emitted.page).toEqual([overview])
88
+ })
89
+
90
+ it('uses generated navigation when explicit pill nav is absent', () => {
91
+ const generated = { id: 'design', label: 'Design' }
92
+ const { navigation } = makeNavigation({
93
+ generatedPillNav: [generated],
94
+ generatedDefaultView: 'design',
95
+ pageSelector: ['page-fallback'],
96
+ })
97
+
98
+ expect(navigation.resolvedPillNav.value).toEqual([generated])
99
+ expect(navigation.firstConfiguredViewId.value).toBe('design')
100
+ expect(navigation.resolvedCurrentPillId.value).toBe('design')
101
+ })
102
+ })