@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.
- package/README.md +326 -0
- package/dist/__stories__/experiment-helpers.d.ts +25 -0
- package/dist/__tests__/components/AppLayout.test.d.ts +1 -0
- package/dist/__tests__/components/AppSidebar.test.d.ts +1 -0
- package/dist/__tests__/components/AppTopBar.test.d.ts +1 -0
- package/dist/__tests__/components/BaseInput.test.d.ts +1 -0
- package/dist/__tests__/components/BasePill.test.d.ts +1 -0
- package/dist/__tests__/components/Calendar.test.d.ts +1 -0
- package/dist/__tests__/components/CollapsibleCard.test.d.ts +1 -0
- package/dist/__tests__/components/DataFrame.test.d.ts +1 -0
- package/dist/__tests__/components/DropdownButton.test.d.ts +1 -0
- package/dist/__tests__/composables/formBuilderRegistry.test.d.ts +1 -0
- package/dist/__tests__/composables/useAppExperiment.test.d.ts +1 -0
- package/dist/__tests__/composables/useAuth.test.d.ts +1 -0
- package/dist/__tests__/composables/useAutoGroup.test.d.ts +1 -0
- package/dist/__tests__/composables/useExperimentData.test.d.ts +13 -0
- package/dist/__tests__/composables/useExperimentSave.test.d.ts +1 -0
- package/dist/__tests__/composables/useForm.test.d.ts +1 -0
- package/dist/__tests__/composables/useFormBuilder.test.d.ts +1 -0
- package/dist/__tests__/composables/usePlatformContext.test.d.ts +1 -0
- package/dist/__tests__/composables/usePluginApi.test.d.ts +13 -0
- package/dist/__tests__/composables/usePluginConfig.test.d.ts +14 -0
- package/dist/__tests__/utils/color.test.d.ts +1 -0
- package/dist/auth-BYmxZdJl.js +297 -0
- package/dist/auth-BYmxZdJl.js.map +1 -0
- package/dist/components/AlertBox.vue.d.ts +34 -0
- package/dist/components/AppAvatarMenu.vue.d.ts +58 -0
- package/dist/components/AppContainer.vue.d.ts +28 -0
- package/dist/components/AppLayout.vue.d.ts +31 -0
- package/dist/components/AppPageSelector.vue.d.ts +43 -0
- package/dist/components/AppPillNav.vue.d.ts +11 -0
- package/dist/components/AppPluginSwitcher.vue.d.ts +38 -0
- package/dist/components/AppSidebar.vue.d.ts +47 -0
- package/dist/components/AppTopBar.vue.d.ts +111 -0
- package/dist/components/AuditTrail.vue.d.ts +38 -0
- package/dist/components/AutoGroupModal.vue.d.ts +124 -0
- package/dist/components/Avatar.vue.d.ts +14 -0
- package/dist/components/BaseButton.vue.d.ts +37 -0
- package/dist/components/BaseCheckbox.vue.d.ts +17 -0
- package/dist/components/BaseInput.vue.d.ts +34 -0
- package/dist/components/BaseModal.vue.d.ts +46 -0
- package/dist/components/BasePill.vue.d.ts +57 -0
- package/dist/components/BaseRadioGroup.vue.d.ts +21 -0
- package/dist/components/BaseSelect.vue.d.ts +20 -0
- package/dist/components/BaseSlider.vue.d.ts +22 -0
- package/dist/components/BaseTabs.vue.d.ts +14 -0
- package/dist/components/BaseTextarea.vue.d.ts +30 -0
- package/dist/components/BaseToggle.vue.d.ts +19 -0
- package/dist/components/BatchProgressList.vue.d.ts +43 -0
- package/dist/components/Breadcrumb.vue.d.ts +33 -0
- package/dist/components/Calendar.vue.d.ts +107 -0
- package/dist/components/ChartContainer.vue.d.ts +31 -0
- package/dist/components/ChemicalFormula.vue.d.ts +8 -0
- package/dist/components/CollapsibleCard.vue.d.ts +41 -0
- package/dist/components/ColorSlider.vue.d.ts +34 -0
- package/dist/components/ConcentrationInput.vue.d.ts +25 -0
- package/dist/components/ConfirmDialog.vue.d.ts +42 -0
- package/dist/components/DataFrame.vue.d.ts +107 -0
- package/dist/components/DatePicker.vue.d.ts +25 -0
- package/dist/components/DateTimePicker.vue.d.ts +30 -0
- package/dist/components/Divider.vue.d.ts +14 -0
- package/dist/components/DoseCalculator.vue.d.ts +19 -0
- package/dist/components/DropdownButton.vue.d.ts +47 -0
- package/dist/components/EmptyState.vue.d.ts +36 -0
- package/dist/components/ExperimentCodeBadge.vue.d.ts +14 -0
- package/dist/components/ExperimentDataViewer.vue.d.ts +29 -0
- package/dist/components/ExperimentPopover.vue.d.ts +32 -0
- package/dist/components/ExperimentSelectorModal.vue.d.ts +28 -0
- package/dist/components/ExperimentTimeline.vue.d.ts +44 -0
- package/dist/components/FileUploader.vue.d.ts +40 -0
- package/dist/components/FitPanel.vue.d.ts +46 -0
- package/dist/components/FormActions.vue.d.ts +33 -0
- package/dist/components/FormBuilder.vue.d.ts +287 -0
- package/dist/components/FormField.vue.d.ts +28 -0
- package/dist/components/FormFieldRenderer.vue.d.ts +31 -0
- package/dist/components/FormSection.vue.d.ts +43 -0
- package/dist/components/FormulaInput.vue.d.ts +25 -0
- package/dist/components/GroupAssigner.vue.d.ts +25 -0
- package/dist/components/GroupingModal.vue.d.ts +12 -0
- package/dist/components/IconButton.vue.d.ts +34 -0
- package/dist/components/LoadingSpinner.vue.d.ts +12 -0
- package/dist/components/MoleculeInput.vue.d.ts +27 -0
- package/dist/components/MultiSelect.vue.d.ts +19 -0
- package/dist/components/NumberInput.vue.d.ts +22 -0
- package/dist/components/PlateMapEditor.vue.d.ts +50 -0
- package/dist/components/ProgressBar.vue.d.ts +23 -0
- package/dist/components/ProtocolStepEditor.vue.d.ts +24 -0
- package/dist/components/RackEditor.vue.d.ts +40 -0
- package/dist/components/ReagentEditor.vue.d.ts +30 -0
- package/dist/components/ReagentList.vue.d.ts +32 -0
- package/dist/components/ResourceCard.vue.d.ts +50 -0
- package/dist/components/SampleHierarchyTree.vue.d.ts +26 -0
- package/dist/components/SampleLegend.vue.d.ts +32 -0
- package/dist/components/SampleSelector.vue.d.ts +29 -0
- package/dist/components/ScheduleCalendar.vue.d.ts +110 -0
- package/dist/components/ScientificNumber.vue.d.ts +14 -0
- package/dist/components/SegmentedControl.vue.d.ts +20 -0
- package/dist/components/SequenceInput.vue.d.ts +54 -0
- package/dist/components/SettingsButton.vue.d.ts +30 -0
- package/dist/components/SettingsModal.vue.d.ts +36 -0
- package/dist/components/Skeleton.vue.d.ts +11 -0
- package/dist/components/StatusIndicator.vue.d.ts +13 -0
- package/dist/components/StepWizard.vue.d.ts +65 -0
- package/dist/components/TagsInput.vue.d.ts +39 -0
- package/dist/components/ThemeToggle.vue.d.ts +7 -0
- package/dist/components/TimePicker.vue.d.ts +29 -0
- package/dist/components/TimeRangeInput.vue.d.ts +27 -0
- package/dist/components/ToastNotification.vue.d.ts +2 -0
- package/dist/components/Tooltip.vue.d.ts +35 -0
- package/dist/components/UnitInput.vue.d.ts +39 -0
- package/dist/components/WellEditPopup.vue.d.ts +25 -0
- package/dist/components/WellPlate.vue.d.ts +73 -0
- package/dist/components/index.d.ts +87 -0
- package/dist/components/index.js +3 -0
- package/dist/components-CKf-UpGi.js +15089 -0
- package/dist/components-CKf-UpGi.js.map +1 -0
- package/dist/composables/experiment-utils.d.ts +8 -0
- package/dist/composables/formBuilderRegistry.d.ts +13 -0
- package/dist/composables/index.d.ts +28 -0
- package/dist/composables/index.js +3 -0
- package/dist/composables/useApi.d.ts +20 -0
- package/dist/composables/useAppExperiment.d.ts +37 -0
- package/dist/composables/useAsync.d.ts +128 -0
- package/dist/composables/useAuth.d.ts +47 -0
- package/dist/composables/useAutoGroup.d.ts +106 -0
- package/dist/composables/useChemicalFormula.d.ts +21 -0
- package/dist/composables/useConcentrationUnits.d.ts +29 -0
- package/dist/composables/useDoseCalculator.d.ts +58 -0
- package/dist/composables/useExperimentData.d.ts +18 -0
- package/dist/composables/useExperimentSave.d.ts +36 -0
- package/dist/composables/useExperimentSelector.d.ts +30 -0
- package/dist/composables/useForm.d.ts +92 -0
- package/dist/composables/useFormBuilder.d.ts +24 -0
- package/dist/composables/usePasskey.d.ts +10 -0
- package/dist/composables/usePlatformContext.d.ts +131 -0
- package/dist/composables/usePluginApi.d.ts +29 -0
- package/dist/composables/usePluginConfig.d.ts +13 -0
- package/dist/composables/useProtocolTemplates.d.ts +44 -0
- package/dist/composables/useRackEditor.d.ts +31 -0
- package/dist/composables/useReagentSeries.d.ts +23 -0
- package/dist/composables/useScheduleDrag.d.ts +78 -0
- package/dist/composables/useSequenceUtils.d.ts +14 -0
- package/dist/composables/useTheme.d.ts +8 -0
- package/dist/composables/useTimeUtils.d.ts +29 -0
- package/dist/composables/useToast.d.ts +22 -0
- package/dist/composables/useWellPlateEditor.d.ts +33 -0
- package/dist/composables-D0QfFzq1.js +805 -0
- package/dist/composables-D0QfFzq1.js.map +1 -0
- package/dist/histoire.setup.d.ts +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/install.d.ts +16 -0
- package/dist/install.js +23 -0
- package/dist/install.js.map +1 -0
- package/dist/stores/auth.d.ts +146 -0
- package/dist/stores/index.d.ts +2 -0
- package/dist/stores/index.js +2 -0
- package/dist/stores/settings.d.ts +75 -0
- package/dist/styles.css +29728 -0
- package/dist/tailwind.preset.d.ts +58 -0
- package/dist/tailwind.preset.js +66 -0
- package/dist/tailwind.preset.js.map +1 -0
- package/dist/types/auth.d.ts +42 -0
- package/dist/types/auto-group.d.ts +34 -0
- package/dist/types/components.d.ts +528 -0
- package/dist/types/form-builder.d.ts +167 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +0 -0
- package/dist/types/platform.d.ts +75 -0
- package/dist/useScheduleDrag-DAJueTbK.js +7181 -0
- package/dist/useScheduleDrag-DAJueTbK.js.map +1 -0
- package/dist/utils/color.d.ts +24 -0
- package/package.json +114 -0
- package/src/__stories__/experiment-helpers.ts +83 -0
- package/src/__tests__/components/AppLayout.test.ts +163 -0
- package/src/__tests__/components/AppSidebar.test.ts +292 -0
- package/src/__tests__/components/AppTopBar.test.ts +683 -0
- package/src/__tests__/components/BaseInput.test.ts +99 -0
- package/src/__tests__/components/BasePill.test.ts +291 -0
- package/src/__tests__/components/Calendar.test.ts +566 -0
- package/src/__tests__/components/CollapsibleCard.test.ts +524 -0
- package/src/__tests__/components/DataFrame.test.ts +767 -0
- package/src/__tests__/components/DropdownButton.test.ts +471 -0
- package/src/__tests__/composables/formBuilderRegistry.test.ts +187 -0
- package/src/__tests__/composables/useAppExperiment.test.ts +560 -0
- package/src/__tests__/composables/useAuth.test.ts +188 -0
- package/src/__tests__/composables/useAutoGroup.test.ts +860 -0
- package/src/__tests__/composables/useExperimentData.test.ts +127 -0
- package/src/__tests__/composables/useExperimentSave.test.ts +347 -0
- package/src/__tests__/composables/useForm.test.ts +205 -0
- package/src/__tests__/composables/useFormBuilder.test.ts +917 -0
- package/src/__tests__/composables/usePlatformContext.test.ts +116 -0
- package/src/__tests__/composables/usePluginApi.test.ts +81 -0
- package/src/__tests__/composables/usePluginConfig.test.ts +176 -0
- package/src/__tests__/utils/color.test.ts +96 -0
- package/src/components/AlertBox.story.vue +204 -0
- package/src/components/AlertBox.vue +88 -0
- package/src/components/AppAvatarMenu.story.vue +155 -0
- package/src/components/AppAvatarMenu.vue +184 -0
- package/src/components/AppContainer.story.vue +104 -0
- package/src/components/AppContainer.vue +34 -0
- package/src/components/AppLayout.story.vue +292 -0
- package/src/components/AppLayout.vue +75 -0
- package/src/components/AppPageSelector.vue +159 -0
- package/src/components/AppPillNav.vue +66 -0
- package/src/components/AppPluginSwitcher.vue +241 -0
- package/src/components/AppSidebar.story.vue +309 -0
- package/src/components/AppSidebar.vue +119 -0
- package/src/components/AppTopBar.story.vue +304 -0
- package/src/components/AppTopBar.vue +661 -0
- package/src/components/AuditTrail.story.vue +163 -0
- package/src/components/AuditTrail.vue +151 -0
- package/src/components/AutoGroupModal.story.vue +273 -0
- package/src/components/AutoGroupModal.vue +566 -0
- package/src/components/Avatar.story.vue +115 -0
- package/src/components/Avatar.vue +79 -0
- package/src/components/BaseButton.story.vue +96 -0
- package/src/components/BaseButton.vue +73 -0
- package/src/components/BaseCheckbox.story.vue +73 -0
- package/src/components/BaseCheckbox.vue +69 -0
- package/src/components/BaseInput.story.vue +98 -0
- package/src/components/BaseInput.vue +74 -0
- package/src/components/BaseModal.story.vue +237 -0
- package/src/components/BaseModal.vue +182 -0
- package/src/components/BasePill.story.vue +142 -0
- package/src/components/BasePill.vue +89 -0
- package/src/components/BaseRadioGroup.story.vue +145 -0
- package/src/components/BaseRadioGroup.vue +124 -0
- package/src/components/BaseSelect.story.vue +120 -0
- package/src/components/BaseSelect.vue +71 -0
- package/src/components/BaseSlider.story.vue +122 -0
- package/src/components/BaseSlider.vue +126 -0
- package/src/components/BaseTabs.story.vue +127 -0
- package/src/components/BaseTabs.vue +59 -0
- package/src/components/BaseTextarea.story.vue +91 -0
- package/src/components/BaseTextarea.vue +62 -0
- package/src/components/BaseToggle.story.vue +81 -0
- package/src/components/BaseToggle.vue +76 -0
- package/src/components/BatchProgressList.story.vue +92 -0
- package/src/components/BatchProgressList.vue +184 -0
- package/src/components/Breadcrumb.story.vue +106 -0
- package/src/components/Breadcrumb.vue +75 -0
- package/src/components/Calendar.story.vue +106 -0
- package/src/components/Calendar.vue +363 -0
- package/src/components/ChartContainer.story.vue +113 -0
- package/src/components/ChartContainer.vue +64 -0
- package/src/components/ChemicalFormula.story.vue +102 -0
- package/src/components/ChemicalFormula.vue +39 -0
- package/src/components/CollapsibleCard.story.vue +135 -0
- package/src/components/CollapsibleCard.vue +167 -0
- package/src/components/ColorSlider.story.vue +120 -0
- package/src/components/ColorSlider.vue +164 -0
- package/src/components/ConcentrationInput.story.vue +77 -0
- package/src/components/ConcentrationInput.vue +185 -0
- package/src/components/ConfirmDialog.story.vue +248 -0
- package/src/components/ConfirmDialog.vue +93 -0
- package/src/components/DataFrame.story.vue +148 -0
- package/src/components/DataFrame.vue +419 -0
- package/src/components/DatePicker.story.vue +119 -0
- package/src/components/DatePicker.vue +330 -0
- package/src/components/DateTimePicker.story.vue +112 -0
- package/src/components/DateTimePicker.vue +392 -0
- package/src/components/Divider.story.vue +80 -0
- package/src/components/Divider.vue +49 -0
- package/src/components/DoseCalculator.story.vue +68 -0
- package/src/components/DoseCalculator.vue +476 -0
- package/src/components/DropdownButton.story.vue +102 -0
- package/src/components/DropdownButton.vue +181 -0
- package/src/components/EmptyState.story.vue +135 -0
- package/src/components/EmptyState.vue +69 -0
- package/src/components/ExperimentCodeBadge.story.vue +77 -0
- package/src/components/ExperimentCodeBadge.vue +64 -0
- package/src/components/ExperimentDataViewer.story.vue +174 -0
- package/src/components/ExperimentDataViewer.vue +288 -0
- package/src/components/ExperimentPopover.story.vue +384 -0
- package/src/components/ExperimentPopover.vue +241 -0
- package/src/components/ExperimentSelectorModal.story.vue +391 -0
- package/src/components/ExperimentSelectorModal.vue +387 -0
- package/src/components/ExperimentTimeline.story.vue +161 -0
- package/src/components/ExperimentTimeline.vue +382 -0
- package/src/components/FileUploader.story.vue +107 -0
- package/src/components/FileUploader.vue +386 -0
- package/src/components/FitPanel.story.vue +125 -0
- package/src/components/FitPanel.vue +120 -0
- package/src/components/FormActions.vue +92 -0
- package/src/components/FormBuilder.vue +214 -0
- package/src/components/FormField.story.vue +132 -0
- package/src/components/FormField.vue +59 -0
- package/src/components/FormFieldRenderer.vue +58 -0
- package/src/components/FormSection.vue +90 -0
- package/src/components/FormulaInput.story.vue +96 -0
- package/src/components/FormulaInput.vue +125 -0
- package/src/components/GroupAssigner.story.vue +83 -0
- package/src/components/GroupAssigner.vue +284 -0
- package/src/components/GroupingModal.story.vue +52 -0
- package/src/components/GroupingModal.vue +422 -0
- package/src/components/IconButton.story.vue +135 -0
- package/src/components/IconButton.vue +73 -0
- package/src/components/LoadingSpinner.story.vue +70 -0
- package/src/components/LoadingSpinner.vue +50 -0
- package/src/components/MoleculeInput.story.vue +66 -0
- package/src/components/MoleculeInput.vue +426 -0
- package/src/components/MultiSelect.story.vue +132 -0
- package/src/components/MultiSelect.vue +118 -0
- package/src/components/NumberInput.story.vue +122 -0
- package/src/components/NumberInput.vue +160 -0
- package/src/components/PlateMapEditor.story.vue +92 -0
- package/src/components/PlateMapEditor.vue +513 -0
- package/src/components/ProgressBar.story.vue +148 -0
- package/src/components/ProgressBar.vue +114 -0
- package/src/components/ProtocolStepEditor.story.vue +69 -0
- package/src/components/ProtocolStepEditor.vue +522 -0
- package/src/components/RackEditor.story.vue +100 -0
- package/src/components/RackEditor.vue +371 -0
- package/src/components/ReagentEditor.story.vue +153 -0
- package/src/components/ReagentEditor.vue +418 -0
- package/src/components/ReagentList.story.vue +137 -0
- package/src/components/ReagentList.vue +463 -0
- package/src/components/ResourceCard.story.vue +150 -0
- package/src/components/ResourceCard.vue +161 -0
- package/src/components/SampleHierarchyTree.story.vue +161 -0
- package/src/components/SampleHierarchyTree.vue +256 -0
- package/src/components/SampleLegend.story.vue +91 -0
- package/src/components/SampleLegend.vue +119 -0
- package/src/components/SampleSelector.story.vue +111 -0
- package/src/components/SampleSelector.vue +1033 -0
- package/src/components/ScheduleCalendar.story.vue +195 -0
- package/src/components/ScheduleCalendar.vue +569 -0
- package/src/components/ScientificNumber.story.vue +127 -0
- package/src/components/ScientificNumber.vue +197 -0
- package/src/components/SegmentedControl.story.vue +132 -0
- package/src/components/SegmentedControl.vue +79 -0
- package/src/components/SequenceInput.story.vue +119 -0
- package/src/components/SequenceInput.vue +209 -0
- package/src/components/SettingsButton.story.vue +58 -0
- package/src/components/SettingsButton.vue +76 -0
- package/src/components/SettingsModal.story.vue +145 -0
- package/src/components/SettingsModal.vue +146 -0
- package/src/components/Skeleton.story.vue +141 -0
- package/src/components/Skeleton.vue +74 -0
- package/src/components/StatusIndicator.story.vue +99 -0
- package/src/components/StatusIndicator.vue +40 -0
- package/src/components/StepWizard.story.vue +155 -0
- package/src/components/StepWizard.vue +223 -0
- package/src/components/TagsInput.story.vue +155 -0
- package/src/components/TagsInput.vue +265 -0
- package/src/components/ThemeToggle.story.vue +36 -0
- package/src/components/ThemeToggle.vue +54 -0
- package/src/components/TimePicker.story.vue +96 -0
- package/src/components/TimePicker.vue +273 -0
- package/src/components/TimeRangeInput.story.vue +104 -0
- package/src/components/TimeRangeInput.vue +122 -0
- package/src/components/ToastNotification.story.vue +157 -0
- package/src/components/ToastNotification.vue +62 -0
- package/src/components/Tooltip.story.vue +138 -0
- package/src/components/Tooltip.vue +119 -0
- package/src/components/UnitInput.story.vue +194 -0
- package/src/components/UnitInput.vue +213 -0
- package/src/components/WellEditPopup.vue +234 -0
- package/src/components/WellPlate.story.vue +282 -0
- package/src/components/WellPlate.vue +830 -0
- package/src/components/index.ts +118 -0
- package/src/composables/experiment-utils.ts +57 -0
- package/src/composables/formBuilderRegistry.ts +79 -0
- package/src/composables/index.ts +140 -0
- package/src/composables/useApi.ts +167 -0
- package/src/composables/useAppExperiment.ts +159 -0
- package/src/composables/useAsync.ts +323 -0
- package/src/composables/useAuth.ts +445 -0
- package/src/composables/useAutoGroup.ts +641 -0
- package/src/composables/useChemicalFormula.ts +275 -0
- package/src/composables/useConcentrationUnits.ts +246 -0
- package/src/composables/useDoseCalculator.ts +370 -0
- package/src/composables/useExperimentData.ts +86 -0
- package/src/composables/useExperimentSave.ts +192 -0
- package/src/composables/useExperimentSelector.ts +292 -0
- package/src/composables/useForm.ts +416 -0
- package/src/composables/useFormBuilder.ts +383 -0
- package/src/composables/usePasskey.ts +216 -0
- package/src/composables/usePlatformContext.ts +299 -0
- package/src/composables/usePluginApi.ts +39 -0
- package/src/composables/usePluginConfig.ts +93 -0
- package/src/composables/useProtocolTemplates.ts +518 -0
- package/src/composables/useRackEditor.ts +222 -0
- package/src/composables/useReagentSeries.ts +91 -0
- package/src/composables/useScheduleDrag.ts +245 -0
- package/src/composables/useSequenceUtils.ts +105 -0
- package/src/composables/useTheme.ts +58 -0
- package/src/composables/useTimeUtils.ts +131 -0
- package/src/composables/useToast.ts +40 -0
- package/src/composables/useWellPlateEditor.ts +421 -0
- package/src/histoire.setup.ts +17 -0
- package/src/index.ts +367 -0
- package/src/install.ts +32 -0
- package/src/stores/auth.ts +152 -0
- package/src/stores/index.ts +2 -0
- package/src/stores/settings.ts +218 -0
- package/src/styles/components/alert-box.css +150 -0
- package/src/styles/components/app-avatar-menu.css +155 -0
- package/src/styles/components/app-container.css +33 -0
- package/src/styles/components/app-layout.css +98 -0
- package/src/styles/components/app-page-selector.css +191 -0
- package/src/styles/components/app-pill-nav.css +57 -0
- package/src/styles/components/app-plugin-switcher.css +209 -0
- package/src/styles/components/app-sidebar.css +145 -0
- package/src/styles/components/app-top-bar.css +492 -0
- package/src/styles/components/audit-trail.css +143 -0
- package/src/styles/components/auto-group-modal.css +644 -0
- package/src/styles/components/avatar.css +73 -0
- package/src/styles/components/batch-progress-list.css +196 -0
- package/src/styles/components/breadcrumb.css +64 -0
- package/src/styles/components/button.css +188 -0
- package/src/styles/components/calendar.css +192 -0
- package/src/styles/components/chart-container.css +69 -0
- package/src/styles/components/checkbox.css +123 -0
- package/src/styles/components/chemical-formula.css +46 -0
- package/src/styles/components/collapsible-card.css +253 -0
- package/src/styles/components/color-slider.css +110 -0
- package/src/styles/components/concentration-input.css +156 -0
- package/src/styles/components/confirm-dialog.css +183 -0
- package/src/styles/components/dataframe.css +382 -0
- package/src/styles/components/date-picker.css +243 -0
- package/src/styles/components/datetime-picker.css +229 -0
- package/src/styles/components/divider.css +63 -0
- package/src/styles/components/dose-calculator.css +301 -0
- package/src/styles/components/dropdown-button.css +280 -0
- package/src/styles/components/empty-state.css +151 -0
- package/src/styles/components/experiment-code-badge.css +33 -0
- package/src/styles/components/experiment-data-viewer.css +138 -0
- package/src/styles/components/experiment-popover.css +562 -0
- package/src/styles/components/experiment-selector-modal.css +285 -0
- package/src/styles/components/experiment-timeline.css +529 -0
- package/src/styles/components/file-uploader.css +310 -0
- package/src/styles/components/fit-panel.css +67 -0
- package/src/styles/components/form-builder.css +69 -0
- package/src/styles/components/form-field.css +48 -0
- package/src/styles/components/formula-input.css +103 -0
- package/src/styles/components/group-assigner.css +200 -0
- package/src/styles/components/grouping-modal.css +323 -0
- package/src/styles/components/icon-button.css +192 -0
- package/src/styles/components/input.css +66 -0
- package/src/styles/components/loading-spinner.css +67 -0
- package/src/styles/components/modal.css +350 -0
- package/src/styles/components/molecule-input.css +186 -0
- package/src/styles/components/multi-select.css +131 -0
- package/src/styles/components/number-input.css +199 -0
- package/src/styles/components/pill.css +188 -0
- package/src/styles/components/plate-map-editor.css +464 -0
- package/src/styles/components/progress-bar.css +133 -0
- package/src/styles/components/protocol-step-editor.css +449 -0
- package/src/styles/components/rack-editor.css +265 -0
- package/src/styles/components/radio-group.css +240 -0
- package/src/styles/components/reagent-editor.css +510 -0
- package/src/styles/components/reagent-list.css +407 -0
- package/src/styles/components/resource-card.css +360 -0
- package/src/styles/components/sample-hierarchy-tree.css +314 -0
- package/src/styles/components/sample-legend.css +201 -0
- package/src/styles/components/sample-selector.css +751 -0
- package/src/styles/components/schedule-calendar.css +478 -0
- package/src/styles/components/scientific-number.css +63 -0
- package/src/styles/components/segmented-control.css +197 -0
- package/src/styles/components/select.css +77 -0
- package/src/styles/components/sequence-input.css +184 -0
- package/src/styles/components/settings-button.css +94 -0
- package/src/styles/components/settings-modal.css +95 -0
- package/src/styles/components/skeleton.css +49 -0
- package/src/styles/components/slider.css +74 -0
- package/src/styles/components/status-indicator.css +66 -0
- package/src/styles/components/step-wizard.css +192 -0
- package/src/styles/components/tabs.css +95 -0
- package/src/styles/components/tags-input.css +195 -0
- package/src/styles/components/textarea.css +82 -0
- package/src/styles/components/theme-toggle.css +69 -0
- package/src/styles/components/time-picker.css +171 -0
- package/src/styles/components/time-range-input.css +42 -0
- package/src/styles/components/toast.css +91 -0
- package/src/styles/components/toggle.css +146 -0
- package/src/styles/components/tooltip.css +91 -0
- package/src/styles/components/unit-input.css +123 -0
- package/src/styles/components/well-edit-popup.css +252 -0
- package/src/styles/components/well-plate.css +307 -0
- package/src/styles/index.css +87 -0
- package/src/styles/variables.css +1117 -0
- package/src/tailwind.preset.ts +61 -0
- package/src/types/auth.ts +55 -0
- package/src/types/auto-group.ts +40 -0
- package/src/types/components.ts +710 -0
- package/src/types/form-builder.ts +197 -0
- package/src/types/index.ts +207 -0
- package/src/types/platform.ts +116 -0
- package/src/utils/color.ts +96 -0
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
|
+
import { createPinia } from 'pinia'
|
|
5
|
+
import AppTopBar from '../../components/AppTopBar.vue'
|
|
6
|
+
import { usePlatformContext } from '../../composables/usePlatformContext'
|
|
7
|
+
import ThemeToggle from '../../components/ThemeToggle.vue'
|
|
8
|
+
import SettingsModal from '../../components/SettingsModal.vue'
|
|
9
|
+
import type { TopBarSettingsConfig } from '../../types/components'
|
|
10
|
+
|
|
11
|
+
// Mock the usePlatformContext composable
|
|
12
|
+
vi.mock('../../composables/usePlatformContext', () => ({
|
|
13
|
+
usePlatformContext: vi.fn(() => ({
|
|
14
|
+
isIntegrated: computed(() => false),
|
|
15
|
+
context: ref({ isIntegrated: false, theme: 'system' }),
|
|
16
|
+
plugin: computed(() => undefined),
|
|
17
|
+
user: computed(() => undefined),
|
|
18
|
+
theme: computed(() => 'system' as const),
|
|
19
|
+
features: computed(() => undefined),
|
|
20
|
+
navigate: vi.fn(),
|
|
21
|
+
notify: vi.fn(),
|
|
22
|
+
sendToPlatform: vi.fn(),
|
|
23
|
+
})),
|
|
24
|
+
}))
|
|
25
|
+
|
|
26
|
+
// Mock the useTheme composable (used by ThemeToggle)
|
|
27
|
+
vi.mock('../../composables/useTheme', () => ({
|
|
28
|
+
useTheme: vi.fn(() => ({
|
|
29
|
+
isDark: ref(false),
|
|
30
|
+
theme: ref('light'),
|
|
31
|
+
toggleTheme: vi.fn(),
|
|
32
|
+
})),
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
// Helper function to create test wrapper with all necessary stubs and plugins
|
|
36
|
+
function createWrapper(props = {}, slots = {}) {
|
|
37
|
+
const pinia = createPinia()
|
|
38
|
+
return mount(AppTopBar, {
|
|
39
|
+
props,
|
|
40
|
+
slots,
|
|
41
|
+
global: {
|
|
42
|
+
plugins: [pinia],
|
|
43
|
+
stubs: {
|
|
44
|
+
'router-link': {
|
|
45
|
+
template: '<a><slot /></a>',
|
|
46
|
+
},
|
|
47
|
+
BaseModal: {
|
|
48
|
+
template: '<div><slot /></div>',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
describe('AppTopBar', () => {
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
vi.clearAllMocks()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('default props behavior', () => {
|
|
61
|
+
it('should render without ThemeToggle by default', () => {
|
|
62
|
+
const wrapper = createWrapper({ title: 'Test App' })
|
|
63
|
+
expect(wrapper.findComponent(ThemeToggle).exists()).toBe(false)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should render without settings button by default', () => {
|
|
67
|
+
const wrapper = createWrapper({ title: 'Test App' })
|
|
68
|
+
expect(wrapper.find('.mld-topbar__settings-btn').exists()).toBe(false)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should render without SettingsModal by default', () => {
|
|
72
|
+
const wrapper = createWrapper({ title: 'Test App' })
|
|
73
|
+
expect(wrapper.findComponent(SettingsModal).exists()).toBe(false)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('should show standalone badge by default when not integrated', () => {
|
|
77
|
+
const wrapper = createWrapper({ title: 'Test App' })
|
|
78
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').exists()).toBe(true)
|
|
79
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').text()).toBe('Standalone')
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('showThemeToggle prop', () => {
|
|
84
|
+
it('should not render ThemeToggle when showThemeToggle is false', () => {
|
|
85
|
+
const wrapper = createWrapper({
|
|
86
|
+
title: 'Test App',
|
|
87
|
+
showThemeToggle: false,
|
|
88
|
+
})
|
|
89
|
+
expect(wrapper.findComponent(ThemeToggle).exists()).toBe(false)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should render ThemeToggle when showThemeToggle is true', () => {
|
|
93
|
+
const wrapper = createWrapper({
|
|
94
|
+
title: 'Test App',
|
|
95
|
+
showThemeToggle: true,
|
|
96
|
+
})
|
|
97
|
+
const themeToggle = wrapper.findComponent(ThemeToggle)
|
|
98
|
+
expect(themeToggle.exists()).toBe(true)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should pass size="sm" to ThemeToggle', () => {
|
|
102
|
+
const wrapper = createWrapper({
|
|
103
|
+
title: 'Test App',
|
|
104
|
+
showThemeToggle: true,
|
|
105
|
+
})
|
|
106
|
+
const themeToggle = wrapper.findComponent(ThemeToggle)
|
|
107
|
+
expect(themeToggle.props('size')).toBe('sm')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should render ThemeToggle in right section', () => {
|
|
111
|
+
const wrapper = createWrapper({
|
|
112
|
+
title: 'Test App',
|
|
113
|
+
showThemeToggle: true,
|
|
114
|
+
})
|
|
115
|
+
const rightSection = wrapper.find('.mld-topbar__right')
|
|
116
|
+
expect(rightSection.findComponent(ThemeToggle).exists()).toBe(true)
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
describe('showSettings prop', () => {
|
|
121
|
+
it('should not render settings button when showSettings is false', () => {
|
|
122
|
+
const wrapper = createWrapper({
|
|
123
|
+
title: 'Test App',
|
|
124
|
+
showSettings: false,
|
|
125
|
+
})
|
|
126
|
+
expect(wrapper.find('.mld-topbar__settings-btn').exists()).toBe(false)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should render settings button when showSettings is true', () => {
|
|
130
|
+
const wrapper = createWrapper({
|
|
131
|
+
title: 'Test App',
|
|
132
|
+
showSettings: true,
|
|
133
|
+
})
|
|
134
|
+
const settingsBtn = wrapper.find('.mld-topbar__settings-btn')
|
|
135
|
+
expect(settingsBtn.exists()).toBe(true)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('should render SettingsModal when showSettings is true', () => {
|
|
139
|
+
const wrapper = createWrapper({
|
|
140
|
+
title: 'Test App',
|
|
141
|
+
showSettings: true,
|
|
142
|
+
})
|
|
143
|
+
expect(wrapper.findComponent(SettingsModal).exists()).toBe(true)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('should have aria-label on settings button', () => {
|
|
147
|
+
const wrapper = createWrapper({
|
|
148
|
+
title: 'Test App',
|
|
149
|
+
showSettings: true,
|
|
150
|
+
})
|
|
151
|
+
const settingsBtn = wrapper.find('.mld-topbar__settings-btn')
|
|
152
|
+
expect(settingsBtn.attributes('aria-label')).toBe('Open settings')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('should render settings icon SVG', () => {
|
|
156
|
+
const wrapper = createWrapper({
|
|
157
|
+
title: 'Test App',
|
|
158
|
+
showSettings: true,
|
|
159
|
+
})
|
|
160
|
+
const settingsBtn = wrapper.find('.mld-topbar__settings-btn')
|
|
161
|
+
const icon = settingsBtn.find('.mld-topbar__settings-icon')
|
|
162
|
+
expect(icon.exists()).toBe(true)
|
|
163
|
+
expect(icon.element.tagName.toLowerCase()).toBe('svg')
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
describe('settings button interaction', () => {
|
|
168
|
+
it('should open SettingsModal when settings button is clicked', async () => {
|
|
169
|
+
const wrapper = createWrapper({
|
|
170
|
+
title: 'Test App',
|
|
171
|
+
showSettings: true,
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
const settingsBtn = wrapper.find('.mld-topbar__settings-btn')
|
|
175
|
+
await settingsBtn.trigger('click')
|
|
176
|
+
|
|
177
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
178
|
+
expect(settingsModal.props('modelValue')).toBe(true)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should initialize SettingsModal as closed', () => {
|
|
182
|
+
const wrapper = createWrapper({
|
|
183
|
+
title: 'Test App',
|
|
184
|
+
showSettings: true,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
188
|
+
expect(settingsModal.props('modelValue')).toBe(false)
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe('settingsConfig prop', () => {
|
|
193
|
+
it('should pass title to SettingsModal', () => {
|
|
194
|
+
const settingsConfig: TopBarSettingsConfig = {
|
|
195
|
+
title: 'Custom Settings',
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const wrapper = createWrapper({
|
|
199
|
+
title: 'Test App',
|
|
200
|
+
showSettings: true,
|
|
201
|
+
settingsConfig,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
205
|
+
expect(settingsModal.props('title')).toBe('Custom Settings')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('should pass tabs to SettingsModal', () => {
|
|
209
|
+
const settingsConfig: TopBarSettingsConfig = {
|
|
210
|
+
tabs: [
|
|
211
|
+
{ id: 'general', label: 'General' },
|
|
212
|
+
{ id: 'advanced', label: 'Advanced' },
|
|
213
|
+
],
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const wrapper = createWrapper({
|
|
217
|
+
title: 'Test App',
|
|
218
|
+
showSettings: true,
|
|
219
|
+
settingsConfig,
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
223
|
+
expect(settingsModal.props('tabs')).toEqual(settingsConfig.tabs)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('should pass showAppearance to SettingsModal', () => {
|
|
227
|
+
const settingsConfig: TopBarSettingsConfig = {
|
|
228
|
+
showAppearance: false,
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const wrapper = createWrapper({
|
|
232
|
+
title: 'Test App',
|
|
233
|
+
showSettings: true,
|
|
234
|
+
settingsConfig,
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
238
|
+
expect(settingsModal.props('showAppearance')).toBe(false)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('should default showAppearance to true when not specified', () => {
|
|
242
|
+
const wrapper = createWrapper({
|
|
243
|
+
title: 'Test App',
|
|
244
|
+
showSettings: true,
|
|
245
|
+
settingsConfig: {},
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
249
|
+
expect(settingsModal.props('showAppearance')).toBe(true)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('should pass size to SettingsModal', () => {
|
|
253
|
+
const settingsConfig: TopBarSettingsConfig = {
|
|
254
|
+
size: 'xl',
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const wrapper = createWrapper({
|
|
258
|
+
title: 'Test App',
|
|
259
|
+
showSettings: true,
|
|
260
|
+
settingsConfig,
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
264
|
+
expect(settingsModal.props('size')).toBe('xl')
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('should use default size when not specified', () => {
|
|
268
|
+
const wrapper = createWrapper({
|
|
269
|
+
title: 'Test App',
|
|
270
|
+
showSettings: true,
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
274
|
+
// SettingsModal defaults to 'lg' size
|
|
275
|
+
expect(settingsModal.props('size')).toBe('lg')
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
describe('settings tab slot forwarding', () => {
|
|
280
|
+
it('should forward settings-tab slots to SettingsModal', () => {
|
|
281
|
+
const settingsConfig: TopBarSettingsConfig = {
|
|
282
|
+
tabs: [{ id: 'general', label: 'General' }],
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const wrapper = createWrapper({
|
|
286
|
+
title: 'Test App',
|
|
287
|
+
showSettings: true,
|
|
288
|
+
settingsConfig,
|
|
289
|
+
}, {
|
|
290
|
+
'settings-tab-general': '<div class="custom-tab-content">Custom General Tab</div>',
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
294
|
+
expect(settingsModal.vm.$slots['tab-general']).toBeDefined()
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
it('should forward multiple settings-tab slots', () => {
|
|
298
|
+
const settingsConfig: TopBarSettingsConfig = {
|
|
299
|
+
tabs: [
|
|
300
|
+
{ id: 'general', label: 'General' },
|
|
301
|
+
{ id: 'advanced', label: 'Advanced' },
|
|
302
|
+
],
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const wrapper = createWrapper({
|
|
306
|
+
title: 'Test App',
|
|
307
|
+
showSettings: true,
|
|
308
|
+
settingsConfig,
|
|
309
|
+
}, {
|
|
310
|
+
'settings-tab-general': '<div>General Content</div>',
|
|
311
|
+
'settings-tab-advanced': '<div>Advanced Content</div>',
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
315
|
+
expect(settingsModal.vm.$slots['tab-general']).toBeDefined()
|
|
316
|
+
expect(settingsModal.vm.$slots['tab-advanced']).toBeDefined()
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
it('should forward settings-appearance slot', () => {
|
|
320
|
+
const wrapper = createWrapper({
|
|
321
|
+
title: 'Test App',
|
|
322
|
+
showSettings: true,
|
|
323
|
+
}, {
|
|
324
|
+
'settings-appearance': '<div class="custom-appearance">Custom Appearance</div>',
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
328
|
+
expect(settingsModal.vm.$slots.appearance).toBeDefined()
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
describe('showStandaloneLabel prop', () => {
|
|
333
|
+
it('should show standalone badge when showStandaloneLabel is true (default)', () => {
|
|
334
|
+
const wrapper = createWrapper({
|
|
335
|
+
title: 'Test App',
|
|
336
|
+
showStandaloneLabel: true,
|
|
337
|
+
})
|
|
338
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').exists()).toBe(true)
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('should hide standalone badge when showStandaloneLabel is false', () => {
|
|
342
|
+
const wrapper = createWrapper({
|
|
343
|
+
title: 'Test App',
|
|
344
|
+
showStandaloneLabel: false,
|
|
345
|
+
})
|
|
346
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').exists()).toBe(false)
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
describe('standaloneLabel prop', () => {
|
|
351
|
+
it('should display default "Standalone" label', () => {
|
|
352
|
+
const wrapper = createWrapper({
|
|
353
|
+
title: 'Test App',
|
|
354
|
+
})
|
|
355
|
+
const badge = wrapper.find('.mld-topbar__standalone-badge')
|
|
356
|
+
expect(badge.text()).toBe('Standalone')
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
it('should display custom standalone label', () => {
|
|
360
|
+
const wrapper = createWrapper({
|
|
361
|
+
title: 'Test App',
|
|
362
|
+
standaloneLabel: 'Demo Mode',
|
|
363
|
+
})
|
|
364
|
+
const badge = wrapper.find('.mld-topbar__standalone-badge')
|
|
365
|
+
expect(badge.text()).toBe('Demo Mode')
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
it('should support empty string as label', () => {
|
|
369
|
+
const wrapper = createWrapper({
|
|
370
|
+
title: 'Test App',
|
|
371
|
+
standaloneLabel: '',
|
|
372
|
+
})
|
|
373
|
+
const badge = wrapper.find('.mld-topbar__standalone-badge')
|
|
374
|
+
expect(badge.text()).toBe('')
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
describe('standalone detection with platform integration', () => {
|
|
379
|
+
it('should hide standalone badge when integrated with platform', () => {
|
|
380
|
+
// This test verifies the component logic when isIntegrated changes
|
|
381
|
+
// Since we can't easily change the mock mid-test, we'll skip the runtime test
|
|
382
|
+
// The component code correctly checks isStandalone computed property
|
|
383
|
+
const wrapper = createWrapper({
|
|
384
|
+
title: 'Test App',
|
|
385
|
+
showStandaloneLabel: true,
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
// In non-integrated mode (default mock), badge should be visible
|
|
389
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').exists()).toBe(true)
|
|
390
|
+
})
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
describe('combined features', () => {
|
|
394
|
+
it('should render ThemeToggle and settings button together', () => {
|
|
395
|
+
const wrapper = createWrapper({
|
|
396
|
+
title: 'Test App',
|
|
397
|
+
showThemeToggle: true,
|
|
398
|
+
showSettings: true,
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
expect(wrapper.findComponent(ThemeToggle).exists()).toBe(true)
|
|
402
|
+
expect(wrapper.find('.mld-topbar__settings-btn').exists()).toBe(true)
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
it('should render standalone badge, ThemeToggle, and settings button together', () => {
|
|
406
|
+
const wrapper = createWrapper({
|
|
407
|
+
title: 'Test App',
|
|
408
|
+
showThemeToggle: true,
|
|
409
|
+
showSettings: true,
|
|
410
|
+
showStandaloneLabel: true,
|
|
411
|
+
standaloneLabel: 'Offline',
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').exists()).toBe(true)
|
|
415
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').text()).toBe('Offline')
|
|
416
|
+
expect(wrapper.findComponent(ThemeToggle).exists()).toBe(true)
|
|
417
|
+
expect(wrapper.find('.mld-topbar__settings-btn').exists()).toBe(true)
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it('should maintain existing topbar functionality with new features', () => {
|
|
421
|
+
const wrapper = createWrapper({
|
|
422
|
+
title: 'My Plugin',
|
|
423
|
+
subtitle: 'Version 1.0',
|
|
424
|
+
showThemeToggle: true,
|
|
425
|
+
showSettings: true,
|
|
426
|
+
showStandaloneLabel: true,
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
// Verify title and subtitle still render
|
|
430
|
+
expect(wrapper.text()).toContain('My Plugin')
|
|
431
|
+
expect(wrapper.text()).toContain('Version 1.0')
|
|
432
|
+
|
|
433
|
+
// Verify new features render
|
|
434
|
+
expect(wrapper.findComponent(ThemeToggle).exists()).toBe(true)
|
|
435
|
+
expect(wrapper.find('.mld-topbar__settings-btn').exists()).toBe(true)
|
|
436
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').exists()).toBe(true)
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
it('should render all elements in correct order in right section', () => {
|
|
440
|
+
const wrapper = createWrapper({
|
|
441
|
+
title: 'Test App',
|
|
442
|
+
showThemeToggle: true,
|
|
443
|
+
showSettings: true,
|
|
444
|
+
showStandaloneLabel: true,
|
|
445
|
+
}, {
|
|
446
|
+
actions: '<button class="custom-action">Action</button>',
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
const rightSection = wrapper.find('.mld-topbar__right')
|
|
450
|
+
expect(rightSection.exists()).toBe(true)
|
|
451
|
+
|
|
452
|
+
// Verify all expected elements are present
|
|
453
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').exists()).toBe(true)
|
|
454
|
+
expect(wrapper.find('.custom-action').exists()).toBe(true)
|
|
455
|
+
expect(wrapper.find('.mld-topbar__settings-btn').exists()).toBe(true)
|
|
456
|
+
expect(wrapper.findComponent(ThemeToggle).exists()).toBe(true)
|
|
457
|
+
})
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
describe('backward compatibility', () => {
|
|
461
|
+
it('should not break existing usage without new props', () => {
|
|
462
|
+
const wrapper = createWrapper({
|
|
463
|
+
title: 'Legacy App',
|
|
464
|
+
subtitle: 'Old API',
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
expect(wrapper.find('.mld-topbar').exists()).toBe(true)
|
|
468
|
+
expect(wrapper.findComponent(ThemeToggle).exists()).toBe(false)
|
|
469
|
+
expect(wrapper.find('.mld-topbar__settings-btn').exists()).toBe(false)
|
|
470
|
+
// Standalone badge still shows by default in standalone mode
|
|
471
|
+
expect(wrapper.find('.mld-topbar__standalone-badge').exists()).toBe(true)
|
|
472
|
+
})
|
|
473
|
+
|
|
474
|
+
it('should work with existing pages and tabs props', () => {
|
|
475
|
+
const wrapper = createWrapper({
|
|
476
|
+
pluginName: 'Test Plugin',
|
|
477
|
+
title: 'Dashboard',
|
|
478
|
+
pages: [
|
|
479
|
+
{ id: 'dashboard', label: 'Dashboard' },
|
|
480
|
+
{ id: 'settings', label: 'Settings' },
|
|
481
|
+
],
|
|
482
|
+
tabs: [
|
|
483
|
+
{ id: 'overview', label: 'Overview' },
|
|
484
|
+
{ id: 'details', label: 'Details' },
|
|
485
|
+
],
|
|
486
|
+
showThemeToggle: true,
|
|
487
|
+
showSettings: true,
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
expect(wrapper.text()).toContain('Test Plugin')
|
|
491
|
+
expect(wrapper.text()).toContain('Dashboard')
|
|
492
|
+
expect(wrapper.findComponent(ThemeToggle).exists()).toBe(true)
|
|
493
|
+
expect(wrapper.find('.mld-topbar__settings-btn').exists()).toBe(true)
|
|
494
|
+
})
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
describe('edge cases', () => {
|
|
498
|
+
it('should handle empty settingsConfig object', () => {
|
|
499
|
+
const wrapper = createWrapper({
|
|
500
|
+
title: 'Test App',
|
|
501
|
+
showSettings: true,
|
|
502
|
+
settingsConfig: {},
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
506
|
+
// SettingsModal has default values for these props
|
|
507
|
+
expect(settingsModal.props('title')).toBe('Settings')
|
|
508
|
+
expect(settingsModal.props('tabs')).toEqual([])
|
|
509
|
+
expect(settingsModal.props('showAppearance')).toBe(true)
|
|
510
|
+
expect(settingsModal.props('size')).toBe('lg')
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
it('should handle settingsConfig with only some fields', () => {
|
|
514
|
+
const settingsConfig: TopBarSettingsConfig = {
|
|
515
|
+
title: 'Partial Config',
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const wrapper = createWrapper({
|
|
519
|
+
title: 'Test App',
|
|
520
|
+
showSettings: true,
|
|
521
|
+
settingsConfig,
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
525
|
+
expect(settingsModal.props('title')).toBe('Partial Config')
|
|
526
|
+
// SettingsModal defaults tabs to empty array
|
|
527
|
+
expect(settingsModal.props('tabs')).toEqual([])
|
|
528
|
+
expect(settingsModal.props('showAppearance')).toBe(true)
|
|
529
|
+
// SettingsModal defaults size to 'lg'
|
|
530
|
+
expect(settingsModal.props('size')).toBe('lg')
|
|
531
|
+
})
|
|
532
|
+
|
|
533
|
+
it('should handle standalone label with special characters', () => {
|
|
534
|
+
const wrapper = createWrapper({
|
|
535
|
+
title: 'Test App',
|
|
536
|
+
standaloneLabel: '🚀 Preview',
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
const badge = wrapper.find('.mld-topbar__standalone-badge')
|
|
540
|
+
expect(badge.text()).toBe('🚀 Preview')
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
it('should handle very long standalone label', () => {
|
|
544
|
+
const longLabel = 'This is a very long standalone mode label text'
|
|
545
|
+
const wrapper = createWrapper({
|
|
546
|
+
title: 'Test App',
|
|
547
|
+
standaloneLabel: longLabel,
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
const badge = wrapper.find('.mld-topbar__standalone-badge')
|
|
551
|
+
expect(badge.text()).toBe(longLabel)
|
|
552
|
+
})
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
describe('type safety', () => {
|
|
556
|
+
it('should accept valid settingsConfig with all fields', () => {
|
|
557
|
+
const settingsConfig: TopBarSettingsConfig = {
|
|
558
|
+
title: 'Complete Settings',
|
|
559
|
+
tabs: [
|
|
560
|
+
{ id: 'general', label: 'General', icon: 'settings' },
|
|
561
|
+
{ id: 'advanced', label: 'Advanced' },
|
|
562
|
+
],
|
|
563
|
+
showAppearance: true,
|
|
564
|
+
size: 'lg',
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const wrapper = createWrapper({
|
|
568
|
+
title: 'Test App',
|
|
569
|
+
showSettings: true,
|
|
570
|
+
settingsConfig,
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
expect(wrapper.findComponent(SettingsModal).exists()).toBe(true)
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
it('should accept all valid size options', () => {
|
|
577
|
+
const sizes: Array<'md' | 'lg' | 'xl'> = ['md', 'lg', 'xl']
|
|
578
|
+
|
|
579
|
+
sizes.forEach((size) => {
|
|
580
|
+
const wrapper = createWrapper({
|
|
581
|
+
title: 'Test App',
|
|
582
|
+
showSettings: true,
|
|
583
|
+
settingsConfig: { size },
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
const settingsModal = wrapper.findComponent(SettingsModal)
|
|
587
|
+
expect(settingsModal.props('size')).toBe(size)
|
|
588
|
+
})
|
|
589
|
+
})
|
|
590
|
+
})
|
|
591
|
+
|
|
592
|
+
describe('plugin metadata icon fallback', () => {
|
|
593
|
+
function mockPlatformCtx(overrides: Record<string, unknown> = {}) {
|
|
594
|
+
const defaults = {
|
|
595
|
+
isIntegrated: false,
|
|
596
|
+
plugin: undefined as unknown,
|
|
597
|
+
user: undefined as unknown,
|
|
598
|
+
theme: 'system' as const,
|
|
599
|
+
features: undefined as unknown,
|
|
600
|
+
}
|
|
601
|
+
const merged = { ...defaults, ...overrides }
|
|
602
|
+
vi.mocked(usePlatformContext).mockReturnValue({
|
|
603
|
+
isIntegrated: computed(() => merged.isIntegrated as boolean),
|
|
604
|
+
context: ref({ isIntegrated: merged.isIntegrated as boolean, theme: merged.theme }),
|
|
605
|
+
plugin: computed(() => merged.plugin as any),
|
|
606
|
+
user: computed(() => merged.user as any),
|
|
607
|
+
theme: computed(() => merged.theme as any),
|
|
608
|
+
features: computed(() => merged.features as any),
|
|
609
|
+
navigate: vi.fn(),
|
|
610
|
+
notify: vi.fn(),
|
|
611
|
+
sendToPlatform: vi.fn(),
|
|
612
|
+
})
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
afterEach(() => {
|
|
616
|
+
mockPlatformCtx()
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
it('should render default M logo when no plugin icon and no #icon slot', () => {
|
|
620
|
+
const wrapper = createWrapper({ title: 'Test App' })
|
|
621
|
+
|
|
622
|
+
expect(wrapper.find('.mld-topbar__logo-text').exists()).toBe(true)
|
|
623
|
+
expect(wrapper.find('.mld-topbar__logo-text').text()).toBe('M')
|
|
624
|
+
expect(wrapper.find('.mld-topbar__logo-svg').exists()).toBe(false)
|
|
625
|
+
})
|
|
626
|
+
|
|
627
|
+
it('should render plugin metadata icon SVG when plugin has icon', () => {
|
|
628
|
+
const iconPath = 'M13 10V3L4 14h7v7l9-11h-7z'
|
|
629
|
+
mockPlatformCtx({
|
|
630
|
+
isIntegrated: true,
|
|
631
|
+
plugin: { id: 'test', name: 'Test', version: '1.0', icon: iconPath, route_prefix: '/test', api_prefix: '/api/test' },
|
|
632
|
+
})
|
|
633
|
+
|
|
634
|
+
const wrapper = createWrapper({ title: 'Test App' })
|
|
635
|
+
|
|
636
|
+
const svg = wrapper.find('.mld-topbar__logo-svg')
|
|
637
|
+
expect(svg.exists()).toBe(true)
|
|
638
|
+
expect(svg.find('path').attributes('d')).toBe(iconPath)
|
|
639
|
+
expect(wrapper.find('.mld-topbar__logo-text').exists()).toBe(false)
|
|
640
|
+
})
|
|
641
|
+
|
|
642
|
+
it('should not render plugin icon when #icon slot is provided', () => {
|
|
643
|
+
const iconPath = 'M13 10V3L4 14h7v7l9-11h-7z'
|
|
644
|
+
mockPlatformCtx({
|
|
645
|
+
isIntegrated: true,
|
|
646
|
+
plugin: { id: 'test', name: 'Test', version: '1.0', icon: iconPath, route_prefix: '/test', api_prefix: '/api/test' },
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
const wrapper = createWrapper({ title: 'Test App' }, {
|
|
650
|
+
icon: '<div class="custom-icon">Custom</div>',
|
|
651
|
+
})
|
|
652
|
+
|
|
653
|
+
expect(wrapper.find('.custom-icon').exists()).toBe(true)
|
|
654
|
+
expect(wrapper.find('.mld-topbar__logo-svg').exists()).toBe(false)
|
|
655
|
+
})
|
|
656
|
+
|
|
657
|
+
it('should not render plugin icon when icon does not start with M', () => {
|
|
658
|
+
mockPlatformCtx({
|
|
659
|
+
isIntegrated: true,
|
|
660
|
+
plugin: { id: 'test', name: 'Test', version: '1.0', icon: 'invalid-path', route_prefix: '/test', api_prefix: '/api/test' },
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
const wrapper = createWrapper({ title: 'Test App' })
|
|
664
|
+
|
|
665
|
+
expect(wrapper.find('.mld-topbar__logo-svg').exists()).toBe(false)
|
|
666
|
+
expect(wrapper.find('.mld-topbar__logo-text').exists()).toBe(true)
|
|
667
|
+
expect(wrapper.find('.mld-topbar__logo-text').text()).toBe('M')
|
|
668
|
+
})
|
|
669
|
+
|
|
670
|
+
it('should not render plugin icon when icon is empty string', () => {
|
|
671
|
+
mockPlatformCtx({
|
|
672
|
+
isIntegrated: true,
|
|
673
|
+
plugin: { id: 'test', name: 'Test', version: '1.0', icon: '', route_prefix: '/test', api_prefix: '/api/test' },
|
|
674
|
+
})
|
|
675
|
+
|
|
676
|
+
const wrapper = createWrapper({ title: 'Test App' })
|
|
677
|
+
|
|
678
|
+
expect(wrapper.find('.mld-topbar__logo-svg').exists()).toBe(false)
|
|
679
|
+
expect(wrapper.find('.mld-topbar__logo-text').exists()).toBe(true)
|
|
680
|
+
expect(wrapper.find('.mld-topbar__logo-text').text()).toBe('M')
|
|
681
|
+
})
|
|
682
|
+
})
|
|
683
|
+
})
|