@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
@@ -1,6 +1,7 @@
1
1
  import { mount } from '@vue/test-utils'
2
2
  import { describe, expect, it } from 'vitest'
3
3
  import SampleSelector from '../../components/SampleSelector.vue'
4
+ import type { AutoGroupResult, SampleGroup } from '../../types'
4
5
 
5
6
  describe('SampleSelector', () => {
6
7
  it('uses shared list selection for select-all and individual sample toggles', async () => {
@@ -83,3 +84,34 @@ describe('SampleSelector', () => {
83
84
  expect(wrapper.findAll('.mint-sample-selector__flat-name').map(item => item.text())).toEqual(['S1', 'S2'])
84
85
  })
85
86
  })
87
+
88
+ describe('SampleSelector — QC overlay regression', () => {
89
+ it('keeps QC samples in the emitted groups via the concat compat rule', async () => {
90
+ const exp: SampleGroup[] = [
91
+ { name: 'Kidney', color: '#3B82F6', samples: ['s1', 's2'] },
92
+ ]
93
+ const qc: SampleGroup[] = [
94
+ { name: 'IQC', color: '#6B7280', samples: ['iqc1'] },
95
+ ]
96
+ const result: AutoGroupResult = {
97
+ groups: [...exp, ...qc],
98
+ experimentalGroups: exp,
99
+ qcGroups: qc,
100
+ metadata: [],
101
+ excludedSamples: [],
102
+ }
103
+ const wrapper = mount(SampleSelector, {
104
+ props: { samples: ['s1', 's2', 'iqc1'], modelValue: [] },
105
+ global: {
106
+ stubs: {
107
+ AutoGroupModal: true,
108
+ },
109
+ },
110
+ })
111
+ ;(wrapper.vm as unknown as { handleSmartGroupApply: (r: AutoGroupResult) => void })
112
+ .handleSmartGroupApply(result)
113
+ const emitted = wrapper.emitted('update:groups')!.at(-1)![0] as SampleGroup[]
114
+ const allSamples = emitted.flatMap(g => g.samples)
115
+ expect(allSamples).toContain('iqc1')
116
+ })
117
+ })
@@ -0,0 +1,37 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vitest'
3
+ import SampleSelectorSampleRow from '../../components/SampleSelectorSampleRow.vue'
4
+
5
+ describe('SampleSelectorSampleRow', () => {
6
+ it('renders selected and dragging states', () => {
7
+ const wrapper = mount(SampleSelectorSampleRow, {
8
+ props: {
9
+ sample: 'S1',
10
+ selected: true,
11
+ dragging: true,
12
+ accentColor: '#3B82F6',
13
+ },
14
+ })
15
+
16
+ expect(wrapper.classes()).toContain('mint-sample-selector__sample--dragging')
17
+ expect((wrapper.get('input').element as HTMLInputElement).checked).toBe(true)
18
+ expect(wrapper.get('input').attributes('style')).toContain('accent-color')
19
+ expect(wrapper.text()).toContain('S1')
20
+ })
21
+
22
+ it('emits toggle and remove actions', async () => {
23
+ const wrapper = mount(SampleSelectorSampleRow, {
24
+ props: {
25
+ sample: 'S2',
26
+ selected: false,
27
+ removable: true,
28
+ },
29
+ })
30
+
31
+ await wrapper.get('input').trigger('change')
32
+ await wrapper.get('button').trigger('click')
33
+
34
+ expect(wrapper.emitted('toggle')).toHaveLength(1)
35
+ expect(wrapper.emitted('remove')).toHaveLength(1)
36
+ })
37
+ })
@@ -0,0 +1,44 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, expect, it } from 'vitest'
3
+ import ScheduleCalendar from '../../components/ScheduleCalendar.vue'
4
+
5
+ describe('ScheduleCalendar', () => {
6
+ it('renders week events with layout styles from the schedule model', () => {
7
+ const wrapper = mount(ScheduleCalendar, {
8
+ props: {
9
+ modelValue: new Date('2024-05-15T12:00:00'),
10
+ dayStartHour: 6,
11
+ dayEndHour: 10,
12
+ slotDuration: 30,
13
+ showNowIndicator: false,
14
+ events: [
15
+ {
16
+ id: 'run-1',
17
+ title: 'LCMS run',
18
+ start: '2024-05-15T07:00:00',
19
+ end: '2024-05-15T08:30:00',
20
+ },
21
+ ],
22
+ },
23
+ })
24
+
25
+ const event = wrapper.find('.mint-schedule__event')
26
+ expect(event.exists()).toBe(true)
27
+ expect(event.find('.mint-schedule__event-title').text()).toBe('LCMS run')
28
+ expect(event.attributes('style')).toContain('top: 80px')
29
+ expect(event.attributes('style')).toContain('height: 120px')
30
+ })
31
+
32
+ it('renders the month grid from the extracted calendar layout', () => {
33
+ const wrapper = mount(ScheduleCalendar, {
34
+ props: {
35
+ modelValue: new Date('2024-05-15T12:00:00'),
36
+ view: 'month',
37
+ },
38
+ })
39
+
40
+ expect(wrapper.findAll('.mint-schedule__month-weekday')).toHaveLength(7)
41
+ expect(wrapper.findAll('.mint-schedule__month-cell')).toHaveLength(42)
42
+ expect(wrapper.find('.mint-schedule__title').text()).toBe('May 2024')
43
+ })
44
+ })
@@ -0,0 +1,97 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { SettingsModalSchema, SettingsTabInput } from '../../types'
3
+ import {
4
+ APPEARANCE_TAB,
5
+ buildFlatSettingsSchema,
6
+ buildSettingsTabs,
7
+ filterSettingsSchemaByAccess,
8
+ normalizeVisibleSettingsTabs,
9
+ settingsGroupToTab,
10
+ } from '../../components/SettingsModal.schema'
11
+
12
+ describe('SettingsModal schema helpers', () => {
13
+ const schema: SettingsModalSchema = {
14
+ groups: [
15
+ {
16
+ id: 'general',
17
+ label: 'General',
18
+ description: 'Shared settings',
19
+ icon: '<svg />',
20
+ fields: [
21
+ { name: 'title', label: 'Title', type: 'text' },
22
+ { name: 'adminOnly', label: 'Admin Only', type: 'text', requiresAdmin: true },
23
+ ],
24
+ columns: 2,
25
+ },
26
+ {
27
+ id: 'hidden',
28
+ label: 'Hidden',
29
+ requiresAdmin: true,
30
+ fields: [
31
+ { name: 'secret', label: 'Secret', type: 'text' },
32
+ ],
33
+ },
34
+ ],
35
+ }
36
+
37
+ it('builds flat form sections from settings groups', () => {
38
+ expect(buildFlatSettingsSchema(schema)).toEqual({
39
+ sections: [
40
+ {
41
+ id: 'general',
42
+ title: '',
43
+ fields: schema.groups[0].fields,
44
+ columns: 2,
45
+ condition: undefined,
46
+ },
47
+ {
48
+ id: 'hidden',
49
+ title: '',
50
+ fields: schema.groups[1].fields,
51
+ columns: undefined,
52
+ condition: undefined,
53
+ },
54
+ ],
55
+ })
56
+ })
57
+
58
+ it('filters groups and fields with the caller access policy', () => {
59
+ const filtered = filterSettingsSchemaByAccess(
60
+ schema,
61
+ (item) => !item.requiresAdmin
62
+ )
63
+
64
+ expect(filtered.groups).toHaveLength(1)
65
+ expect(filtered.groups[0].id).toBe('general')
66
+ expect(filtered.groups[0].fields.map(field => field.name)).toEqual(['title'])
67
+ })
68
+
69
+ it('maps visible schema groups to tabs', () => {
70
+ expect(settingsGroupToTab(schema.groups[0])).toEqual({
71
+ id: 'general',
72
+ label: 'General',
73
+ description: 'Shared settings',
74
+ icon: '<svg />',
75
+ })
76
+ })
77
+
78
+ it('normalizes manual tabs before access filtering', () => {
79
+ const tabs: SettingsTabInput[] = [
80
+ 'General',
81
+ { id: 'admin', label: 'Admin', requiresAdmin: true },
82
+ ]
83
+
84
+ expect(
85
+ normalizeVisibleSettingsTabs(tabs, (item) => !item.requiresAdmin)
86
+ ).toEqual([
87
+ { id: 'General', label: 'General' },
88
+ ])
89
+ })
90
+
91
+ it('appends the appearance tab when enabled', () => {
92
+ expect(buildSettingsTabs([{ id: 'general', label: 'General' }], true)).toEqual([
93
+ { id: 'general', label: 'General' },
94
+ APPEARANCE_TAB,
95
+ ])
96
+ })
97
+ })
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ conditionGradientStyle,
4
+ formatConcentration,
5
+ getHeatmapColor,
6
+ HEATMAP_COLORS,
7
+ } from '../../components/WellPlate.colors'
8
+
9
+ describe('WellPlate color helpers', () => {
10
+ it('formats concentrations for compact condition headers', () => {
11
+ expect(formatConcentration(1000)).toBe('1k')
12
+ expect(formatConcentration(0.001)).toBe('1e-3')
13
+ expect(formatConcentration(12.5)).toBe('12.5')
14
+ })
15
+
16
+ it('creates readable condition gradient styles', () => {
17
+ expect(conditionGradientStyle('#000000', 10, [0, 10])).toEqual({
18
+ backgroundColor: 'rgba(0, 0, 0, 0.75)',
19
+ color: '#ffffff',
20
+ })
21
+ })
22
+
23
+ it('resolves configured heatmap palette colors', () => {
24
+ expect(getHeatmapColor({ enabled: false }, 1)).toBeNull()
25
+ expect(getHeatmapColor({ enabled: true, min: 0, max: 100 }, 0)).toBe(HEATMAP_COLORS.viridis[0])
26
+ expect(getHeatmapColor({ enabled: true, colorScale: 'custom', customColors: ['#111', '#222'] }, 1)).toBe('#222')
27
+ })
28
+ })
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { ColumnCondition, RowCondition } from '../../types'
3
+ import {
4
+ createColumnConditionMap,
5
+ createColumnConditionSpans,
6
+ createRowConditionMap,
7
+ createRowConditionSpanByRow,
8
+ createRowConditionSpans,
9
+ } from '../../components/WellPlate.conditions'
10
+
11
+ const columnCondition: ColumnCondition = {
12
+ label: 'Drug A',
13
+ color: '#10b981',
14
+ concentrations: [1, 10],
15
+ cols: [2, 3],
16
+ unit: 'uM',
17
+ }
18
+
19
+ const rowCondition: RowCondition = {
20
+ label: 'Drug B',
21
+ color: '#6366f1',
22
+ concentrations: [0.1, 1],
23
+ rows: ['B', 'C'],
24
+ }
25
+
26
+ describe('WellPlate condition helpers', () => {
27
+ it('maps column and row labels to their condition index', () => {
28
+ expect(createColumnConditionMap([columnCondition]).get(3)).toEqual({
29
+ condition: columnCondition,
30
+ indexInGroup: 1,
31
+ })
32
+ expect(createRowConditionMap([rowCondition]).get('C')).toEqual({
33
+ condition: rowCondition,
34
+ indexInGroup: 1,
35
+ })
36
+ })
37
+
38
+ it('creates column spans with gaps around condition groups', () => {
39
+ const spans = createColumnConditionSpans(
40
+ [1, 2, 3, 4],
41
+ createColumnConditionMap([columnCondition]),
42
+ )
43
+
44
+ expect(spans).toEqual([
45
+ { gap: true, colspan: 1 },
46
+ { condition: columnCondition, colspan: 2 },
47
+ { gap: true, colspan: 1 },
48
+ ])
49
+ })
50
+
51
+ it('creates row spans and indexes them by starting row', () => {
52
+ const spans = createRowConditionSpans(
53
+ ['A', 'B', 'C', 'D'],
54
+ createRowConditionMap([rowCondition]),
55
+ )
56
+
57
+ expect(spans).toEqual([
58
+ { gap: true, rowspan: 1, startRow: 0 },
59
+ { condition: rowCondition, rowspan: 2, startRow: 1 },
60
+ { gap: true, rowspan: 1, startRow: 3 },
61
+ ])
62
+ expect(createRowConditionSpanByRow(spans).get(1)).toEqual({
63
+ condition: rowCondition,
64
+ rowspan: 2,
65
+ startRow: 1,
66
+ })
67
+ })
68
+ })
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ createPlateColumnLabels,
4
+ createPlateRowLabels,
5
+ createWellGrid,
6
+ getRectangleWellIds,
7
+ PLATE_CONFIGS,
8
+ WELL_PLATE_SIZE_CONFIGS,
9
+ } from '../../components/WellPlate.geometry'
10
+
11
+ describe('WellPlate geometry helpers', () => {
12
+ it('exposes supported plate dimensions and size presets', () => {
13
+ expect(PLATE_CONFIGS[96]).toEqual({ rows: 8, cols: 12 })
14
+ expect(PLATE_CONFIGS[384]).toEqual({ rows: 16, cols: 24 })
15
+ expect(WELL_PLATE_SIZE_CONFIGS.fill.cellWidth).toBe('100%')
16
+ })
17
+
18
+ it('creates row and column labels from a plate config', () => {
19
+ const config = PLATE_CONFIGS[24]
20
+
21
+ expect(createPlateRowLabels(config)).toEqual(['A', 'B', 'C', 'D'])
22
+ expect(createPlateColumnLabels(config)).toEqual([1, 2, 3, 4, 5, 6])
23
+ })
24
+
25
+ it('creates a well grid with partial well data merged into defaults', () => {
26
+ const config = PLATE_CONFIGS[6]
27
+ const rows = createPlateRowLabels(config)
28
+ const grid = createWellGrid(config, rows, {
29
+ A1: { state: 'filled', sampleType: 'qc', value: 42, metadata: { label: 'QC 1' } },
30
+ })
31
+
32
+ expect(grid[0][0]).toEqual({
33
+ id: 'A1',
34
+ row: 0,
35
+ col: 0,
36
+ state: 'filled',
37
+ sampleType: 'qc',
38
+ value: 42,
39
+ metadata: { label: 'QC 1' },
40
+ })
41
+ expect(grid[1][2]).toMatchObject({ id: 'B3', row: 1, col: 2, state: 'empty' })
42
+ })
43
+
44
+ it('creates rectangle well ids independent of drag direction', () => {
45
+ expect(Array.from(getRectangleWellIds({ row: 1, col: 2 }, { row: 0, col: 0 }, ['A', 'B']))).toEqual([
46
+ 'A1',
47
+ 'A2',
48
+ 'A3',
49
+ 'B1',
50
+ 'B2',
51
+ 'B3',
52
+ ])
53
+ })
54
+ })
@@ -0,0 +1,171 @@
1
+ import { describe, expect, it, vi } from 'vitest'
2
+ import {
3
+ nextClickedWellSelection,
4
+ useWellPlateInteraction,
5
+ } from '../../components/WellPlate.interaction'
6
+ import type { Well, WellPlateSelectionMode } from '../../types'
7
+
8
+ function well(overrides: Partial<Well> = {}): Well {
9
+ return {
10
+ id: 'A1',
11
+ row: 0,
12
+ col: 0,
13
+ state: 'empty',
14
+ ...overrides,
15
+ }
16
+ }
17
+
18
+ function mouseEvent(overrides: Partial<MouseEvent> = {}): MouseEvent {
19
+ return {
20
+ preventDefault: vi.fn(),
21
+ button: 0,
22
+ shiftKey: false,
23
+ metaKey: false,
24
+ ctrlKey: false,
25
+ currentTarget: {
26
+ getBoundingClientRect: () => ({ right: 10, top: 20 }),
27
+ },
28
+ ...overrides,
29
+ } as unknown as MouseEvent
30
+ }
31
+
32
+ function dragEvent(data: Record<string, string> = {}): DragEvent {
33
+ return {
34
+ preventDefault: vi.fn(),
35
+ dataTransfer: {
36
+ effectAllowed: '',
37
+ dropEffect: '',
38
+ getData: (format: string) => data[format] ?? '',
39
+ setData: vi.fn(),
40
+ },
41
+ } as unknown as DragEvent
42
+ }
43
+
44
+ function createInteraction(overrides: Partial<Parameters<typeof useWellPlateInteraction>[0]> = {}) {
45
+ const emitted: Record<string, unknown[]> = {}
46
+ const selected = { value: [] as string[] }
47
+ const selectionMode = { value: 'multiple' as WellPlateSelectionMode }
48
+ const rows = ['A', 'B']
49
+ const grid = [
50
+ [well({ id: 'A1', row: 0, col: 0 }), well({ id: 'A2', row: 0, col: 1 })],
51
+ [well({ id: 'B1', row: 1, col: 0 }), well({ id: 'B2', row: 1, col: 1 })],
52
+ ]
53
+
54
+ const interaction = useWellPlateInteraction({
55
+ modelValue: () => selected.value,
56
+ selectionMode: () => selectionMode.value,
57
+ disabled: () => false,
58
+ readonly: () => false,
59
+ editable: () => false,
60
+ allowSampleDrop: () => false,
61
+ sampleDropParser: () => undefined,
62
+ rowLabels: () => rows,
63
+ wellGrid: () => grid,
64
+ emitSelected: (wellIds) => {
65
+ emitted.selected = [...(emitted.selected ?? []), wellIds]
66
+ selected.value = wellIds
67
+ },
68
+ emitWellClick: (wellId, event) => {
69
+ emitted.click = [...(emitted.click ?? []), [wellId, event]]
70
+ },
71
+ emitWellHover: (wellId, event) => {
72
+ emitted.hover = [...(emitted.hover ?? []), [wellId, event]]
73
+ },
74
+ emitContextMenu: (wellId, event) => {
75
+ emitted.context = [...(emitted.context ?? []), [wellId, event]]
76
+ },
77
+ emitWellMove: (sourceWellId, targetWellId) => {
78
+ emitted.move = [...(emitted.move ?? []), [sourceWellId, targetWellId]]
79
+ },
80
+ emitWellEdit: (wellId, data) => {
81
+ emitted.edit = [...(emitted.edit ?? []), [wellId, data]]
82
+ },
83
+ emitWellClear: (wellId) => {
84
+ emitted.clear = [...(emitted.clear ?? []), wellId]
85
+ },
86
+ emitSampleDrop: (wellId, data, event) => {
87
+ emitted.drop = [...(emitted.drop ?? []), [wellId, data, event]]
88
+ },
89
+ ...overrides,
90
+ })
91
+
92
+ return { emitted, grid, interaction, selected, selectionMode }
93
+ }
94
+
95
+ describe('WellPlate interaction helpers', () => {
96
+ it('calculates clicked well selection for single and multi-select modes', () => {
97
+ expect(nextClickedWellSelection([], 'A1', 'single', mouseEvent())).toEqual(['A1'])
98
+ expect(nextClickedWellSelection(['A1'], 'A1', 'single', mouseEvent())).toEqual([])
99
+ expect(nextClickedWellSelection(['A1'], 'A2', 'multiple', mouseEvent({ shiftKey: true }))).toEqual(['A1', 'A2'])
100
+ expect(nextClickedWellSelection(['A1', 'A2'], 'A1', 'multiple', mouseEvent({ ctrlKey: true }))).toEqual(['A2'])
101
+ expect(nextClickedWellSelection(['A1'], 'A1', 'multiple', mouseEvent())).toEqual([])
102
+ })
103
+
104
+ it('emits click selection changes and opens editor instead when editable', () => {
105
+ const { emitted, grid, interaction } = createInteraction()
106
+
107
+ interaction.handleWellClick(grid[0][0], mouseEvent())
108
+
109
+ expect(emitted.click?.[0]).toEqual(['A1', expect.anything()])
110
+ expect(emitted.selected?.[0]).toEqual(['A1'])
111
+
112
+ const editable = createInteraction({ editable: () => true })
113
+ editable.interaction.handleWellClick(grid[0][1], mouseEvent())
114
+
115
+ expect(editable.interaction.editingWellId.value).toBe('A2')
116
+ expect(editable.interaction.editPopupPosition.value).toEqual({ x: 18, y: 20 })
117
+ expect(editable.emitted.selected).toBeUndefined()
118
+ })
119
+
120
+ it('tracks rectangle drag selection and emits on mouseup', () => {
121
+ const { emitted, grid, interaction, selectionMode } = createInteraction()
122
+ selectionMode.value = 'rectangle'
123
+
124
+ interaction.handleWellMouseDown(grid[0][0], mouseEvent())
125
+ interaction.handleWellMouseEnter(grid[1][1], mouseEvent())
126
+
127
+ expect(Array.from(interaction.dragSelectedWells.value)).toEqual(['A1', 'A2', 'B1', 'B2'])
128
+
129
+ interaction.handleMouseUp()
130
+
131
+ expect(emitted.selected?.[0]).toEqual(['A1', 'A2', 'B1', 'B2'])
132
+ expect(interaction.isDragging.value).toBe(false)
133
+ })
134
+
135
+ it('moves wells in drag mode and resets drag state', () => {
136
+ const { emitted, grid, interaction, selectionMode } = createInteraction()
137
+ selectionMode.value = 'drag'
138
+
139
+ interaction.handleDragStart({ ...grid[0][0], sampleType: 'sample' }, dragEvent())
140
+ interaction.handleDrop(grid[1][1], dragEvent())
141
+
142
+ expect(emitted.move?.[0]).toEqual(['A1', 'B2'])
143
+ expect(interaction.dragSourceWell.value).toBeNull()
144
+ expect(interaction.dragTargetWell.value).toBeNull()
145
+ })
146
+
147
+ it('parses dropped samples when sample drop is enabled', () => {
148
+ const { emitted, grid, interaction } = createInteraction({ allowSampleDrop: () => true })
149
+
150
+ interaction.handleDrop(grid[0][0], dragEvent({ 'text/plain': 'Sample A' }))
151
+
152
+ expect(emitted.drop?.[0]).toEqual([
153
+ 'A1',
154
+ { sampleName: 'Sample A', label: 'Sample A', raw: 'Sample A' },
155
+ expect.anything(),
156
+ ])
157
+ })
158
+
159
+ it('handles keyboard clear and select all shortcuts', () => {
160
+ const { emitted, interaction, selected } = createInteraction()
161
+ selected.value = ['A1']
162
+
163
+ interaction.handleKeyDown(new KeyboardEvent('keydown', { key: 'Escape' }))
164
+ interaction.handleKeyDown(new KeyboardEvent('keydown', { key: 'a', metaKey: true }))
165
+
166
+ expect(emitted.selected).toEqual([
167
+ [],
168
+ ['A1', 'A2', 'B1', 'B2'],
169
+ ])
170
+ })
171
+ })
@@ -0,0 +1,13 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { DEFAULT_WELL_LEGEND_ITEMS } from '../../components/WellPlate.legend'
3
+
4
+ describe('WellPlate legend helpers', () => {
5
+ it('keeps default sample legend item order stable', () => {
6
+ expect(DEFAULT_WELL_LEGEND_ITEMS.map(item => item.type)).toEqual([
7
+ 'sample',
8
+ 'blank',
9
+ 'qc',
10
+ 'iqc',
11
+ ])
12
+ })
13
+ })
@@ -0,0 +1,122 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ sampleTypeIndicator,
4
+ wellBadge,
5
+ wellClasses,
6
+ wellLabel,
7
+ wellStyle,
8
+ } from '../../components/WellPlate.rendering'
9
+ import type { Well } from '../../types'
10
+
11
+ function well(overrides: Partial<Well> = {}): Well {
12
+ return {
13
+ id: 'A1',
14
+ row: 0,
15
+ col: 0,
16
+ state: 'empty',
17
+ ...overrides,
18
+ }
19
+ }
20
+
21
+ describe('WellPlate rendering helpers', () => {
22
+ it('derives well classes from selection and drag priority', () => {
23
+ expect(wellClasses(well({ sampleType: 'sample' }), {
24
+ wellShape: 'circle',
25
+ selectionMode: 'drag',
26
+ selected: true,
27
+ dragOver: false,
28
+ hovered: false,
29
+ disabled: false,
30
+ readonly: false,
31
+ dragSource: false,
32
+ dragTarget: true,
33
+ })).toEqual([
34
+ 'mint-well-plate__well',
35
+ 'mint-well-plate__well--circle',
36
+ 'mint-well-plate__well--draggable',
37
+ 'mint-well-plate__well--drag-target',
38
+ ])
39
+ })
40
+
41
+ it('suppresses hover styling when readonly and includes disabled/filled states', () => {
42
+ expect(wellClasses(well({ state: 'filled' }), {
43
+ wellShape: 'rounded',
44
+ selectionMode: 'multiple',
45
+ selected: false,
46
+ dragOver: false,
47
+ hovered: true,
48
+ disabled: true,
49
+ readonly: true,
50
+ dragSource: false,
51
+ dragTarget: false,
52
+ })).toEqual([
53
+ 'mint-well-plate__well',
54
+ 'mint-well-plate__well--rounded',
55
+ 'mint-well-plate__well--disabled',
56
+ 'mint-well-plate__well--filled',
57
+ ])
58
+ })
59
+
60
+ it('prefers heatmap and custom sample colors before defaults', () => {
61
+ expect(wellStyle(well({ value: 0.5 }), {
62
+ heatmap: { enabled: true, colorScale: 'viridis', min: 0, max: 1 },
63
+ sampleColors: {},
64
+ })).toEqual({
65
+ backgroundColor: expect.any(String),
66
+ border: '1px solid transparent',
67
+ })
68
+
69
+ expect(wellStyle(well({ sampleType: 'treated' }), {
70
+ heatmap: { enabled: false },
71
+ sampleColors: { treated: '#123456' },
72
+ })).toEqual({
73
+ backgroundColor: '#12345626',
74
+ border: '1px solid #12345666',
75
+ })
76
+
77
+ expect(wellStyle(well({ sampleType: 'qc' }), {
78
+ heatmap: { enabled: false },
79
+ sampleColors: {},
80
+ })).toEqual({
81
+ backgroundColor: 'rgba(139, 92, 246, 0.15)',
82
+ border: '1px solid rgba(139, 92, 246, 0.4)',
83
+ })
84
+ })
85
+
86
+ it('falls back to empty or filled well styles', () => {
87
+ expect(wellStyle(well(), {
88
+ heatmap: { enabled: false },
89
+ sampleColors: {},
90
+ })).toEqual({
91
+ backgroundColor: 'var(--bg-tertiary)',
92
+ border: '1px dashed var(--border-color)',
93
+ })
94
+
95
+ expect(wellStyle(well({ state: 'filled' }), {
96
+ heatmap: { enabled: false },
97
+ sampleColors: {},
98
+ })).toEqual({
99
+ backgroundColor: 'var(--bg-tertiary)',
100
+ border: '1px solid var(--border-color)',
101
+ })
102
+ })
103
+
104
+ it('formats indicators, labels, and badges for optional overlays', () => {
105
+ expect(sampleTypeIndicator(well({ sampleType: 'qc' }), true)).toBe('Q')
106
+ expect(sampleTypeIndicator(well({ sampleType: 'treated' }), true)).toBe('T')
107
+ expect(sampleTypeIndicator(well({ sampleType: 'treated' }), false)).toBeNull()
108
+
109
+ expect(wellLabel(well({ metadata: { label: 'Dose A' } }), true)).toBe('Dose A')
110
+ expect(wellLabel(well({ metadata: { label: 'Dose A' } }), false)).toBeUndefined()
111
+
112
+ expect(wellBadge(well({ metadata: { injectionCount: 3 } }), true)).toEqual({
113
+ text: '3',
114
+ color: '#6366f1',
115
+ })
116
+ expect(wellBadge(well({ metadata: { customMethod: 'LCMS-2' } }), true)).toEqual({
117
+ text: '+',
118
+ color: '#ec4899',
119
+ })
120
+ expect(wellBadge(well({ metadata: { injectionCount: 1 } }), true)).toBeNull()
121
+ })
122
+ })