@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
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ getPluginWorkspaceSidebarSlotNames,
4
+ getPluginWorkspaceTopBarSlotNames,
5
+ hasPluginWorkspaceSidebarSurface,
6
+ } from '../../components/PluginWorkspaceView.shell'
7
+
8
+ describe('PluginWorkspaceView shell helpers', () => {
9
+ it('selects forwarded topbar and sidebar slots by key without depending on Vue slot return types', () => {
10
+ const slots: Readonly<Record<string, unknown>> = {
11
+ default: undefined,
12
+ actions: undefined,
13
+ 'settings-tab-analysis': undefined,
14
+ header: undefined,
15
+ 'collapsed-footer': undefined,
16
+ 'section-import': undefined,
17
+ unrelated: undefined,
18
+ }
19
+
20
+ expect(getPluginWorkspaceTopBarSlotNames(slots)).toEqual([
21
+ 'actions',
22
+ 'settings-tab-analysis',
23
+ ])
24
+ expect(getPluginWorkspaceSidebarSlotNames(slots)).toEqual([
25
+ 'header',
26
+ 'collapsed-footer',
27
+ 'section-import',
28
+ ])
29
+ })
30
+
31
+ it('keeps the sidebar surface hidden when all sidebar inputs are empty', () => {
32
+ expect(hasPluginWorkspaceSidebarSurface({
33
+ showSidebar: true,
34
+ panels: {},
35
+ forms: {},
36
+ model: undefined,
37
+ controls: undefined,
38
+ showSidebarWhenEmpty: false,
39
+ hasSidebarSlot: false,
40
+ sidebarSlotCount: 0,
41
+ })).toBe(false)
42
+ })
43
+ })
@@ -0,0 +1,31 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import {
4
+ PROTOCOL_STEP_TYPE_COLORS,
5
+ PROTOCOL_STEP_TYPE_ICONS,
6
+ formatProtocolDuration,
7
+ } from '../../components/ProtocolStep.presentation'
8
+
9
+ describe('ProtocolStep presentation helpers', () => {
10
+ it('formats protocol durations for editor and timeline displays', () => {
11
+ expect(formatProtocolDuration(undefined)).toBe('')
12
+ expect(formatProtocolDuration(15)).toBe('15m')
13
+ expect(formatProtocolDuration(60)).toBe('1h')
14
+ expect(formatProtocolDuration(95)).toBe('1h 35m')
15
+ })
16
+
17
+ it('keeps icons and colors available for every protocol step type', () => {
18
+ expect(Object.keys(PROTOCOL_STEP_TYPE_ICONS).sort()).toEqual([
19
+ 'addition',
20
+ 'centrifuge',
21
+ 'custom',
22
+ 'incubation',
23
+ 'measurement',
24
+ 'mix',
25
+ 'transfer',
26
+ 'wash',
27
+ ])
28
+ expect(PROTOCOL_STEP_TYPE_COLORS.measurement).toBe('#8B5CF6')
29
+ expect(PROTOCOL_STEP_TYPE_ICONS.custom).toContain('M10.325')
30
+ })
31
+ })
@@ -0,0 +1,165 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { ProtocolStep } from '../../types'
3
+ import type { StepTemplate } from '../../composables/useProtocolTemplates'
4
+ import {
5
+ buildCustomProtocolTemplate,
6
+ buildProtocolPreviewParams,
7
+ buildProtocolStepEditorState,
8
+ buildProtocolStepPreview,
9
+ buildProtocolTemplateDefaults,
10
+ } from '../../components/ProtocolStepEditor.state'
11
+
12
+ const template: StepTemplate = {
13
+ id: 'builtin-incubation',
14
+ type: 'incubation',
15
+ name: 'Incubation',
16
+ description: 'Hold samples under controlled conditions',
17
+ defaultDuration: 60,
18
+ parameters: [
19
+ {
20
+ key: 'temperature',
21
+ label: 'Temperature',
22
+ type: 'temperature',
23
+ unit: 'C',
24
+ default: 37,
25
+ required: true,
26
+ },
27
+ {
28
+ key: 'notes',
29
+ label: 'Notes',
30
+ type: 'text',
31
+ },
32
+ ],
33
+ isBuiltIn: true,
34
+ }
35
+
36
+ describe('ProtocolStepEditor state helpers', () => {
37
+ it('builds editor defaults from template parameter defaults', () => {
38
+ expect(buildProtocolTemplateDefaults(template)).toEqual({
39
+ name: 'Incubation',
40
+ description: 'Hold samples under controlled conditions',
41
+ duration: 60,
42
+ parameters: {
43
+ temperature: 37,
44
+ },
45
+ })
46
+ })
47
+
48
+ it('initializes editor fields from an existing step', () => {
49
+ const step: ProtocolStep = {
50
+ id: 'step-1',
51
+ type: 'incubation',
52
+ name: 'Overnight incubation',
53
+ description: 'Long run',
54
+ duration: 720,
55
+ status: 'in_progress',
56
+ parameters: {
57
+ temperature: 4,
58
+ },
59
+ order: 3,
60
+ }
61
+
62
+ const state = buildProtocolStepEditorState(step, (type) => (
63
+ type === 'incubation' ? template : undefined
64
+ ))
65
+
66
+ expect(state).toEqual({
67
+ selectedTemplateId: 'builtin-incubation',
68
+ fields: {
69
+ name: 'Overnight incubation',
70
+ description: 'Long run',
71
+ duration: 720,
72
+ parameters: {
73
+ temperature: 4,
74
+ },
75
+ },
76
+ })
77
+ })
78
+
79
+ it('preserves source step metadata when building a preview', () => {
80
+ const preview = buildProtocolStepPreview(
81
+ template,
82
+ {
83
+ name: '',
84
+ description: 'Preview description',
85
+ parameters: {
86
+ temperature: 25,
87
+ },
88
+ },
89
+ {
90
+ id: 'step-2',
91
+ type: 'incubation',
92
+ name: 'Source',
93
+ status: 'completed',
94
+ order: 5,
95
+ }
96
+ )
97
+
98
+ expect(preview).toEqual({
99
+ id: 'step-2',
100
+ type: 'incubation',
101
+ name: 'Incubation',
102
+ description: 'Preview description',
103
+ duration: 60,
104
+ status: 'completed',
105
+ parameters: {
106
+ temperature: 25,
107
+ },
108
+ order: 5,
109
+ })
110
+ })
111
+
112
+ it('formats only populated preview parameters', () => {
113
+ const formatted = buildProtocolPreviewParams(
114
+ template,
115
+ {
116
+ temperature: 37,
117
+ notes: '',
118
+ },
119
+ (value, parameter) => `${parameter.label}: ${value}`
120
+ )
121
+
122
+ expect(formatted).toBe('Temperature: 37')
123
+ })
124
+
125
+ it('creates a custom template from the current editor fields', () => {
126
+ expect(
127
+ buildCustomProtocolTemplate(
128
+ template,
129
+ {
130
+ name: 'Cold incubation',
131
+ description: 'Cold room workflow',
132
+ duration: 120,
133
+ parameters: {
134
+ temperature: 4,
135
+ notes: 'keep covered',
136
+ },
137
+ },
138
+ 'custom-fixed'
139
+ )
140
+ ).toEqual({
141
+ id: 'custom-fixed',
142
+ type: 'incubation',
143
+ name: 'Cold incubation',
144
+ description: 'Cold room workflow',
145
+ defaultDuration: 120,
146
+ parameters: [
147
+ {
148
+ key: 'temperature',
149
+ label: 'Temperature',
150
+ type: 'temperature',
151
+ unit: 'C',
152
+ default: 4,
153
+ required: true,
154
+ },
155
+ {
156
+ key: 'notes',
157
+ label: 'Notes',
158
+ type: 'text',
159
+ default: 'keep covered',
160
+ },
161
+ ],
162
+ isBuiltIn: false,
163
+ })
164
+ })
165
+ })
@@ -0,0 +1,44 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vitest'
3
+ import ProtocolStepParameterField from '../../components/ProtocolStepParameterField.vue'
4
+ import type { ParameterDefinition } from '../../composables/useProtocolTemplates'
5
+
6
+ describe('ProtocolStepParameterField', () => {
7
+ it('emits numeric parameter changes with valueAsNumber', async () => {
8
+ const parameter: ParameterDefinition = {
9
+ key: 'temperature',
10
+ label: 'Temperature',
11
+ type: 'temperature',
12
+ unit: 'C',
13
+ }
14
+
15
+ const wrapper = mount(ProtocolStepParameterField, {
16
+ props: {
17
+ parameter,
18
+ modelValue: 37,
19
+ },
20
+ })
21
+
22
+ await wrapper.get('input').setValue('25')
23
+ expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([25])
24
+ })
25
+
26
+ it('renders field validation errors', () => {
27
+ const parameter: ParameterDefinition = {
28
+ key: 'buffer',
29
+ label: 'Buffer',
30
+ type: 'select',
31
+ options: [{ value: 'PBS', label: 'PBS' }],
32
+ }
33
+
34
+ const wrapper = mount(ProtocolStepParameterField, {
35
+ props: {
36
+ parameter,
37
+ error: 'Buffer is required',
38
+ },
39
+ })
40
+
41
+ expect(wrapper.text()).toContain('Buffer is required')
42
+ expect(wrapper.get('select').classes()).toContain('mint-protocol-editor__input--error')
43
+ })
44
+ })
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { Reagent } from '../../types'
3
+ import {
4
+ formatReagentExpiryDate,
5
+ getReagentColumnValue,
6
+ getReagentRowClasses,
7
+ getReagentStockFillClass,
8
+ getReagentStockLevel,
9
+ isReagentExpired,
10
+ isReagentExpiringSoon,
11
+ isReagentLowStock,
12
+ } from '../../components/ReagentList.presentation'
13
+
14
+ describe('ReagentList presentation helpers', () => {
15
+ const reagent: Reagent = {
16
+ id: 'reagent-1',
17
+ name: 'Glucose',
18
+ catalogNumber: 'G-7528',
19
+ lotNumber: 'LOT-A',
20
+ supplier: 'Sigma',
21
+ expiryDate: '2026-06-01T00:00:00.000Z',
22
+ stockLevel: 12,
23
+ }
24
+
25
+ it('extracts sortable values for table columns', () => {
26
+ expect(getReagentColumnValue(reagent, 'name')).toBe('Glucose')
27
+ expect(getReagentColumnValue(reagent, 'catalog')).toBe('G-7528')
28
+ expect(getReagentColumnValue(reagent, 'expiry')).toBe(new Date(reagent.expiryDate!).getTime())
29
+ expect(getReagentColumnValue({ id: 'empty', name: 'Empty' }, 'stock')).toBeUndefined()
30
+ })
31
+
32
+ it('classifies expiry states relative to a supplied date', () => {
33
+ const now = new Date('2026-05-22T00:00:00.000Z')
34
+
35
+ expect(isReagentExpired(reagent, now)).toBe(false)
36
+ expect(isReagentExpiringSoon(reagent, 30, now)).toBe(true)
37
+ expect(isReagentExpired({ ...reagent, expiryDate: '2026-05-01T00:00:00.000Z' }, now)).toBe(true)
38
+ })
39
+
40
+ it('classifies low stock and stock fill tone', () => {
41
+ expect(isReagentLowStock(reagent, 15)).toBe(true)
42
+ expect(getReagentStockLevel({ id: 'missing', name: 'Missing' })).toBe(0)
43
+ expect(getReagentStockFillClass(70)).toBe('mint-reagent-list__stock-fill--good')
44
+ expect(getReagentStockFillClass(30)).toBe('mint-reagent-list__stock-fill--warning')
45
+ expect(getReagentStockFillClass(10)).toBe('mint-reagent-list__stock-fill--low')
46
+ })
47
+
48
+ it('formats missing and present expiry dates', () => {
49
+ expect(formatReagentExpiryDate(undefined)).toBe('-')
50
+ expect(formatReagentExpiryDate('2026-06-01T00:00:00.000Z')).toBe('Jun 2026')
51
+ })
52
+
53
+ it('builds row state classes without mixing expired and expiring-soon', () => {
54
+ expect(
55
+ getReagentRowClasses(reagent, {
56
+ lowStockThreshold: 15,
57
+ draggedId: 'reagent-1',
58
+ dragOverId: 'reagent-2',
59
+ now: new Date('2026-05-22T00:00:00.000Z'),
60
+ })
61
+ ).toEqual([
62
+ 'mint-reagent-list__row',
63
+ 'mint-reagent-list__row--expiring-soon',
64
+ 'mint-reagent-list__row--low-stock',
65
+ 'mint-reagent-list__row--dragging',
66
+ ])
67
+ })
68
+ })
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { SampleGroup } from '../../types'
3
+ import {
4
+ DEFAULT_COLOR_PICKER_SEED,
5
+ applySampleGroupColorEdit,
6
+ createSampleGroup,
7
+ getSampleGroupColorEditSeed,
8
+ } from '../../components/SampleSelector.colors'
9
+
10
+ const groups: SampleGroup[] = [
11
+ { name: 'Control/A', color: '#3B82F6', samples: ['S1'] },
12
+ { name: 'Control/B', color: '#06B6D4', samples: ['S2'] },
13
+ ]
14
+
15
+ describe('SampleSelector color helpers', () => {
16
+ it('applies a single group color edit without mutating other groups', () => {
17
+ const updated = applySampleGroupColorEdit(
18
+ groups,
19
+ { kind: 'single', name: 'Control/B' },
20
+ '#111111',
21
+ )
22
+
23
+ expect(updated.map(group => group.color)).toEqual(['#3B82F6', '#111111'])
24
+ expect(groups.map(group => group.color)).toEqual(['#3B82F6', '#06B6D4'])
25
+ })
26
+
27
+ it('applies a family color edit to every named subgroup', () => {
28
+ const updated = applySampleGroupColorEdit(
29
+ groups,
30
+ { kind: 'family', names: ['Control/A', 'Control/B'] },
31
+ '#222222',
32
+ )
33
+
34
+ expect(updated.map(group => group.color)).toEqual(['#222222', '#222222'])
35
+ })
36
+
37
+ it('returns the default color seed when no color edit is active', () => {
38
+ expect(getSampleGroupColorEditSeed(null, name => name)).toBe(DEFAULT_COLOR_PICKER_SEED)
39
+ })
40
+
41
+ it('creates trimmed empty sample groups with the next unused default color', () => {
42
+ expect(createSampleGroup(' Treatment ', groups)).toEqual({
43
+ name: 'Treatment',
44
+ color: '#10B981',
45
+ samples: [],
46
+ })
47
+ expect(createSampleGroup(' ', groups)).toBeNull()
48
+ })
49
+ })
@@ -0,0 +1,100 @@
1
+ import { ref } from 'vue'
2
+ import { describe, expect, it } from 'vitest'
3
+ import type { SampleGroup } from '../../types'
4
+ import { useSampleSelectorDrag } from '../../components/SampleSelector.drag'
5
+
6
+ type TestDataTransfer = DataTransfer & { data: Record<string, string> }
7
+ type TestDragEvent = Omit<DragEvent, 'dataTransfer'> & {
8
+ dataTransfer: TestDataTransfer
9
+ defaultPrevented: boolean
10
+ }
11
+
12
+ function createDragEvent(options: { clientY?: number; top?: number; height?: number } = {}) {
13
+ const data: Record<string, string> = {}
14
+ const transfer = {
15
+ effectAllowed: '',
16
+ dropEffect: '',
17
+ data,
18
+ setData(type: string, value: string) {
19
+ data[type] = value
20
+ },
21
+ } as unknown as TestDataTransfer
22
+ let prevented = false
23
+ const target = {
24
+ getBoundingClientRect: () => ({
25
+ top: options.top ?? 0,
26
+ height: options.height ?? 20,
27
+ }),
28
+ contains: () => false,
29
+ }
30
+
31
+ return {
32
+ dataTransfer: transfer,
33
+ preventDefault: () => {
34
+ prevented = true
35
+ },
36
+ get defaultPrevented() {
37
+ return prevented
38
+ },
39
+ clientY: options.clientY ?? 0,
40
+ currentTarget: target,
41
+ relatedTarget: null,
42
+ } as unknown as TestDragEvent
43
+ }
44
+
45
+ const groups: SampleGroup[] = [
46
+ { name: 'Treatment/A', color: '#111111', samples: ['S1'] },
47
+ { name: 'Treatment/B', color: '#222222', samples: ['S2'] },
48
+ { name: 'Control/A', color: '#333333', samples: ['S3'] },
49
+ { name: 'Control/B', color: '#444444', samples: ['S4'] },
50
+ ]
51
+
52
+ describe('SampleSelector drag helpers', () => {
53
+ it('moves dragged samples between groups and resets sample drag state', () => {
54
+ const state = ref<SampleGroup[]>([
55
+ { name: 'A', color: '#111111', samples: ['S1'] },
56
+ { name: 'B', color: '#222222', samples: ['S2'] },
57
+ ])
58
+ const drag = useSampleSelectorDrag(state)
59
+ const startEvent = createDragEvent()
60
+ const overEvent = createDragEvent()
61
+ const dropEvent = createDragEvent()
62
+
63
+ drag.handleDragStart('S1', 'A', startEvent)
64
+ drag.handleDragOver('B', overEvent)
65
+ drag.handleDrop('B', dropEvent)
66
+
67
+ expect(startEvent.dataTransfer.data['text/plain']).toBe('S1')
68
+ expect(overEvent.dataTransfer.dropEffect).toBe('move')
69
+ expect(dropEvent.defaultPrevented).toBe(true)
70
+ expect(state.value.map(group => group.samples)).toEqual([[], ['S2', 'S1']])
71
+ expect(drag.draggingSample.value).toBeNull()
72
+ expect(drag.dragOverGroup.value).toBeNull()
73
+ })
74
+
75
+ it('reorders sibling subgroups without allowing cross-major drops', () => {
76
+ const state = ref(groups)
77
+ const drag = useSampleSelectorDrag(state)
78
+
79
+ drag.handleGroupDragStart('Control/A', 'sub', createDragEvent())
80
+ drag.handleGroupDragOver('Treatment/A', 'sub', createDragEvent({ clientY: 2 }))
81
+
82
+ expect(drag.reorderTarget.value).toBeNull()
83
+
84
+ const overEvent = createDragEvent({ clientY: 18 })
85
+ const dropEvent = createDragEvent()
86
+ drag.handleGroupDragOver('Control/B', 'sub', overEvent)
87
+ drag.handleGroupDrop('Control/B', 'sub', dropEvent)
88
+
89
+ expect(overEvent.dataTransfer.dropEffect).toBe('move')
90
+ expect(dropEvent.defaultPrevented).toBe(true)
91
+ expect(state.value.map(group => group.name)).toEqual([
92
+ 'Treatment/A',
93
+ 'Treatment/B',
94
+ 'Control/B',
95
+ 'Control/A',
96
+ ])
97
+ expect(drag.draggingGroup.value).toBeNull()
98
+ expect(drag.reorderTarget.value).toBeNull()
99
+ })
100
+ })
@@ -0,0 +1,81 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { SampleGroup } from '../../types'
3
+ import {
4
+ detectSampleGroupSeparator,
5
+ getSampleGroupMajorPrefix,
6
+ moveSampleToGroup,
7
+ removeSampleFromGroup,
8
+ removeSampleGroup,
9
+ removeSampleMajorGroup,
10
+ reorderSampleGroup,
11
+ reorderSampleMajorGroup,
12
+ } from '../../components/SampleSelector.groups'
13
+
14
+ const groups: SampleGroup[] = [
15
+ { name: 'Treatment/A', color: '#111111', samples: ['S1'] },
16
+ { name: 'Treatment/B', color: '#222222', samples: ['S2'] },
17
+ { name: 'Control/A', color: '#333333', samples: ['S3'] },
18
+ { name: 'Control/B', color: '#444444', samples: ['S4'] },
19
+ ]
20
+
21
+ describe('SampleSelector group helpers', () => {
22
+ it('detects the hierarchy separator from group names', () => {
23
+ expect(detectSampleGroupSeparator(groups)).toBe('/')
24
+ expect(detectSampleGroupSeparator([{ name: 'Control_A', color: '#111111', samples: [] }])).toBe('_')
25
+ })
26
+
27
+ it('extracts major group prefixes with the detected separator', () => {
28
+ expect(getSampleGroupMajorPrefix('Treatment/A', '/')).toBe('Treatment')
29
+ expect(getSampleGroupMajorPrefix('Ungrouped', '/')).toBe('Ungrouped')
30
+ })
31
+
32
+ it('moves a sample from one group to another without duplicating target samples', () => {
33
+ const moved = moveSampleToGroup(groups, 'S1', 'Treatment/A', 'Control/A')
34
+
35
+ expect(moved.map(group => group.samples)).toEqual([[], ['S2'], ['S3', 'S1'], ['S4']])
36
+ expect(moveSampleToGroup(groups, 'S3', null, 'Control/A')[2].samples).toEqual(['S3'])
37
+ })
38
+
39
+ it('removes groups and samples without mutating the source array', () => {
40
+ expect(removeSampleGroup(groups, 'Treatment/B').map(group => group.name)).toEqual([
41
+ 'Treatment/A',
42
+ 'Control/A',
43
+ 'Control/B',
44
+ ])
45
+ expect(
46
+ removeSampleMajorGroup(groups, {
47
+ subGroups: [{ name: 'Treatment/A' }, { name: 'Treatment/B' }],
48
+ }).map(group => group.name),
49
+ ).toEqual(['Control/A', 'Control/B'])
50
+ expect(removeSampleFromGroup(groups, 'S1', 'Treatment/A')[0].samples).toEqual([])
51
+ expect(groups[0].samples).toEqual(['S1'])
52
+ })
53
+
54
+ it('reorders flat groups without mutating the source array', () => {
55
+ const reordered = reorderSampleGroup(groups, 'Control/A', 'Treatment/A', 'before')
56
+
57
+ expect(reordered.map(group => group.name)).toEqual([
58
+ 'Control/A',
59
+ 'Treatment/A',
60
+ 'Treatment/B',
61
+ 'Control/B',
62
+ ])
63
+ expect(groups.map(group => group.name)).toEqual([
64
+ 'Treatment/A',
65
+ 'Treatment/B',
66
+ 'Control/A',
67
+ 'Control/B',
68
+ ])
69
+ })
70
+
71
+ it('reorders major group families as contiguous blocks', () => {
72
+ const reordered = reorderSampleMajorGroup(groups, 'Control', 'Treatment', 'before')
73
+
74
+ expect(reordered.map(group => group.name)).toEqual([
75
+ 'Control/A',
76
+ 'Control/B',
77
+ 'Treatment/A',
78
+ 'Treatment/B',
79
+ ])
80
+ })
81
+ })
@@ -0,0 +1,70 @@
1
+ import { ref } from 'vue'
2
+ import { describe, expect, it } from 'vitest'
3
+ import type { SampleGroup } from '../../types'
4
+ import type { SampleMajorGroup } from '../../composables/useSampleGroups'
5
+ import { useSampleSelectorSelection } from '../../components/SampleSelector.selection'
6
+
7
+ const groups: SampleGroup[] = [
8
+ { name: 'Control/A', color: '#111111', samples: ['S1', 'S2'] },
9
+ { name: 'Treatment/A', color: '#222222', samples: ['S3'] },
10
+ ]
11
+
12
+ function createSelection(selected: string[] = []) {
13
+ const selectedRef = ref(selected)
14
+ const emitted: string[][] = []
15
+ const selection = useSampleSelectorSelection({
16
+ selected: () => selectedRef.value,
17
+ samples: () => ['S1', 'S2', 'S3'],
18
+ findGroup: groupName => groups.find(group => group.name === groupName),
19
+ emitSelected: samples => {
20
+ emitted.push(samples)
21
+ selectedRef.value = samples
22
+ },
23
+ })
24
+
25
+ return { emitted, selectedRef, selection }
26
+ }
27
+
28
+ describe('SampleSelector selection helpers', () => {
29
+ it('adapts select-all and single-sample toggles to update events', () => {
30
+ const { emitted, selection } = createSelection()
31
+
32
+ selection.toggleSelectAll()
33
+ selection.toggleSample('S1')
34
+
35
+ expect(emitted).toEqual([
36
+ ['S1', 'S2', 'S3'],
37
+ ['S2', 'S3'],
38
+ ])
39
+ expect(selection.isAllSelected.value).toBe(false)
40
+ })
41
+
42
+ it('toggles sample groups and exposes group selection state', () => {
43
+ const { emitted, selection } = createSelection(['S1'])
44
+
45
+ expect(selection.isGroupPartiallySelected('Control/A')).toBe(true)
46
+ expect(selection.isGroupFullySelected('Control/A')).toBe(false)
47
+
48
+ selection.toggleGroupSamples('Control/A')
49
+
50
+ expect(emitted[0]).toEqual(['S1', 'S2'])
51
+ expect(selection.isGroupFullySelected('Control/A')).toBe(true)
52
+ })
53
+
54
+ it('handles major groups through their aggregate sample list', () => {
55
+ const majorGroup: SampleMajorGroup = {
56
+ name: 'Control',
57
+ color: '#111111',
58
+ subGroups: [],
59
+ allSamples: ['S1', 'S2'],
60
+ }
61
+ const { emitted, selection } = createSelection(['S1', 'S2'])
62
+
63
+ expect(selection.isMajorGroupFullySelected(majorGroup)).toBe(true)
64
+
65
+ selection.toggleMajorGroupSamples(majorGroup)
66
+
67
+ expect(emitted[0]).toEqual([])
68
+ expect(selection.isMajorGroupPartiallySelected(majorGroup)).toBe(false)
69
+ })
70
+ })