@morscherlab/mint-sdk 1.0.0-alpha.2

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 (491) hide show
  1. package/README.md +326 -0
  2. package/dist/__stories__/experiment-helpers.d.ts +25 -0
  3. package/dist/__tests__/components/AppLayout.test.d.ts +1 -0
  4. package/dist/__tests__/components/AppSidebar.test.d.ts +1 -0
  5. package/dist/__tests__/components/AppTopBar.test.d.ts +1 -0
  6. package/dist/__tests__/components/BaseInput.test.d.ts +1 -0
  7. package/dist/__tests__/components/BasePill.test.d.ts +1 -0
  8. package/dist/__tests__/components/Calendar.test.d.ts +1 -0
  9. package/dist/__tests__/components/CollapsibleCard.test.d.ts +1 -0
  10. package/dist/__tests__/components/DataFrame.test.d.ts +1 -0
  11. package/dist/__tests__/components/DropdownButton.test.d.ts +1 -0
  12. package/dist/__tests__/composables/formBuilderRegistry.test.d.ts +1 -0
  13. package/dist/__tests__/composables/useAppExperiment.test.d.ts +1 -0
  14. package/dist/__tests__/composables/useAuth.test.d.ts +1 -0
  15. package/dist/__tests__/composables/useAutoGroup.test.d.ts +1 -0
  16. package/dist/__tests__/composables/useExperimentData.test.d.ts +13 -0
  17. package/dist/__tests__/composables/useExperimentSave.test.d.ts +1 -0
  18. package/dist/__tests__/composables/useForm.test.d.ts +1 -0
  19. package/dist/__tests__/composables/useFormBuilder.test.d.ts +1 -0
  20. package/dist/__tests__/composables/usePlatformContext.test.d.ts +1 -0
  21. package/dist/__tests__/composables/usePluginApi.test.d.ts +13 -0
  22. package/dist/__tests__/composables/usePluginConfig.test.d.ts +14 -0
  23. package/dist/__tests__/utils/color.test.d.ts +1 -0
  24. package/dist/auth-BYmxZdJl.js +297 -0
  25. package/dist/auth-BYmxZdJl.js.map +1 -0
  26. package/dist/components/AlertBox.vue.d.ts +34 -0
  27. package/dist/components/AppAvatarMenu.vue.d.ts +58 -0
  28. package/dist/components/AppContainer.vue.d.ts +28 -0
  29. package/dist/components/AppLayout.vue.d.ts +31 -0
  30. package/dist/components/AppPageSelector.vue.d.ts +43 -0
  31. package/dist/components/AppPillNav.vue.d.ts +11 -0
  32. package/dist/components/AppPluginSwitcher.vue.d.ts +38 -0
  33. package/dist/components/AppSidebar.vue.d.ts +47 -0
  34. package/dist/components/AppTopBar.vue.d.ts +111 -0
  35. package/dist/components/AuditTrail.vue.d.ts +38 -0
  36. package/dist/components/AutoGroupModal.vue.d.ts +124 -0
  37. package/dist/components/Avatar.vue.d.ts +14 -0
  38. package/dist/components/BaseButton.vue.d.ts +37 -0
  39. package/dist/components/BaseCheckbox.vue.d.ts +17 -0
  40. package/dist/components/BaseInput.vue.d.ts +34 -0
  41. package/dist/components/BaseModal.vue.d.ts +46 -0
  42. package/dist/components/BasePill.vue.d.ts +57 -0
  43. package/dist/components/BaseRadioGroup.vue.d.ts +21 -0
  44. package/dist/components/BaseSelect.vue.d.ts +20 -0
  45. package/dist/components/BaseSlider.vue.d.ts +22 -0
  46. package/dist/components/BaseTabs.vue.d.ts +14 -0
  47. package/dist/components/BaseTextarea.vue.d.ts +30 -0
  48. package/dist/components/BaseToggle.vue.d.ts +19 -0
  49. package/dist/components/BatchProgressList.vue.d.ts +43 -0
  50. package/dist/components/Breadcrumb.vue.d.ts +33 -0
  51. package/dist/components/Calendar.vue.d.ts +107 -0
  52. package/dist/components/ChartContainer.vue.d.ts +31 -0
  53. package/dist/components/ChemicalFormula.vue.d.ts +8 -0
  54. package/dist/components/CollapsibleCard.vue.d.ts +41 -0
  55. package/dist/components/ColorSlider.vue.d.ts +34 -0
  56. package/dist/components/ConcentrationInput.vue.d.ts +25 -0
  57. package/dist/components/ConfirmDialog.vue.d.ts +42 -0
  58. package/dist/components/DataFrame.vue.d.ts +107 -0
  59. package/dist/components/DatePicker.vue.d.ts +25 -0
  60. package/dist/components/DateTimePicker.vue.d.ts +30 -0
  61. package/dist/components/Divider.vue.d.ts +14 -0
  62. package/dist/components/DoseCalculator.vue.d.ts +19 -0
  63. package/dist/components/DropdownButton.vue.d.ts +47 -0
  64. package/dist/components/EmptyState.vue.d.ts +36 -0
  65. package/dist/components/ExperimentCodeBadge.vue.d.ts +14 -0
  66. package/dist/components/ExperimentDataViewer.vue.d.ts +29 -0
  67. package/dist/components/ExperimentPopover.vue.d.ts +32 -0
  68. package/dist/components/ExperimentSelectorModal.vue.d.ts +28 -0
  69. package/dist/components/ExperimentTimeline.vue.d.ts +44 -0
  70. package/dist/components/FileUploader.vue.d.ts +40 -0
  71. package/dist/components/FitPanel.vue.d.ts +46 -0
  72. package/dist/components/FormActions.vue.d.ts +33 -0
  73. package/dist/components/FormBuilder.vue.d.ts +287 -0
  74. package/dist/components/FormField.vue.d.ts +28 -0
  75. package/dist/components/FormFieldRenderer.vue.d.ts +31 -0
  76. package/dist/components/FormSection.vue.d.ts +43 -0
  77. package/dist/components/FormulaInput.vue.d.ts +25 -0
  78. package/dist/components/GroupAssigner.vue.d.ts +25 -0
  79. package/dist/components/GroupingModal.vue.d.ts +12 -0
  80. package/dist/components/IconButton.vue.d.ts +34 -0
  81. package/dist/components/LoadingSpinner.vue.d.ts +12 -0
  82. package/dist/components/MoleculeInput.vue.d.ts +27 -0
  83. package/dist/components/MultiSelect.vue.d.ts +19 -0
  84. package/dist/components/NumberInput.vue.d.ts +22 -0
  85. package/dist/components/PlateMapEditor.vue.d.ts +50 -0
  86. package/dist/components/ProgressBar.vue.d.ts +23 -0
  87. package/dist/components/ProtocolStepEditor.vue.d.ts +24 -0
  88. package/dist/components/RackEditor.vue.d.ts +40 -0
  89. package/dist/components/ReagentEditor.vue.d.ts +30 -0
  90. package/dist/components/ReagentList.vue.d.ts +32 -0
  91. package/dist/components/ResourceCard.vue.d.ts +50 -0
  92. package/dist/components/SampleHierarchyTree.vue.d.ts +26 -0
  93. package/dist/components/SampleLegend.vue.d.ts +32 -0
  94. package/dist/components/SampleSelector.vue.d.ts +29 -0
  95. package/dist/components/ScheduleCalendar.vue.d.ts +110 -0
  96. package/dist/components/ScientificNumber.vue.d.ts +14 -0
  97. package/dist/components/SegmentedControl.vue.d.ts +20 -0
  98. package/dist/components/SequenceInput.vue.d.ts +54 -0
  99. package/dist/components/SettingsButton.vue.d.ts +30 -0
  100. package/dist/components/SettingsModal.vue.d.ts +36 -0
  101. package/dist/components/Skeleton.vue.d.ts +11 -0
  102. package/dist/components/StatusIndicator.vue.d.ts +13 -0
  103. package/dist/components/StepWizard.vue.d.ts +65 -0
  104. package/dist/components/TagsInput.vue.d.ts +39 -0
  105. package/dist/components/ThemeToggle.vue.d.ts +7 -0
  106. package/dist/components/TimePicker.vue.d.ts +29 -0
  107. package/dist/components/TimeRangeInput.vue.d.ts +27 -0
  108. package/dist/components/ToastNotification.vue.d.ts +2 -0
  109. package/dist/components/Tooltip.vue.d.ts +35 -0
  110. package/dist/components/UnitInput.vue.d.ts +39 -0
  111. package/dist/components/WellEditPopup.vue.d.ts +25 -0
  112. package/dist/components/WellPlate.vue.d.ts +73 -0
  113. package/dist/components/index.d.ts +87 -0
  114. package/dist/components/index.js +3 -0
  115. package/dist/components-CKf-UpGi.js +15089 -0
  116. package/dist/components-CKf-UpGi.js.map +1 -0
  117. package/dist/composables/experiment-utils.d.ts +8 -0
  118. package/dist/composables/formBuilderRegistry.d.ts +13 -0
  119. package/dist/composables/index.d.ts +28 -0
  120. package/dist/composables/index.js +3 -0
  121. package/dist/composables/useApi.d.ts +20 -0
  122. package/dist/composables/useAppExperiment.d.ts +37 -0
  123. package/dist/composables/useAsync.d.ts +128 -0
  124. package/dist/composables/useAuth.d.ts +47 -0
  125. package/dist/composables/useAutoGroup.d.ts +106 -0
  126. package/dist/composables/useChemicalFormula.d.ts +21 -0
  127. package/dist/composables/useConcentrationUnits.d.ts +29 -0
  128. package/dist/composables/useDoseCalculator.d.ts +58 -0
  129. package/dist/composables/useExperimentData.d.ts +18 -0
  130. package/dist/composables/useExperimentSave.d.ts +36 -0
  131. package/dist/composables/useExperimentSelector.d.ts +30 -0
  132. package/dist/composables/useForm.d.ts +92 -0
  133. package/dist/composables/useFormBuilder.d.ts +24 -0
  134. package/dist/composables/usePasskey.d.ts +10 -0
  135. package/dist/composables/usePlatformContext.d.ts +131 -0
  136. package/dist/composables/usePluginApi.d.ts +29 -0
  137. package/dist/composables/usePluginConfig.d.ts +13 -0
  138. package/dist/composables/useProtocolTemplates.d.ts +44 -0
  139. package/dist/composables/useRackEditor.d.ts +31 -0
  140. package/dist/composables/useReagentSeries.d.ts +23 -0
  141. package/dist/composables/useScheduleDrag.d.ts +78 -0
  142. package/dist/composables/useSequenceUtils.d.ts +14 -0
  143. package/dist/composables/useTheme.d.ts +8 -0
  144. package/dist/composables/useTimeUtils.d.ts +29 -0
  145. package/dist/composables/useToast.d.ts +22 -0
  146. package/dist/composables/useWellPlateEditor.d.ts +33 -0
  147. package/dist/composables-D0QfFzq1.js +805 -0
  148. package/dist/composables-D0QfFzq1.js.map +1 -0
  149. package/dist/histoire.setup.d.ts +1 -0
  150. package/dist/index.d.ts +6 -0
  151. package/dist/index.js +7 -0
  152. package/dist/install.d.ts +16 -0
  153. package/dist/install.js +23 -0
  154. package/dist/install.js.map +1 -0
  155. package/dist/stores/auth.d.ts +146 -0
  156. package/dist/stores/index.d.ts +2 -0
  157. package/dist/stores/index.js +2 -0
  158. package/dist/stores/settings.d.ts +75 -0
  159. package/dist/styles.css +29728 -0
  160. package/dist/tailwind.preset.d.ts +58 -0
  161. package/dist/tailwind.preset.js +66 -0
  162. package/dist/tailwind.preset.js.map +1 -0
  163. package/dist/types/auth.d.ts +42 -0
  164. package/dist/types/auto-group.d.ts +34 -0
  165. package/dist/types/components.d.ts +528 -0
  166. package/dist/types/form-builder.d.ts +167 -0
  167. package/dist/types/index.d.ts +5 -0
  168. package/dist/types/index.js +0 -0
  169. package/dist/types/platform.d.ts +75 -0
  170. package/dist/useScheduleDrag-DAJueTbK.js +7181 -0
  171. package/dist/useScheduleDrag-DAJueTbK.js.map +1 -0
  172. package/dist/utils/color.d.ts +24 -0
  173. package/package.json +114 -0
  174. package/src/__stories__/experiment-helpers.ts +83 -0
  175. package/src/__tests__/components/AppLayout.test.ts +163 -0
  176. package/src/__tests__/components/AppSidebar.test.ts +292 -0
  177. package/src/__tests__/components/AppTopBar.test.ts +683 -0
  178. package/src/__tests__/components/BaseInput.test.ts +99 -0
  179. package/src/__tests__/components/BasePill.test.ts +291 -0
  180. package/src/__tests__/components/Calendar.test.ts +566 -0
  181. package/src/__tests__/components/CollapsibleCard.test.ts +524 -0
  182. package/src/__tests__/components/DataFrame.test.ts +767 -0
  183. package/src/__tests__/components/DropdownButton.test.ts +471 -0
  184. package/src/__tests__/composables/formBuilderRegistry.test.ts +187 -0
  185. package/src/__tests__/composables/useAppExperiment.test.ts +560 -0
  186. package/src/__tests__/composables/useAuth.test.ts +188 -0
  187. package/src/__tests__/composables/useAutoGroup.test.ts +860 -0
  188. package/src/__tests__/composables/useExperimentData.test.ts +127 -0
  189. package/src/__tests__/composables/useExperimentSave.test.ts +347 -0
  190. package/src/__tests__/composables/useForm.test.ts +205 -0
  191. package/src/__tests__/composables/useFormBuilder.test.ts +917 -0
  192. package/src/__tests__/composables/usePlatformContext.test.ts +116 -0
  193. package/src/__tests__/composables/usePluginApi.test.ts +81 -0
  194. package/src/__tests__/composables/usePluginConfig.test.ts +176 -0
  195. package/src/__tests__/utils/color.test.ts +96 -0
  196. package/src/components/AlertBox.story.vue +204 -0
  197. package/src/components/AlertBox.vue +88 -0
  198. package/src/components/AppAvatarMenu.story.vue +155 -0
  199. package/src/components/AppAvatarMenu.vue +184 -0
  200. package/src/components/AppContainer.story.vue +104 -0
  201. package/src/components/AppContainer.vue +34 -0
  202. package/src/components/AppLayout.story.vue +292 -0
  203. package/src/components/AppLayout.vue +75 -0
  204. package/src/components/AppPageSelector.vue +159 -0
  205. package/src/components/AppPillNav.vue +66 -0
  206. package/src/components/AppPluginSwitcher.vue +241 -0
  207. package/src/components/AppSidebar.story.vue +309 -0
  208. package/src/components/AppSidebar.vue +119 -0
  209. package/src/components/AppTopBar.story.vue +304 -0
  210. package/src/components/AppTopBar.vue +661 -0
  211. package/src/components/AuditTrail.story.vue +163 -0
  212. package/src/components/AuditTrail.vue +151 -0
  213. package/src/components/AutoGroupModal.story.vue +273 -0
  214. package/src/components/AutoGroupModal.vue +566 -0
  215. package/src/components/Avatar.story.vue +115 -0
  216. package/src/components/Avatar.vue +79 -0
  217. package/src/components/BaseButton.story.vue +96 -0
  218. package/src/components/BaseButton.vue +73 -0
  219. package/src/components/BaseCheckbox.story.vue +73 -0
  220. package/src/components/BaseCheckbox.vue +69 -0
  221. package/src/components/BaseInput.story.vue +98 -0
  222. package/src/components/BaseInput.vue +74 -0
  223. package/src/components/BaseModal.story.vue +237 -0
  224. package/src/components/BaseModal.vue +182 -0
  225. package/src/components/BasePill.story.vue +142 -0
  226. package/src/components/BasePill.vue +89 -0
  227. package/src/components/BaseRadioGroup.story.vue +145 -0
  228. package/src/components/BaseRadioGroup.vue +124 -0
  229. package/src/components/BaseSelect.story.vue +120 -0
  230. package/src/components/BaseSelect.vue +71 -0
  231. package/src/components/BaseSlider.story.vue +122 -0
  232. package/src/components/BaseSlider.vue +126 -0
  233. package/src/components/BaseTabs.story.vue +127 -0
  234. package/src/components/BaseTabs.vue +59 -0
  235. package/src/components/BaseTextarea.story.vue +91 -0
  236. package/src/components/BaseTextarea.vue +62 -0
  237. package/src/components/BaseToggle.story.vue +81 -0
  238. package/src/components/BaseToggle.vue +76 -0
  239. package/src/components/BatchProgressList.story.vue +92 -0
  240. package/src/components/BatchProgressList.vue +184 -0
  241. package/src/components/Breadcrumb.story.vue +106 -0
  242. package/src/components/Breadcrumb.vue +75 -0
  243. package/src/components/Calendar.story.vue +106 -0
  244. package/src/components/Calendar.vue +363 -0
  245. package/src/components/ChartContainer.story.vue +113 -0
  246. package/src/components/ChartContainer.vue +64 -0
  247. package/src/components/ChemicalFormula.story.vue +102 -0
  248. package/src/components/ChemicalFormula.vue +39 -0
  249. package/src/components/CollapsibleCard.story.vue +135 -0
  250. package/src/components/CollapsibleCard.vue +167 -0
  251. package/src/components/ColorSlider.story.vue +120 -0
  252. package/src/components/ColorSlider.vue +164 -0
  253. package/src/components/ConcentrationInput.story.vue +77 -0
  254. package/src/components/ConcentrationInput.vue +185 -0
  255. package/src/components/ConfirmDialog.story.vue +248 -0
  256. package/src/components/ConfirmDialog.vue +93 -0
  257. package/src/components/DataFrame.story.vue +148 -0
  258. package/src/components/DataFrame.vue +419 -0
  259. package/src/components/DatePicker.story.vue +119 -0
  260. package/src/components/DatePicker.vue +330 -0
  261. package/src/components/DateTimePicker.story.vue +112 -0
  262. package/src/components/DateTimePicker.vue +392 -0
  263. package/src/components/Divider.story.vue +80 -0
  264. package/src/components/Divider.vue +49 -0
  265. package/src/components/DoseCalculator.story.vue +68 -0
  266. package/src/components/DoseCalculator.vue +476 -0
  267. package/src/components/DropdownButton.story.vue +102 -0
  268. package/src/components/DropdownButton.vue +181 -0
  269. package/src/components/EmptyState.story.vue +135 -0
  270. package/src/components/EmptyState.vue +69 -0
  271. package/src/components/ExperimentCodeBadge.story.vue +77 -0
  272. package/src/components/ExperimentCodeBadge.vue +64 -0
  273. package/src/components/ExperimentDataViewer.story.vue +174 -0
  274. package/src/components/ExperimentDataViewer.vue +288 -0
  275. package/src/components/ExperimentPopover.story.vue +384 -0
  276. package/src/components/ExperimentPopover.vue +241 -0
  277. package/src/components/ExperimentSelectorModal.story.vue +391 -0
  278. package/src/components/ExperimentSelectorModal.vue +387 -0
  279. package/src/components/ExperimentTimeline.story.vue +161 -0
  280. package/src/components/ExperimentTimeline.vue +382 -0
  281. package/src/components/FileUploader.story.vue +107 -0
  282. package/src/components/FileUploader.vue +386 -0
  283. package/src/components/FitPanel.story.vue +125 -0
  284. package/src/components/FitPanel.vue +120 -0
  285. package/src/components/FormActions.vue +92 -0
  286. package/src/components/FormBuilder.vue +214 -0
  287. package/src/components/FormField.story.vue +132 -0
  288. package/src/components/FormField.vue +59 -0
  289. package/src/components/FormFieldRenderer.vue +58 -0
  290. package/src/components/FormSection.vue +90 -0
  291. package/src/components/FormulaInput.story.vue +96 -0
  292. package/src/components/FormulaInput.vue +125 -0
  293. package/src/components/GroupAssigner.story.vue +83 -0
  294. package/src/components/GroupAssigner.vue +284 -0
  295. package/src/components/GroupingModal.story.vue +52 -0
  296. package/src/components/GroupingModal.vue +422 -0
  297. package/src/components/IconButton.story.vue +135 -0
  298. package/src/components/IconButton.vue +73 -0
  299. package/src/components/LoadingSpinner.story.vue +70 -0
  300. package/src/components/LoadingSpinner.vue +50 -0
  301. package/src/components/MoleculeInput.story.vue +66 -0
  302. package/src/components/MoleculeInput.vue +426 -0
  303. package/src/components/MultiSelect.story.vue +132 -0
  304. package/src/components/MultiSelect.vue +118 -0
  305. package/src/components/NumberInput.story.vue +122 -0
  306. package/src/components/NumberInput.vue +160 -0
  307. package/src/components/PlateMapEditor.story.vue +92 -0
  308. package/src/components/PlateMapEditor.vue +513 -0
  309. package/src/components/ProgressBar.story.vue +148 -0
  310. package/src/components/ProgressBar.vue +114 -0
  311. package/src/components/ProtocolStepEditor.story.vue +69 -0
  312. package/src/components/ProtocolStepEditor.vue +522 -0
  313. package/src/components/RackEditor.story.vue +100 -0
  314. package/src/components/RackEditor.vue +371 -0
  315. package/src/components/ReagentEditor.story.vue +153 -0
  316. package/src/components/ReagentEditor.vue +418 -0
  317. package/src/components/ReagentList.story.vue +137 -0
  318. package/src/components/ReagentList.vue +463 -0
  319. package/src/components/ResourceCard.story.vue +150 -0
  320. package/src/components/ResourceCard.vue +161 -0
  321. package/src/components/SampleHierarchyTree.story.vue +161 -0
  322. package/src/components/SampleHierarchyTree.vue +256 -0
  323. package/src/components/SampleLegend.story.vue +91 -0
  324. package/src/components/SampleLegend.vue +119 -0
  325. package/src/components/SampleSelector.story.vue +111 -0
  326. package/src/components/SampleSelector.vue +1033 -0
  327. package/src/components/ScheduleCalendar.story.vue +195 -0
  328. package/src/components/ScheduleCalendar.vue +569 -0
  329. package/src/components/ScientificNumber.story.vue +127 -0
  330. package/src/components/ScientificNumber.vue +197 -0
  331. package/src/components/SegmentedControl.story.vue +132 -0
  332. package/src/components/SegmentedControl.vue +79 -0
  333. package/src/components/SequenceInput.story.vue +119 -0
  334. package/src/components/SequenceInput.vue +209 -0
  335. package/src/components/SettingsButton.story.vue +58 -0
  336. package/src/components/SettingsButton.vue +76 -0
  337. package/src/components/SettingsModal.story.vue +145 -0
  338. package/src/components/SettingsModal.vue +146 -0
  339. package/src/components/Skeleton.story.vue +141 -0
  340. package/src/components/Skeleton.vue +74 -0
  341. package/src/components/StatusIndicator.story.vue +99 -0
  342. package/src/components/StatusIndicator.vue +40 -0
  343. package/src/components/StepWizard.story.vue +155 -0
  344. package/src/components/StepWizard.vue +223 -0
  345. package/src/components/TagsInput.story.vue +155 -0
  346. package/src/components/TagsInput.vue +265 -0
  347. package/src/components/ThemeToggle.story.vue +36 -0
  348. package/src/components/ThemeToggle.vue +54 -0
  349. package/src/components/TimePicker.story.vue +96 -0
  350. package/src/components/TimePicker.vue +273 -0
  351. package/src/components/TimeRangeInput.story.vue +104 -0
  352. package/src/components/TimeRangeInput.vue +122 -0
  353. package/src/components/ToastNotification.story.vue +157 -0
  354. package/src/components/ToastNotification.vue +62 -0
  355. package/src/components/Tooltip.story.vue +138 -0
  356. package/src/components/Tooltip.vue +119 -0
  357. package/src/components/UnitInput.story.vue +194 -0
  358. package/src/components/UnitInput.vue +213 -0
  359. package/src/components/WellEditPopup.vue +234 -0
  360. package/src/components/WellPlate.story.vue +282 -0
  361. package/src/components/WellPlate.vue +830 -0
  362. package/src/components/index.ts +118 -0
  363. package/src/composables/experiment-utils.ts +57 -0
  364. package/src/composables/formBuilderRegistry.ts +79 -0
  365. package/src/composables/index.ts +140 -0
  366. package/src/composables/useApi.ts +167 -0
  367. package/src/composables/useAppExperiment.ts +159 -0
  368. package/src/composables/useAsync.ts +323 -0
  369. package/src/composables/useAuth.ts +445 -0
  370. package/src/composables/useAutoGroup.ts +641 -0
  371. package/src/composables/useChemicalFormula.ts +275 -0
  372. package/src/composables/useConcentrationUnits.ts +246 -0
  373. package/src/composables/useDoseCalculator.ts +370 -0
  374. package/src/composables/useExperimentData.ts +86 -0
  375. package/src/composables/useExperimentSave.ts +192 -0
  376. package/src/composables/useExperimentSelector.ts +292 -0
  377. package/src/composables/useForm.ts +416 -0
  378. package/src/composables/useFormBuilder.ts +383 -0
  379. package/src/composables/usePasskey.ts +216 -0
  380. package/src/composables/usePlatformContext.ts +299 -0
  381. package/src/composables/usePluginApi.ts +39 -0
  382. package/src/composables/usePluginConfig.ts +93 -0
  383. package/src/composables/useProtocolTemplates.ts +518 -0
  384. package/src/composables/useRackEditor.ts +222 -0
  385. package/src/composables/useReagentSeries.ts +91 -0
  386. package/src/composables/useScheduleDrag.ts +245 -0
  387. package/src/composables/useSequenceUtils.ts +105 -0
  388. package/src/composables/useTheme.ts +58 -0
  389. package/src/composables/useTimeUtils.ts +131 -0
  390. package/src/composables/useToast.ts +40 -0
  391. package/src/composables/useWellPlateEditor.ts +421 -0
  392. package/src/histoire.setup.ts +17 -0
  393. package/src/index.ts +367 -0
  394. package/src/install.ts +32 -0
  395. package/src/stores/auth.ts +152 -0
  396. package/src/stores/index.ts +2 -0
  397. package/src/stores/settings.ts +218 -0
  398. package/src/styles/components/alert-box.css +150 -0
  399. package/src/styles/components/app-avatar-menu.css +155 -0
  400. package/src/styles/components/app-container.css +33 -0
  401. package/src/styles/components/app-layout.css +98 -0
  402. package/src/styles/components/app-page-selector.css +191 -0
  403. package/src/styles/components/app-pill-nav.css +57 -0
  404. package/src/styles/components/app-plugin-switcher.css +209 -0
  405. package/src/styles/components/app-sidebar.css +145 -0
  406. package/src/styles/components/app-top-bar.css +492 -0
  407. package/src/styles/components/audit-trail.css +143 -0
  408. package/src/styles/components/auto-group-modal.css +644 -0
  409. package/src/styles/components/avatar.css +73 -0
  410. package/src/styles/components/batch-progress-list.css +196 -0
  411. package/src/styles/components/breadcrumb.css +64 -0
  412. package/src/styles/components/button.css +188 -0
  413. package/src/styles/components/calendar.css +192 -0
  414. package/src/styles/components/chart-container.css +69 -0
  415. package/src/styles/components/checkbox.css +123 -0
  416. package/src/styles/components/chemical-formula.css +46 -0
  417. package/src/styles/components/collapsible-card.css +253 -0
  418. package/src/styles/components/color-slider.css +110 -0
  419. package/src/styles/components/concentration-input.css +156 -0
  420. package/src/styles/components/confirm-dialog.css +183 -0
  421. package/src/styles/components/dataframe.css +382 -0
  422. package/src/styles/components/date-picker.css +243 -0
  423. package/src/styles/components/datetime-picker.css +229 -0
  424. package/src/styles/components/divider.css +63 -0
  425. package/src/styles/components/dose-calculator.css +301 -0
  426. package/src/styles/components/dropdown-button.css +280 -0
  427. package/src/styles/components/empty-state.css +151 -0
  428. package/src/styles/components/experiment-code-badge.css +33 -0
  429. package/src/styles/components/experiment-data-viewer.css +138 -0
  430. package/src/styles/components/experiment-popover.css +562 -0
  431. package/src/styles/components/experiment-selector-modal.css +285 -0
  432. package/src/styles/components/experiment-timeline.css +529 -0
  433. package/src/styles/components/file-uploader.css +310 -0
  434. package/src/styles/components/fit-panel.css +67 -0
  435. package/src/styles/components/form-builder.css +69 -0
  436. package/src/styles/components/form-field.css +48 -0
  437. package/src/styles/components/formula-input.css +103 -0
  438. package/src/styles/components/group-assigner.css +200 -0
  439. package/src/styles/components/grouping-modal.css +323 -0
  440. package/src/styles/components/icon-button.css +192 -0
  441. package/src/styles/components/input.css +66 -0
  442. package/src/styles/components/loading-spinner.css +67 -0
  443. package/src/styles/components/modal.css +350 -0
  444. package/src/styles/components/molecule-input.css +186 -0
  445. package/src/styles/components/multi-select.css +131 -0
  446. package/src/styles/components/number-input.css +199 -0
  447. package/src/styles/components/pill.css +188 -0
  448. package/src/styles/components/plate-map-editor.css +464 -0
  449. package/src/styles/components/progress-bar.css +133 -0
  450. package/src/styles/components/protocol-step-editor.css +449 -0
  451. package/src/styles/components/rack-editor.css +265 -0
  452. package/src/styles/components/radio-group.css +240 -0
  453. package/src/styles/components/reagent-editor.css +510 -0
  454. package/src/styles/components/reagent-list.css +407 -0
  455. package/src/styles/components/resource-card.css +360 -0
  456. package/src/styles/components/sample-hierarchy-tree.css +314 -0
  457. package/src/styles/components/sample-legend.css +201 -0
  458. package/src/styles/components/sample-selector.css +751 -0
  459. package/src/styles/components/schedule-calendar.css +478 -0
  460. package/src/styles/components/scientific-number.css +63 -0
  461. package/src/styles/components/segmented-control.css +197 -0
  462. package/src/styles/components/select.css +77 -0
  463. package/src/styles/components/sequence-input.css +184 -0
  464. package/src/styles/components/settings-button.css +94 -0
  465. package/src/styles/components/settings-modal.css +95 -0
  466. package/src/styles/components/skeleton.css +49 -0
  467. package/src/styles/components/slider.css +74 -0
  468. package/src/styles/components/status-indicator.css +66 -0
  469. package/src/styles/components/step-wizard.css +192 -0
  470. package/src/styles/components/tabs.css +95 -0
  471. package/src/styles/components/tags-input.css +195 -0
  472. package/src/styles/components/textarea.css +82 -0
  473. package/src/styles/components/theme-toggle.css +69 -0
  474. package/src/styles/components/time-picker.css +171 -0
  475. package/src/styles/components/time-range-input.css +42 -0
  476. package/src/styles/components/toast.css +91 -0
  477. package/src/styles/components/toggle.css +146 -0
  478. package/src/styles/components/tooltip.css +91 -0
  479. package/src/styles/components/unit-input.css +123 -0
  480. package/src/styles/components/well-edit-popup.css +252 -0
  481. package/src/styles/components/well-plate.css +307 -0
  482. package/src/styles/index.css +87 -0
  483. package/src/styles/variables.css +1117 -0
  484. package/src/tailwind.preset.ts +61 -0
  485. package/src/types/auth.ts +55 -0
  486. package/src/types/auto-group.ts +40 -0
  487. package/src/types/components.ts +710 -0
  488. package/src/types/form-builder.ts +197 -0
  489. package/src/types/index.ts +207 -0
  490. package/src/types/platform.ts +116 -0
  491. package/src/utils/color.ts +96 -0
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Color utilities for deriving same-hue-family shades.
3
+ *
4
+ * Used by SampleSelector and other group/legend UIs where a parent color
5
+ * needs to seed a gradient of related shades for its children.
6
+ */
7
+ export interface Hsl {
8
+ h: number;
9
+ s: number;
10
+ l: number;
11
+ }
12
+ export declare function hexToHsl(hex: string): Hsl;
13
+ export declare function hslToHex(h: number, s: number, l: number): string;
14
+ /**
15
+ * Derive a same-hue-family shade of `parentHex` for child at `index` of `total`.
16
+ *
17
+ * Lightness is distributed evenly within [35, 75] so all children remain
18
+ * readable on light/dark backgrounds. Hue and saturation are inherited from
19
+ * the parent. With `total <= 1`, returns the parent unchanged.
20
+ *
21
+ * Children are ordered light → dark by index, so index 0 is the brightest
22
+ * sibling and index `total - 1` is the darkest.
23
+ */
24
+ export declare function deriveShade(parentHex: string, index: number, total: number): string;
package/package.json ADDED
@@ -0,0 +1,114 @@
1
+ {
2
+ "name": "@morscherlab/mint-sdk",
3
+ "version": "1.0.0-alpha.2",
4
+ "description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec INtegrated Toolkit.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "sideEffects": [
10
+ "*.css",
11
+ "./dist/styles.css"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ },
18
+ "./install": {
19
+ "types": "./dist/install.d.ts",
20
+ "import": "./dist/install.js"
21
+ },
22
+ "./components": {
23
+ "types": "./dist/components/index.d.ts",
24
+ "import": "./dist/components/index.js"
25
+ },
26
+ "./composables": {
27
+ "types": "./dist/composables/index.d.ts",
28
+ "import": "./dist/composables/index.js"
29
+ },
30
+ "./stores": {
31
+ "types": "./dist/stores/index.d.ts",
32
+ "import": "./dist/stores/index.js"
33
+ },
34
+ "./types": {
35
+ "types": "./dist/types/index.d.ts",
36
+ "import": "./dist/types/index.js"
37
+ },
38
+ "./styles": "./dist/styles.css",
39
+ "./tailwind.preset": {
40
+ "types": "./dist/tailwind.preset.d.ts",
41
+ "import": "./dist/tailwind.preset.js"
42
+ }
43
+ },
44
+ "files": [
45
+ "dist",
46
+ "src"
47
+ ],
48
+ "scripts": {
49
+ "build": "vite build && vue-tsc --emitDeclarationOnly",
50
+ "dev": "vite build --watch",
51
+ "typecheck": "vue-tsc --noEmit",
52
+ "clean": "rm -rf dist",
53
+ "test": "vitest run",
54
+ "story:dev": "histoire dev",
55
+ "story:build": "histoire build",
56
+ "story:preview": "histoire preview"
57
+ },
58
+ "peerDependencies": {
59
+ "@simplewebauthn/browser": "^10.0.0",
60
+ "pinia": "^2.1.0 || ^3.0.0",
61
+ "tailwindcss": "^4.1.0",
62
+ "vue": "^3.4.0",
63
+ "vue-router": "^4.2.0"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "@simplewebauthn/browser": {
67
+ "optional": true
68
+ },
69
+ "vue-router": {
70
+ "optional": true
71
+ }
72
+ },
73
+ "dependencies": {
74
+ "@floating-ui/dom": "^1.7.6",
75
+ "axios": "1.13.6"
76
+ },
77
+ "devDependencies": {
78
+ "@histoire/plugin-vue": "1.0.0-beta.1",
79
+ "@simplewebauthn/browser": "10.0.0",
80
+ "@simplewebauthn/types": "10.0.0",
81
+ "@types/node": "22.19.13",
82
+ "@vitejs/plugin-vue": "6.0.5",
83
+ "@vue/test-utils": "2.4.6",
84
+ "happy-dom": "15.11.7",
85
+ "histoire": "1.0.0-beta.1",
86
+ "typescript": "5.9.3",
87
+ "vite": "8.0.0",
88
+ "vite-plugin-dts": "4.5.4",
89
+ "vitest": "4.1.0",
90
+ "vue-router": "4.6.4",
91
+ "vue-tsc": "2.2.12"
92
+ },
93
+ "repository": {
94
+ "type": "git",
95
+ "url": "https://github.com/MorscherLab/mld",
96
+ "directory": "packages/sdk-frontend"
97
+ },
98
+ "keywords": [
99
+ "vue",
100
+ "vue3",
101
+ "sdk",
102
+ "mint",
103
+ "mass-spec",
104
+ "metabolomics",
105
+ "plugin",
106
+ "components"
107
+ ],
108
+ "author": "MorscherLab",
109
+ "license": "MIT",
110
+ "publishConfig": {
111
+ "access": "public",
112
+ "registry": "https://registry.npmjs.org"
113
+ }
114
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Shared helpers for story files that need experiment selector integration.
3
+ * Provides mock data, API interceptors, and a provider wrapper component.
4
+ */
5
+ import { defineComponent, onUnmounted } from 'vue'
6
+ import { useAppExperiment } from '../composables/useAppExperiment'
7
+ import { useApi } from '../composables/useApi'
8
+ import type { ExperimentSummary } from '../types'
9
+
10
+ export const mockExperiments: ExperimentSummary[] = [
11
+ { id: 1, experiment_code: 'DR-HeLa-DOX-001', name: 'Doxorubicin dose-response in HeLa cells', status: 'completed', experiment_type: 'dose_response', project: 'Oncology Screen Q1', project_name: 'Oncology Screen Q1', created_at: '2026-02-28T14:30:00Z', updated_at: '2026-03-01T09:15:00Z', has_design_data: true },
12
+ { id: 2, experiment_code: 'CV-MCF7-24H-002', name: 'MCF-7 cell viability 24h time course', status: 'completed', experiment_type: 'cell_viability', project: 'Oncology Screen Q1', project_name: 'Oncology Screen Q1', created_at: '2026-02-25T10:00:00Z', updated_at: '2026-02-26T16:45:00Z', has_design_data: true },
13
+ { id: 3, experiment_code: 'DR-PANC1-GEM-003', name: 'Gemcitabine IC50 in PANC-1', status: 'ongoing', experiment_type: 'dose_response', project: 'Pancreatic Cancer', project_name: 'Pancreatic Cancer', created_at: '2026-03-01T08:00:00Z', updated_at: '2026-03-02T11:30:00Z', has_design_data: true },
14
+ { id: 4, experiment_code: 'CV-A549-CIS-004', name: 'Cisplatin cytotoxicity A549 lung', status: 'ongoing', experiment_type: 'cell_viability', project: 'Lung Cancer Panel', project_name: 'Lung Cancer Panel', created_at: '2026-03-01T13:00:00Z', updated_at: '2026-03-02T08:00:00Z', has_design_data: false },
15
+ { id: 5, experiment_code: 'DR-HEK293-CTRL-005', name: 'HEK-293T control viability baseline', status: 'completed', experiment_type: 'dose_response', project: 'Toxicity Controls', project_name: 'Toxicity Controls', created_at: '2026-02-20T09:00:00Z', updated_at: '2026-02-22T17:00:00Z', has_design_data: true },
16
+ ]
17
+
18
+ const mockExperimentTypes = [
19
+ { name: 'dose_response', color: '#3b82f6' },
20
+ { name: 'cell_viability', color: '#10b981' },
21
+ ]
22
+
23
+ const mockProjects = [
24
+ { id: 1, name: 'Oncology Screen Q1' },
25
+ { id: 2, name: 'Pancreatic Cancer' },
26
+ { id: 3, name: 'Lung Cancer Panel' },
27
+ { id: 4, name: 'Toxicity Controls' },
28
+ ]
29
+
30
+ export function installMockInterceptor() {
31
+ const { client } = useApi()
32
+ client.interceptors.request.use((config) => {
33
+ const url = config.url ?? ''
34
+ if (url.includes('/experiments/experiment-types')) {
35
+ config.adapter = () => Promise.resolve({ data: mockExperimentTypes, status: 200, statusText: 'OK', headers: {}, config })
36
+ return config
37
+ }
38
+ if (url.includes('/projects')) {
39
+ config.adapter = () => Promise.resolve({ data: mockProjects, status: 200, statusText: 'OK', headers: {}, config })
40
+ return config
41
+ }
42
+ if (url.startsWith('/api/experiments')) {
43
+ const params = new URLSearchParams(url.split('?')[1] ?? '')
44
+ const skip = Number(params.get('skip') ?? 0)
45
+ const limit = Number(params.get('limit') ?? 100)
46
+ let filtered = [...mockExperiments]
47
+ const status = params.get('status')
48
+ const search = params.get('search')?.toLowerCase()
49
+ if (status) filtered = filtered.filter(e => e.status === status)
50
+ if (search) filtered = filtered.filter(e => e.name.toLowerCase().includes(search) || e.experiment_code?.toLowerCase().includes(search))
51
+ config.adapter = () => Promise.resolve({ data: { experiments: filtered.slice(skip, skip + limit), total: filtered.length }, status: 200, statusText: 'OK', headers: {}, config })
52
+ }
53
+ return config
54
+ })
55
+ }
56
+
57
+ /**
58
+ * Wrapper component that fakes integrated mode and provides useAppExperiment context.
59
+ * Use in story files to demonstrate experiment selector integration.
60
+ *
61
+ * Props:
62
+ * preselect (boolean, default true) - pre-select the first mock experiment
63
+ */
64
+ export const ExperimentProvider = defineComponent({
65
+ props: { preselect: { type: Boolean, default: true } },
66
+ setup(props, { slots }) {
67
+ ;(window as any).__MINT_PLATFORM__ = { isIntegrated: true, theme: 'system' }
68
+ installMockInterceptor()
69
+ const { set } = useAppExperiment({
70
+ onSave: async () => 'Saved!',
71
+ })
72
+ if (props.preselect) {
73
+ set({
74
+ id: 1,
75
+ name: 'Doxorubicin dose-response in HeLa cells',
76
+ experiment_code: 'DR-HeLa-DOX-001',
77
+ status: 'completed',
78
+ })
79
+ }
80
+ onUnmounted(() => { delete (window as any).__MINT_PLATFORM__ })
81
+ return () => slots.default?.()
82
+ },
83
+ })
@@ -0,0 +1,163 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import AppLayout from '../../components/AppLayout.vue'
4
+
5
+ describe('AppLayout', () => {
6
+ describe('rendering with default props', () => {
7
+ it('should render layout with default classes', () => {
8
+ const wrapper = mount(AppLayout)
9
+ expect(wrapper.find('.mld-layout').exists()).toBe(true)
10
+ expect(wrapper.find('.mld-layout--sidebar-right').exists()).toBe(false)
11
+ expect(wrapper.find('.mld-layout--floating').exists()).toBe(false)
12
+ })
13
+
14
+ it('should render body container', () => {
15
+ const wrapper = mount(AppLayout)
16
+ expect(wrapper.find('.mld-layout__body').exists()).toBe(true)
17
+ })
18
+
19
+ it('should render main content slot', () => {
20
+ const wrapper = mount(AppLayout, {
21
+ slots: {
22
+ default: '<p>Main content</p>',
23
+ },
24
+ })
25
+ expect(wrapper.find('.mld-layout__main').exists()).toBe(true)
26
+ expect(wrapper.find('.mld-layout__main').text()).toBe('Main content')
27
+ })
28
+ })
29
+
30
+ describe('topbar slot', () => {
31
+ it('should render topbar when slot is provided', () => {
32
+ const wrapper = mount(AppLayout, {
33
+ slots: {
34
+ topbar: '<div class="custom-topbar">Topbar</div>',
35
+ },
36
+ })
37
+ expect(wrapper.find('.mld-layout__topbar').exists()).toBe(true)
38
+ expect(wrapper.find('.custom-topbar').text()).toBe('Topbar')
39
+ })
40
+
41
+ it('should not render topbar when slot is not provided', () => {
42
+ const wrapper = mount(AppLayout)
43
+ expect(wrapper.find('.mld-layout__topbar').exists()).toBe(false)
44
+ })
45
+ })
46
+
47
+ describe('sidebar slot', () => {
48
+ it('should render sidebar when slot is provided', () => {
49
+ const wrapper = mount(AppLayout, {
50
+ slots: {
51
+ sidebar: '<div class="custom-sidebar">Sidebar</div>',
52
+ },
53
+ })
54
+ expect(wrapper.find('.mld-layout__sidebar').exists()).toBe(true)
55
+ expect(wrapper.find('.custom-sidebar').text()).toBe('Sidebar')
56
+ })
57
+
58
+ it('should not render sidebar when slot is not provided', () => {
59
+ const wrapper = mount(AppLayout)
60
+ expect(wrapper.find('.mld-layout__sidebar').exists()).toBe(false)
61
+ })
62
+ })
63
+
64
+ describe('sidebarPosition prop', () => {
65
+ it('should default to left position', () => {
66
+ const wrapper = mount(AppLayout)
67
+ expect(wrapper.find('.mld-layout--sidebar-right').exists()).toBe(false)
68
+ })
69
+
70
+ it('should add right class when sidebarPosition is right', () => {
71
+ const wrapper = mount(AppLayout, {
72
+ props: { sidebarPosition: 'right' },
73
+ })
74
+ expect(wrapper.find('.mld-layout--sidebar-right').exists()).toBe(true)
75
+ })
76
+
77
+ it('should not add right class when sidebarPosition is left', () => {
78
+ const wrapper = mount(AppLayout, {
79
+ props: { sidebarPosition: 'left' },
80
+ })
81
+ expect(wrapper.find('.mld-layout--sidebar-right').exists()).toBe(false)
82
+ })
83
+ })
84
+
85
+ describe('floating prop', () => {
86
+ it('should not add floating class by default', () => {
87
+ const wrapper = mount(AppLayout)
88
+ expect(wrapper.find('.mld-layout--floating').exists()).toBe(false)
89
+ })
90
+
91
+ it('should add floating class when floating is true', () => {
92
+ const wrapper = mount(AppLayout, {
93
+ props: { floating: true },
94
+ })
95
+ expect(wrapper.find('.mld-layout--floating').exists()).toBe(true)
96
+ })
97
+ })
98
+
99
+ describe('sidebar width styling', () => {
100
+ it('should not apply inline width when default auto is used', () => {
101
+ const wrapper = mount(AppLayout, {
102
+ slots: { sidebar: '<div>Sidebar</div>' },
103
+ })
104
+ const sidebar = wrapper.find('.mld-layout__sidebar')
105
+ expect(sidebar.attributes('style')).toBeUndefined()
106
+ })
107
+
108
+ it('should apply custom sidebar width', () => {
109
+ const wrapper = mount(AppLayout, {
110
+ props: { sidebarWidth: '300px' },
111
+ slots: { sidebar: '<div>Sidebar</div>' },
112
+ })
113
+ const sidebar = wrapper.find('.mld-layout__sidebar')
114
+ expect(sidebar.attributes('style')).toContain('width: 300px')
115
+ })
116
+
117
+ it('should handle width with different CSS units', () => {
118
+ const wrapper = mount(AppLayout, {
119
+ props: { sidebarWidth: '20rem' },
120
+ slots: { sidebar: '<div>Sidebar</div>' },
121
+ })
122
+ const sidebar = wrapper.find('.mld-layout__sidebar')
123
+ expect(sidebar.attributes('style')).toContain('width: 20rem')
124
+ })
125
+ })
126
+
127
+ describe('integration: all slots', () => {
128
+ it('should render topbar, sidebar, and main content together', () => {
129
+ const wrapper = mount(AppLayout, {
130
+ slots: {
131
+ topbar: '<div class="topbar-content">Header</div>',
132
+ sidebar: '<div class="sidebar-content">Sidebar</div>',
133
+ default: '<div class="main-content">Content</div>',
134
+ },
135
+ })
136
+
137
+ expect(wrapper.find('.topbar-content').text()).toBe('Header')
138
+ expect(wrapper.find('.sidebar-content').text()).toBe('Sidebar')
139
+ expect(wrapper.find('.main-content').text()).toBe('Content')
140
+ })
141
+
142
+ it('should apply multiple modifier classes', () => {
143
+ const wrapper = mount(AppLayout, {
144
+ props: {
145
+ sidebarPosition: 'right',
146
+ floating: true,
147
+ },
148
+ })
149
+
150
+ const layout = wrapper.find('.mld-layout')
151
+ expect(layout.classes()).toContain('mld-layout--sidebar-right')
152
+ expect(layout.classes()).toContain('mld-layout--floating')
153
+ })
154
+ })
155
+
156
+ describe('edge cases', () => {
157
+ it('should handle empty default slot', () => {
158
+ const wrapper = mount(AppLayout)
159
+ expect(wrapper.find('.mld-layout__main').exists()).toBe(true)
160
+ expect(wrapper.find('.mld-layout__main').text()).toBe('')
161
+ })
162
+ })
163
+ })
@@ -0,0 +1,292 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import AppSidebar from '../../components/AppSidebar.vue'
4
+ import CollapsibleCard from '../../components/CollapsibleCard.vue'
5
+ import type { SidebarToolSection } from '../../types'
6
+
7
+ const samplePanels: Record<string, SidebarToolSection[]> = {
8
+ analysis: [
9
+ { id: 'parameters', label: 'Parameters', icon: '⚙' },
10
+ { id: 'filters', label: 'Filters', icon: '🔍', defaultOpen: false },
11
+ ],
12
+ results: [
13
+ { id: 'display', label: 'Display Options' },
14
+ ],
15
+ }
16
+
17
+ describe('AppSidebar', () => {
18
+ describe('rendering with default props', () => {
19
+ it('should render sidebar with default classes', () => {
20
+ const wrapper = mount(AppSidebar)
21
+ expect(wrapper.find('.mld-sidebar').exists()).toBe(true)
22
+ expect(wrapper.find('.mld-sidebar--left').exists()).toBe(true)
23
+ expect(wrapper.find('.mld-sidebar--floating').exists()).toBe(true)
24
+ })
25
+
26
+ it('should apply default width', () => {
27
+ const wrapper = mount(AppSidebar, {
28
+ props: { panels: samplePanels, activeView: 'analysis' },
29
+ })
30
+ expect(wrapper.find('.mld-sidebar').attributes('style')).toContain('width: 280px')
31
+ })
32
+
33
+ it('should be hidden when no panels match active view', () => {
34
+ const wrapper = mount(AppSidebar, {
35
+ props: { panels: samplePanels, activeView: 'settings' },
36
+ })
37
+ expect(wrapper.find('.mld-sidebar--hidden').exists()).toBe(true)
38
+ })
39
+
40
+ it('should be hidden when activeView is empty', () => {
41
+ const wrapper = mount(AppSidebar)
42
+ expect(wrapper.find('.mld-sidebar--hidden').exists()).toBe(true)
43
+ })
44
+ })
45
+
46
+ describe('side prop', () => {
47
+ it('should default to left side', () => {
48
+ const wrapper = mount(AppSidebar)
49
+ expect(wrapper.find('.mld-sidebar--left').exists()).toBe(true)
50
+ expect(wrapper.find('.mld-sidebar--right').exists()).toBe(false)
51
+ })
52
+
53
+ it('should apply right side class', () => {
54
+ const wrapper = mount(AppSidebar, {
55
+ props: { side: 'right' },
56
+ })
57
+ expect(wrapper.find('.mld-sidebar--right').exists()).toBe(true)
58
+ expect(wrapper.find('.mld-sidebar--left').exists()).toBe(false)
59
+ })
60
+ })
61
+
62
+ describe('floating prop', () => {
63
+ it('should apply floating class by default', () => {
64
+ const wrapper = mount(AppSidebar)
65
+ expect(wrapper.find('.mld-sidebar--floating').exists()).toBe(true)
66
+ expect(wrapper.find('.mld-sidebar--static').exists()).toBe(false)
67
+ })
68
+
69
+ it('should apply static class when floating is false', () => {
70
+ const wrapper = mount(AppSidebar, {
71
+ props: { floating: false },
72
+ })
73
+ expect(wrapper.find('.mld-sidebar--static').exists()).toBe(true)
74
+ expect(wrapper.find('.mld-sidebar--floating').exists()).toBe(false)
75
+ })
76
+ })
77
+
78
+ describe('width prop', () => {
79
+ it('should apply custom width', () => {
80
+ const wrapper = mount(AppSidebar, {
81
+ props: {
82
+ panels: samplePanels,
83
+ activeView: 'analysis',
84
+ width: '300px',
85
+ },
86
+ })
87
+ expect(wrapper.find('.mld-sidebar').attributes('style')).toContain('width: 300px')
88
+ })
89
+
90
+ it('should handle different CSS units', () => {
91
+ const wrapper = mount(AppSidebar, {
92
+ props: {
93
+ panels: samplePanels,
94
+ activeView: 'analysis',
95
+ width: '20rem',
96
+ },
97
+ })
98
+ expect(wrapper.find('.mld-sidebar').attributes('style')).toContain('width: 20rem')
99
+ })
100
+ })
101
+
102
+ describe('panels and activeView', () => {
103
+ it('should render sections for active view', () => {
104
+ const wrapper = mount(AppSidebar, {
105
+ props: { panels: samplePanels, activeView: 'analysis' },
106
+ global: { components: { CollapsibleCard } },
107
+ })
108
+ const cards = wrapper.findAllComponents(CollapsibleCard)
109
+ expect(cards).toHaveLength(2)
110
+ })
111
+
112
+ it('should render CollapsibleCard for each section', () => {
113
+ const wrapper = mount(AppSidebar, {
114
+ props: { panels: samplePanels, activeView: 'analysis' },
115
+ global: { components: { CollapsibleCard } },
116
+ })
117
+ const cards = wrapper.findAllComponents(CollapsibleCard)
118
+ expect(cards).toHaveLength(2)
119
+ })
120
+
121
+ it('should pass section label as title to CollapsibleCard', () => {
122
+ const wrapper = mount(AppSidebar, {
123
+ props: { panels: samplePanels, activeView: 'analysis' },
124
+ global: { components: { CollapsibleCard } },
125
+ })
126
+ const cards = wrapper.findAllComponents(CollapsibleCard)
127
+ expect(cards[0].props('title')).toBe('Parameters')
128
+ expect(cards[1].props('title')).toBe('Filters')
129
+ })
130
+
131
+ it('should pass section icon to CollapsibleCard', () => {
132
+ const wrapper = mount(AppSidebar, {
133
+ props: { panels: samplePanels, activeView: 'analysis' },
134
+ global: { components: { CollapsibleCard } },
135
+ })
136
+ const cards = wrapper.findAllComponents(CollapsibleCard)
137
+ expect(cards[0].props('icon')).toBe('⚙')
138
+ })
139
+
140
+ it('should respect defaultOpen on sections', () => {
141
+ const wrapper = mount(AppSidebar, {
142
+ props: { panels: samplePanels, activeView: 'analysis' },
143
+ global: { components: { CollapsibleCard } },
144
+ })
145
+ const cards = wrapper.findAllComponents(CollapsibleCard)
146
+ expect(cards[0].props('defaultOpen')).toBe(true)
147
+ expect(cards[1].props('defaultOpen')).toBe(false)
148
+ })
149
+
150
+ it('should switch sections when activeView changes', async () => {
151
+ const wrapper = mount(AppSidebar, {
152
+ props: { panels: samplePanels, activeView: 'analysis' },
153
+ global: { components: { CollapsibleCard } },
154
+ })
155
+ expect(wrapper.findAllComponents(CollapsibleCard)).toHaveLength(2)
156
+
157
+ await wrapper.setProps({ activeView: 'results' })
158
+ expect(wrapper.findAllComponents(CollapsibleCard)).toHaveLength(1)
159
+ })
160
+
161
+ it('should hide when switching to view with no panels', async () => {
162
+ const wrapper = mount(AppSidebar, {
163
+ props: { panels: samplePanels, activeView: 'analysis' },
164
+ global: { components: { CollapsibleCard } },
165
+ })
166
+ expect(wrapper.find('.mld-sidebar--hidden').exists()).toBe(false)
167
+
168
+ await wrapper.setProps({ activeView: 'settings' })
169
+ expect(wrapper.find('.mld-sidebar--hidden').exists()).toBe(true)
170
+ })
171
+ })
172
+
173
+ describe('section slots', () => {
174
+ it('should render named section slots', () => {
175
+ const wrapper = mount(AppSidebar, {
176
+ props: { panels: samplePanels, activeView: 'analysis' },
177
+ global: { components: { CollapsibleCard } },
178
+ slots: {
179
+ 'section-parameters': '<div class="params-content">Parameters content</div>',
180
+ 'section-filters': '<div class="filters-content">Filters content</div>',
181
+ },
182
+ })
183
+ expect(wrapper.find('.params-content').exists()).toBe(true)
184
+ expect(wrapper.find('.filters-content').exists()).toBe(true)
185
+ })
186
+
187
+ it('should not render section slots for inactive view', () => {
188
+ const wrapper = mount(AppSidebar, {
189
+ props: { panels: samplePanels, activeView: 'results' },
190
+ global: { components: { CollapsibleCard } },
191
+ slots: {
192
+ 'section-parameters': '<div class="params-content">Parameters content</div>',
193
+ 'section-display': '<div class="display-content">Display content</div>',
194
+ },
195
+ })
196
+ expect(wrapper.find('.params-content').exists()).toBe(false)
197
+ expect(wrapper.find('.display-content').exists()).toBe(true)
198
+ })
199
+ })
200
+
201
+ describe('slots', () => {
202
+ it('should render header slot', () => {
203
+ const wrapper = mount(AppSidebar, {
204
+ props: { panels: samplePanels, activeView: 'analysis' },
205
+ slots: {
206
+ header: '<div class="custom-header">Header</div>',
207
+ },
208
+ })
209
+ expect(wrapper.find('.mld-sidebar__header').exists()).toBe(true)
210
+ expect(wrapper.find('.custom-header').text()).toBe('Header')
211
+ })
212
+
213
+ it('should render footer slot', () => {
214
+ const wrapper = mount(AppSidebar, {
215
+ props: { panels: samplePanels, activeView: 'analysis' },
216
+ slots: {
217
+ footer: '<div class="custom-footer">Footer</div>',
218
+ },
219
+ })
220
+ expect(wrapper.find('.mld-sidebar__footer').exists()).toBe(true)
221
+ expect(wrapper.find('.custom-footer').text()).toBe('Footer')
222
+ })
223
+
224
+ it('should not render header when slot is not provided', () => {
225
+ const wrapper = mount(AppSidebar, {
226
+ props: { panels: samplePanels, activeView: 'analysis' },
227
+ })
228
+ expect(wrapper.find('.mld-sidebar__header').exists()).toBe(false)
229
+ })
230
+
231
+ it('should not render footer when slot is not provided', () => {
232
+ const wrapper = mount(AppSidebar, {
233
+ props: { panels: samplePanels, activeView: 'analysis' },
234
+ })
235
+ expect(wrapper.find('.mld-sidebar__footer').exists()).toBe(false)
236
+ })
237
+ })
238
+
239
+ describe('edge cases', () => {
240
+ it('should handle empty panels object', () => {
241
+ const wrapper = mount(AppSidebar, {
242
+ props: { panels: {}, activeView: 'analysis' },
243
+ })
244
+ expect(wrapper.find('.mld-sidebar--hidden').exists()).toBe(true)
245
+ })
246
+
247
+ it('should handle view with empty sections array', () => {
248
+ const wrapper = mount(AppSidebar, {
249
+ props: { panels: { empty: [] }, activeView: 'empty' },
250
+ })
251
+ expect(wrapper.find('.mld-sidebar--hidden').exists()).toBe(true)
252
+ })
253
+
254
+ it('should handle undefined activeView', () => {
255
+ const wrapper = mount(AppSidebar, {
256
+ props: { panels: samplePanels },
257
+ })
258
+ expect(wrapper.find('.mld-sidebar--hidden').exists()).toBe(true)
259
+ })
260
+ })
261
+
262
+ describe('integration: multiple classes', () => {
263
+ it('should apply multiple classes correctly', () => {
264
+ const wrapper = mount(AppSidebar, {
265
+ props: {
266
+ side: 'left',
267
+ floating: false,
268
+ },
269
+ })
270
+
271
+ const sidebar = wrapper.find('.mld-sidebar')
272
+ expect(sidebar.classes()).toContain('mld-sidebar--left')
273
+ expect(sidebar.classes()).toContain('mld-sidebar--static')
274
+ })
275
+
276
+ it('should render all slots with sections', () => {
277
+ const wrapper = mount(AppSidebar, {
278
+ props: { panels: samplePanels, activeView: 'analysis' },
279
+ global: { components: { CollapsibleCard } },
280
+ slots: {
281
+ header: '<div class="header">Header</div>',
282
+ footer: '<div class="footer">Footer</div>',
283
+ 'section-parameters': '<div>Params</div>',
284
+ },
285
+ })
286
+
287
+ expect(wrapper.find('.header').exists()).toBe(true)
288
+ expect(wrapper.find('.footer').exists()).toBe(true)
289
+ expect(wrapper.find('.mld-sidebar__sections').exists()).toBe(true)
290
+ })
291
+ })
292
+ })