@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,566 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import Calendar from '../../components/Calendar.vue'
4
+ import type { CalendarMarker } from '../../types'
5
+
6
+ describe('Calendar', () => {
7
+ describe('rendering with default props', () => {
8
+ it('should render calendar container', () => {
9
+ const wrapper = mount(Calendar)
10
+ expect(wrapper.find('.mld-calendar').exists()).toBe(true)
11
+ })
12
+
13
+ it('should show navigation by default', () => {
14
+ const wrapper = mount(Calendar)
15
+ expect(wrapper.find('.mld-calendar__header').exists()).toBe(true)
16
+ expect(wrapper.findAll('.mld-calendar__nav-btn')).toHaveLength(2)
17
+ })
18
+
19
+ it('should display current month and year', () => {
20
+ const wrapper = mount(Calendar, {
21
+ props: { month: 0, year: 2024 },
22
+ })
23
+ expect(wrapper.find('.mld-calendar__title').text()).toContain('January')
24
+ expect(wrapper.find('.mld-calendar__title').text()).toContain('2024')
25
+ })
26
+
27
+ it('should render weekday labels', () => {
28
+ const wrapper = mount(Calendar)
29
+ const weekdays = wrapper.findAll('.mld-calendar__weekday')
30
+ expect(weekdays).toHaveLength(7)
31
+ })
32
+
33
+ it('should render 42 days in fixed weeks mode', () => {
34
+ const wrapper = mount(Calendar, {
35
+ props: { fixedWeeks: true },
36
+ })
37
+ const days = wrapper.findAll('.mld-calendar__day')
38
+ expect(days).toHaveLength(42)
39
+ })
40
+
41
+ it('should render day grid', () => {
42
+ const wrapper = mount(Calendar)
43
+ expect(wrapper.find('.mld-calendar__grid').exists()).toBe(true)
44
+ })
45
+ })
46
+
47
+ describe('month navigation', () => {
48
+ it('should navigate to previous month', async () => {
49
+ const wrapper = mount(Calendar, {
50
+ props: { month: 1, year: 2024 },
51
+ })
52
+ const prevBtn = wrapper.findAll('.mld-calendar__nav-btn')[0]
53
+ await prevBtn.trigger('click')
54
+
55
+ expect(wrapper.emitted('update:month')).toHaveLength(1)
56
+ expect(wrapper.emitted('update:month')?.[0]).toEqual([0])
57
+ })
58
+
59
+ it('should navigate to next month', async () => {
60
+ const wrapper = mount(Calendar, {
61
+ props: { month: 0, year: 2024 },
62
+ })
63
+ const nextBtn = wrapper.findAll('.mld-calendar__nav-btn')[1]
64
+ await nextBtn.trigger('click')
65
+
66
+ expect(wrapper.emitted('update:month')).toHaveLength(1)
67
+ expect(wrapper.emitted('update:month')?.[0]).toEqual([1])
68
+ })
69
+
70
+ it('should navigate to previous year when going back from January', async () => {
71
+ const wrapper = mount(Calendar, {
72
+ props: { month: 0, year: 2024 },
73
+ })
74
+ const prevBtn = wrapper.findAll('.mld-calendar__nav-btn')[0]
75
+ await prevBtn.trigger('click')
76
+
77
+ expect(wrapper.emitted('update:month')?.[0]).toEqual([11])
78
+ expect(wrapper.emitted('update:year')?.[0]).toEqual([2023])
79
+ })
80
+
81
+ it('should navigate to next year when going forward from December', async () => {
82
+ const wrapper = mount(Calendar, {
83
+ props: { month: 11, year: 2024 },
84
+ })
85
+ const nextBtn = wrapper.findAll('.mld-calendar__nav-btn')[1]
86
+ await nextBtn.trigger('click')
87
+
88
+ expect(wrapper.emitted('update:month')?.[0]).toEqual([0])
89
+ expect(wrapper.emitted('update:year')?.[0]).toEqual([2025])
90
+ })
91
+
92
+ it('should emit navigate event with direction', async () => {
93
+ const wrapper = mount(Calendar, {
94
+ props: { month: 5, year: 2024 },
95
+ })
96
+ const prevBtn = wrapper.findAll('.mld-calendar__nav-btn')[0]
97
+ await prevBtn.trigger('click')
98
+
99
+ expect(wrapper.emitted('navigate')).toHaveLength(1)
100
+ expect(wrapper.emitted('navigate')?.[0]).toEqual(['prev', 4, 2024])
101
+ })
102
+
103
+ it('should hide navigation when showNavigation is false', () => {
104
+ const wrapper = mount(Calendar, {
105
+ props: { showNavigation: false },
106
+ })
107
+ expect(wrapper.find('.mld-calendar__header').exists()).toBe(false)
108
+ })
109
+ })
110
+
111
+ describe('single date selection', () => {
112
+ it('should emit selected date on day click', async () => {
113
+ const wrapper = mount(Calendar, {
114
+ props: { selectionMode: 'single', month: 0, year: 2024 },
115
+ })
116
+ const days = wrapper.findAll('.mld-calendar__day')
117
+ await days[15].trigger('click') // Click a day
118
+
119
+ expect(wrapper.emitted('update:modelValue')).toHaveLength(1)
120
+ const emittedDate = wrapper.emitted('update:modelValue')?.[0]?.[0] as Date
121
+ expect(emittedDate).toBeInstanceOf(Date)
122
+ })
123
+
124
+ it('should mark selected date', () => {
125
+ const selectedDate = new Date(2024, 0, 15)
126
+ const wrapper = mount(Calendar, {
127
+ props: {
128
+ selectionMode: 'single',
129
+ modelValue: selectedDate,
130
+ month: 0,
131
+ year: 2024,
132
+ },
133
+ })
134
+ const selectedDays = wrapper.findAll('.mld-calendar__day--selected')
135
+ expect(selectedDays.length).toBeGreaterThan(0)
136
+ })
137
+
138
+ it('should emit day-click event', async () => {
139
+ const wrapper = mount(Calendar, {
140
+ props: { selectionMode: 'single', month: 0, year: 2024 },
141
+ })
142
+ const days = wrapper.findAll('.mld-calendar__day')
143
+ await days[10].trigger('click')
144
+
145
+ expect(wrapper.emitted('day-click')).toHaveLength(1)
146
+ const dayContext = wrapper.emitted('day-click')?.[0]?.[0]
147
+ expect(dayContext).toHaveProperty('date')
148
+ expect(dayContext).toHaveProperty('isToday')
149
+ expect(dayContext).toHaveProperty('isSelected')
150
+ })
151
+ })
152
+
153
+ describe('range selection', () => {
154
+ it('should select range start on first click', async () => {
155
+ const wrapper = mount(Calendar, {
156
+ props: { selectionMode: 'range', month: 0, year: 2024 },
157
+ })
158
+ const days = wrapper.findAll('.mld-calendar__day')
159
+ await days[10].trigger('click')
160
+
161
+ // First click doesn't emit yet (waiting for end date)
162
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
163
+ })
164
+
165
+ it('should emit range on second click', async () => {
166
+ const wrapper = mount(Calendar, {
167
+ props: { selectionMode: 'range', month: 0, year: 2024 },
168
+ })
169
+ const days = wrapper.findAll('.mld-calendar__day')
170
+ await days[10].trigger('click')
171
+ await days[15].trigger('click')
172
+
173
+ expect(wrapper.emitted('update:modelValue')).toHaveLength(1)
174
+ const range = wrapper.emitted('update:modelValue')?.[0]?.[0] as { start: Date; end: Date }
175
+ expect(range).toHaveProperty('start')
176
+ expect(range).toHaveProperty('end')
177
+ expect(range.start).toBeInstanceOf(Date)
178
+ expect(range.end).toBeInstanceOf(Date)
179
+ })
180
+
181
+ it('should swap start and end if end is before start', async () => {
182
+ const wrapper = mount(Calendar, {
183
+ props: { selectionMode: 'range', month: 0, year: 2024 },
184
+ })
185
+ const days = wrapper.findAll('.mld-calendar__day')
186
+ await days[15].trigger('click')
187
+ await days[10].trigger('click')
188
+
189
+ const range = wrapper.emitted('update:modelValue')?.[0]?.[0] as { start: Date; end: Date }
190
+ expect(range.start.getTime()).toBeLessThan(range.end.getTime())
191
+ })
192
+
193
+ it('should mark dates in range', () => {
194
+ const start = new Date(2024, 0, 10)
195
+ const end = new Date(2024, 0, 15)
196
+ const wrapper = mount(Calendar, {
197
+ props: {
198
+ selectionMode: 'range',
199
+ modelValue: { start, end },
200
+ month: 0,
201
+ year: 2024,
202
+ },
203
+ })
204
+ const inRangeDays = wrapper.findAll('.mld-calendar__day--in-range')
205
+ expect(inRangeDays.length).toBeGreaterThan(0)
206
+ })
207
+ })
208
+
209
+ describe('multiple selection', () => {
210
+ it('should add dates to selection array', async () => {
211
+ const wrapper = mount(Calendar, {
212
+ props: { selectionMode: 'multiple', modelValue: [], month: 0, year: 2024 },
213
+ })
214
+ const days = wrapper.findAll('.mld-calendar__day')
215
+ await days[10].trigger('click')
216
+
217
+ expect(wrapper.emitted('update:modelValue')).toHaveLength(1)
218
+ const selected = wrapper.emitted('update:modelValue')?.[0]?.[0] as Date[]
219
+ expect(Array.isArray(selected)).toBe(true)
220
+ expect(selected).toHaveLength(1)
221
+ })
222
+
223
+ it('should remove date when clicking selected date', async () => {
224
+ const date1 = new Date(2024, 0, 10)
225
+ const date2 = new Date(2024, 0, 15)
226
+ const wrapper = mount(Calendar, {
227
+ props: {
228
+ selectionMode: 'multiple',
229
+ modelValue: [date1, date2],
230
+ month: 0,
231
+ year: 2024,
232
+ },
233
+ })
234
+
235
+ // Find and click the first selected date
236
+ const days = wrapper.findAll('.mld-calendar__day')
237
+ const dayToClick = days.find(d => d.classes().includes('mld-calendar__day--selected'))
238
+ if (dayToClick) {
239
+ await dayToClick.trigger('click')
240
+ const newSelection = wrapper.emitted('update:modelValue')?.[0]?.[0] as Date[]
241
+ expect(newSelection).toHaveLength(1)
242
+ }
243
+ })
244
+ })
245
+
246
+ describe('markers', () => {
247
+ it('should display markers on dates', () => {
248
+ const markers: CalendarMarker[] = [
249
+ { date: new Date(2024, 0, 15), color: '#ff0000', label: 'Event' },
250
+ ]
251
+ const wrapper = mount(Calendar, {
252
+ props: { markers, month: 0, year: 2024 },
253
+ })
254
+ expect(wrapper.find('.mld-calendar__marker').exists()).toBe(true)
255
+ })
256
+
257
+ it('should apply marker color', () => {
258
+ const markers: CalendarMarker[] = [
259
+ { date: new Date(2024, 0, 15), color: '#ff0000', label: 'Event' },
260
+ ]
261
+ const wrapper = mount(Calendar, {
262
+ props: { markers, month: 0, year: 2024 },
263
+ })
264
+ const marker = wrapper.find('.mld-calendar__marker')
265
+ expect(marker.attributes('style')).toContain('--marker-color')
266
+ })
267
+
268
+ it('should show marker label as title', () => {
269
+ const markers: CalendarMarker[] = [
270
+ { date: new Date(2024, 0, 15), label: 'Important Event' },
271
+ ]
272
+ const wrapper = mount(Calendar, {
273
+ props: { markers, month: 0, year: 2024 },
274
+ })
275
+ const marker = wrapper.find('.mld-calendar__marker')
276
+ expect(marker.attributes('title')).toBe('Important Event')
277
+ })
278
+
279
+ it('should limit markers to 3 per day', () => {
280
+ const markers: CalendarMarker[] = [
281
+ { date: new Date(2024, 0, 15), label: 'Event 1' },
282
+ { date: new Date(2024, 0, 15), label: 'Event 2' },
283
+ { date: new Date(2024, 0, 15), label: 'Event 3' },
284
+ { date: new Date(2024, 0, 15), label: 'Event 4' },
285
+ ]
286
+ const wrapper = mount(Calendar, {
287
+ props: { markers, month: 0, year: 2024 },
288
+ })
289
+ // Find the day with markers
290
+ const markersContainer = wrapper.find('.mld-calendar__markers')
291
+ const renderedMarkers = markersContainer.findAll('.mld-calendar__marker')
292
+ expect(renderedMarkers).toHaveLength(3)
293
+ })
294
+
295
+ it('should support different marker types', () => {
296
+ const markers: CalendarMarker[] = [
297
+ { date: new Date(2024, 0, 15), type: 'dot' },
298
+ { date: new Date(2024, 0, 16), type: 'bar' },
299
+ ]
300
+ const wrapper = mount(Calendar, {
301
+ props: { markers, month: 0, year: 2024 },
302
+ })
303
+ expect(wrapper.find('.mld-calendar__marker--dot').exists()).toBe(true)
304
+ expect(wrapper.find('.mld-calendar__marker--bar').exists()).toBe(true)
305
+ })
306
+ })
307
+
308
+ describe('disabled dates', () => {
309
+ it('should disable dates before minDate', () => {
310
+ const minDate = new Date(2024, 0, 15)
311
+ const wrapper = mount(Calendar, {
312
+ props: { minDate, month: 0, year: 2024 },
313
+ })
314
+ const days = wrapper.findAll('.mld-calendar__day')
315
+ // Days before 15th should be disabled
316
+ const firstDay = days[0]
317
+ expect(firstDay.attributes('disabled')).toBeDefined()
318
+ })
319
+
320
+ it('should disable dates after maxDate', () => {
321
+ const maxDate = new Date(2024, 0, 15)
322
+ const wrapper = mount(Calendar, {
323
+ props: { maxDate, month: 0, year: 2024 },
324
+ })
325
+ const days = wrapper.findAll('.mld-calendar__day')
326
+ // Days after 15th should be disabled (later in the array)
327
+ const lastDays = days.slice(-5)
328
+ const hasDisabled = lastDays.some(d => d.attributes('disabled') !== undefined)
329
+ expect(hasDisabled).toBe(true)
330
+ })
331
+
332
+ it('should disable dates in disabledDates array', () => {
333
+ const disabledDates = [new Date(2024, 0, 10), new Date(2024, 0, 20)]
334
+ const wrapper = mount(Calendar, {
335
+ props: { disabledDates, month: 0, year: 2024 },
336
+ })
337
+ const disabledDays = wrapper.findAll('.mld-calendar__day--disabled')
338
+ expect(disabledDays.length).toBeGreaterThan(0)
339
+ })
340
+
341
+ it('should use custom isDateDisabled function', () => {
342
+ const isWeekend = (date: Date) => {
343
+ const day = date.getDay()
344
+ return day === 0 || day === 6
345
+ }
346
+ const wrapper = mount(Calendar, {
347
+ props: { isDateDisabled: isWeekend, month: 0, year: 2024 },
348
+ })
349
+ const disabledDays = wrapper.findAll('.mld-calendar__day--disabled')
350
+ // Should have disabled weekend days
351
+ expect(disabledDays.length).toBeGreaterThan(0)
352
+ })
353
+
354
+ it('should not emit selection for disabled dates', async () => {
355
+ const minDate = new Date(2024, 0, 15)
356
+ const wrapper = mount(Calendar, {
357
+ props: { selectionMode: 'single', minDate, month: 0, year: 2024 },
358
+ })
359
+ const days = wrapper.findAll('.mld-calendar__day')
360
+ const disabledDay = days.find(d => d.attributes('disabled') !== undefined)
361
+
362
+ if (disabledDay) {
363
+ await disabledDay.trigger('click')
364
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
365
+ }
366
+ })
367
+ })
368
+
369
+ describe('week configuration', () => {
370
+ it('should start week on Monday by default', () => {
371
+ const wrapper = mount(Calendar, {
372
+ props: { weekStartsOn: 1 },
373
+ })
374
+ const weekdays = wrapper.findAll('.mld-calendar__weekday')
375
+ // First weekday should be Mon (depends on locale)
376
+ expect(weekdays[0].text()).toBeTruthy()
377
+ })
378
+
379
+ it('should start week on Sunday', () => {
380
+ const wrapper = mount(Calendar, {
381
+ props: { weekStartsOn: 0 },
382
+ })
383
+ const weekdays = wrapper.findAll('.mld-calendar__weekday')
384
+ expect(weekdays).toHaveLength(7)
385
+ })
386
+ })
387
+
388
+ describe('outside days', () => {
389
+ it('should mark days outside current month', () => {
390
+ const wrapper = mount(Calendar, {
391
+ props: { month: 0, year: 2024, showOutsideDays: true },
392
+ })
393
+ const outsideDays = wrapper.findAll('.mld-calendar__day--outside')
394
+ expect(outsideDays.length).toBeGreaterThan(0)
395
+ })
396
+
397
+ it('should not select outside days when showOutsideDays is false', async () => {
398
+ const wrapper = mount(Calendar, {
399
+ props: {
400
+ selectionMode: 'single',
401
+ month: 0,
402
+ year: 2024,
403
+ showOutsideDays: false,
404
+ },
405
+ })
406
+ const outsideDay = wrapper.find('.mld-calendar__day--outside')
407
+ if (outsideDay.exists()) {
408
+ await outsideDay.trigger('click')
409
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
410
+ }
411
+ })
412
+ })
413
+
414
+ describe('today marker', () => {
415
+ it('should mark today\'s date', () => {
416
+ const today = new Date()
417
+ const wrapper = mount(Calendar, {
418
+ props: { month: today.getMonth(), year: today.getFullYear() },
419
+ })
420
+ const todayDay = wrapper.find('.mld-calendar__day--today')
421
+ expect(todayDay.exists()).toBe(true)
422
+ })
423
+ })
424
+
425
+ describe('locale support', () => {
426
+ it('should use default en-US locale', () => {
427
+ const wrapper = mount(Calendar, {
428
+ props: { month: 0, year: 2024 },
429
+ })
430
+ expect(wrapper.find('.mld-calendar__title').text()).toContain('January')
431
+ })
432
+
433
+ it('should accept custom locale', () => {
434
+ const wrapper = mount(Calendar, {
435
+ props: { month: 0, year: 2024, locale: 'de-DE' },
436
+ })
437
+ const title = wrapper.find('.mld-calendar__title').text()
438
+ expect(title).toBeTruthy()
439
+ })
440
+ })
441
+
442
+ describe('accessibility', () => {
443
+ it('should have button type on day cells', () => {
444
+ const wrapper = mount(Calendar)
445
+ const days = wrapper.findAll('.mld-calendar__day')
446
+ days.forEach(day => {
447
+ expect(day.attributes('type')).toBe('button')
448
+ })
449
+ })
450
+
451
+ it('should have aria-label on navigation buttons', () => {
452
+ const wrapper = mount(Calendar)
453
+ const navButtons = wrapper.findAll('.mld-calendar__nav-btn')
454
+ expect(navButtons[0].attributes('aria-label')).toBe('Previous month')
455
+ expect(navButtons[1].attributes('aria-label')).toBe('Next month')
456
+ })
457
+
458
+ it('should disable button element for disabled dates', () => {
459
+ const minDate = new Date(2024, 0, 15)
460
+ const wrapper = mount(Calendar, {
461
+ props: { minDate, month: 0, year: 2024 },
462
+ })
463
+ const disabledDay = wrapper.find('.mld-calendar__day--disabled')
464
+ expect(disabledDay.attributes('disabled')).toBeDefined()
465
+ })
466
+ })
467
+
468
+ describe('selection mode none', () => {
469
+ it('should not emit selection when mode is none', async () => {
470
+ const wrapper = mount(Calendar, {
471
+ props: { selectionMode: 'none', month: 0, year: 2024 },
472
+ })
473
+ const days = wrapper.findAll('.mld-calendar__day')
474
+ await days[10].trigger('click')
475
+
476
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
477
+ })
478
+
479
+ it('should still emit day-click event in none mode', async () => {
480
+ const wrapper = mount(Calendar, {
481
+ props: { selectionMode: 'none', month: 0, year: 2024 },
482
+ })
483
+ const days = wrapper.findAll('.mld-calendar__day')
484
+ await days[10].trigger('click')
485
+
486
+ expect(wrapper.emitted('day-click')).toHaveLength(1)
487
+ })
488
+ })
489
+
490
+ describe('slots', () => {
491
+ it('should support custom header slot', () => {
492
+ const wrapper = mount(Calendar, {
493
+ slots: {
494
+ header: '<div class="custom-header">Custom Header</div>',
495
+ },
496
+ })
497
+ expect(wrapper.find('.custom-header').exists()).toBe(true)
498
+ expect(wrapper.find('.custom-header').text()).toBe('Custom Header')
499
+ })
500
+
501
+ it('should support custom week-day slot', () => {
502
+ const wrapper = mount(Calendar, {
503
+ slots: {
504
+ 'week-day': '<span class="custom-weekday">X</span>',
505
+ },
506
+ })
507
+ expect(wrapper.find('.custom-weekday').exists()).toBe(true)
508
+ })
509
+
510
+ it('should support custom day-content slot', () => {
511
+ const wrapper = mount(Calendar, {
512
+ slots: {
513
+ 'day-content': '<div class="custom-day">Custom</div>',
514
+ },
515
+ })
516
+ expect(wrapper.find('.custom-day').exists()).toBe(true)
517
+ })
518
+ })
519
+
520
+ describe('edge cases', () => {
521
+ it('should handle year boundaries correctly', async () => {
522
+ const wrapper = mount(Calendar, {
523
+ props: { month: 11, year: 2023 },
524
+ })
525
+ const nextBtn = wrapper.findAll('.mld-calendar__nav-btn')[1]
526
+ await nextBtn.trigger('click')
527
+
528
+ expect(wrapper.emitted('update:year')?.[0]).toEqual([2024])
529
+ expect(wrapper.emitted('update:month')?.[0]).toEqual([0])
530
+ })
531
+
532
+ it('should handle string date inputs for markers', () => {
533
+ const markers: CalendarMarker[] = [
534
+ { date: '2024-01-15', label: 'Event' },
535
+ ]
536
+ const wrapper = mount(Calendar, {
537
+ props: { markers, month: 0, year: 2024 },
538
+ })
539
+ expect(wrapper.find('.mld-calendar__marker').exists()).toBe(true)
540
+ })
541
+
542
+ it('should handle string date inputs for disabled dates', () => {
543
+ const wrapper = mount(Calendar, {
544
+ props: {
545
+ disabledDates: ['2024-01-15', '2024-01-16'],
546
+ month: 0,
547
+ year: 2024,
548
+ },
549
+ })
550
+ const disabledDays = wrapper.findAll('.mld-calendar__day--disabled')
551
+ expect(disabledDays.length).toBeGreaterThan(0)
552
+ })
553
+
554
+ it('should handle minDate as string', () => {
555
+ const wrapper = mount(Calendar, {
556
+ props: {
557
+ minDate: '2024-01-15',
558
+ month: 0,
559
+ year: 2024,
560
+ },
561
+ })
562
+ const disabledDays = wrapper.findAll('.mld-calendar__day--disabled')
563
+ expect(disabledDays.length).toBeGreaterThan(0)
564
+ })
565
+ })
566
+ })