@morscherlab/mint-sdk 1.2.10 → 1.3.0-beta.10
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 +45 -137
- package/dist/ResourcePickerListInternal-CFAw4vTZ.js +605 -0
- package/dist/ResourcePickerListInternal-CFAw4vTZ.js.map +1 -0
- package/dist/__stories__/experiment-helpers.d.ts +12 -1
- package/dist/__tests__/components/ScientificNumber.test.d.ts +1 -0
- package/dist/__tests__/components/SequenceInput.test.d.ts +1 -0
- package/dist/__tests__/components/Skeleton.test.d.ts +1 -0
- package/dist/__tests__/components/SmartGroupFieldRecipe.test.d.ts +1 -0
- package/dist/__tests__/components/SmartGroupModal.test.d.ts +1 -0
- package/dist/__tests__/components/StepWizard.test.d.ts +1 -0
- package/dist/__tests__/components/TimeRangeInput.test.d.ts +1 -0
- package/dist/__tests__/components/UnitInput.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.keyboard.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.sampleTypeIndicator.test.d.ts +1 -0
- package/dist/__tests__/composables/useChemicalFormula.test.d.ts +1 -0
- package/dist/__tests__/composables/useRackEditor.test.d.ts +1 -0
- package/dist/__tests__/composables/useSequenceUtils.test.d.ts +1 -0
- package/dist/__tests__/styles/baseControlsRules.test.d.ts +1 -0
- package/dist/__tests__/styles/controlRefreshRules.test.d.ts +1 -0
- package/dist/__tests__/styles/darkModeRules.test.d.ts +1 -0
- package/dist/__tests__/styles/menusShellRefresh.test.d.ts +1 -0
- package/dist/__tests__/templates/templatesDeprecation.test.d.ts +1 -0
- package/dist/__tests__/utils/plotlyTemplate.test.d.ts +1 -0
- package/dist/__tests__/utils/sampleTypeMarker.test.d.ts +1 -0
- package/dist/{analysisArtifactTypes-DQ0MjwHZ.js → analysisArtifactTypes-Cy3D0Ipg.js} +1 -1
- package/dist/{analysisArtifactTypes-DQ0MjwHZ.js.map → analysisArtifactTypes-Cy3D0Ipg.js.map} +1 -1
- package/dist/components/ActionMenu.vue.d.ts +17 -5
- package/dist/components/AdductText.vue.d.ts +3 -2
- package/dist/components/AlertBox.vue.d.ts +11 -14
- package/dist/components/AppAvatarMenu.vue.d.ts +18 -28
- package/dist/components/AppContainer.vue.d.ts +7 -11
- package/dist/components/AppLayout.vue.d.ts +11 -13
- package/dist/components/AppSidebar.vue.d.ts +30 -28
- package/dist/components/AppToastContainer.vue.d.ts +2 -1
- package/dist/components/AppTopBar.vue.d.ts +45 -44
- package/dist/components/ArtifactSaveDialog.vue.d.ts +3 -2
- package/dist/components/ArtifactSelectorModal.vue.d.ts +5 -6
- package/dist/components/AuditTrail.vue.d.ts +17 -18
- package/dist/components/AutoGroupModal.vue.d.ts +4 -3
- package/dist/components/Avatar.vue.d.ts +2 -1
- package/dist/components/BaseButton.vue.d.ts +11 -13
- package/dist/components/BaseCheckbox.vue.d.ts +7 -1
- package/dist/components/BaseInput.vue.d.ts +15 -2
- package/dist/components/BaseModal.vue.d.ts +20 -23
- package/dist/components/BasePill.vue.d.ts +10 -13
- package/dist/components/BaseRadioGroup.vue.d.ts +2 -1
- package/dist/components/BaseSelect.vue.d.ts +3 -2
- package/dist/components/BaseSlider.vue.d.ts +18 -1
- package/dist/components/BaseTabs.vue.d.ts +2 -1
- package/dist/components/BaseTextarea.vue.d.ts +2 -1
- package/dist/components/BaseToggle.vue.d.ts +2 -1
- package/dist/components/BatchProgressList.vue.d.ts +22 -26
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +7 -14
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +6 -13
- package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +7 -14
- package/dist/components/BioTemplateRenderer.vue.d.ts +2 -1
- package/dist/components/Breadcrumb.vue.d.ts +13 -16
- package/dist/components/Calendar.vue.d.ts +21 -23
- package/dist/components/ChartContainer.vue.d.ts +19 -25
- package/dist/components/ChemicalFormula.vue.d.ts +7 -11
- package/dist/components/CollapsibleCard.vue.d.ts +7 -11
- package/dist/components/ComponentBindingRenderer.vue.d.ts +2 -1
- package/dist/components/ConcentrationInput.vue.d.ts +2 -1
- package/dist/components/ConfirmDialog.vue.d.ts +10 -13
- package/dist/components/ControlWorkspaceView.vue.d.ts +9 -18
- package/dist/components/DataFrame.vue.d.ts +27 -24
- package/dist/components/DatePicker.vue.d.ts +3 -6
- package/dist/components/DateTimePicker.vue.d.ts +3 -6
- package/dist/components/Divider.vue.d.ts +3 -2
- package/dist/components/DoseCalculator.vue.d.ts +2 -1
- package/dist/components/DoseCalculatorVolumeField.vue.d.ts +2 -1
- package/dist/components/DoseDesignWorkspaceView.vue.d.ts +9 -18
- package/dist/components/EmptyState.vue.d.ts +9 -12
- package/dist/components/ExperimentCodeBadge.vue.d.ts +2 -1
- package/dist/components/ExperimentDataViewer.vue.d.ts +13 -15
- package/dist/components/ExperimentPopover.vue.d.ts +3 -5
- package/dist/components/ExperimentSelectorModal.vue.d.ts +3 -4
- package/dist/components/ExperimentTimeline.vue.d.ts +13 -15
- package/dist/components/FilePicker.vue.d.ts +10 -5
- package/dist/components/FileUploader.vue.d.ts +8 -16
- package/dist/components/FitPanel.vue.d.ts +26 -16
- package/dist/components/FormActions.vue.d.ts +2 -1
- package/dist/components/FormBuilder.vue.d.ts +54 -250
- package/dist/components/FormField.vue.d.ts +14 -16
- package/dist/components/FormulaInput.vue.d.ts +2 -1
- package/dist/components/GeneratedPathInput.vue.d.ts +6 -5
- package/dist/components/GeneratedPlotlyResult.vue.d.ts +2 -1
- package/dist/components/GeneratedPluginView.vue.d.ts +2 -1
- package/dist/components/GeneratedResultRenderer.vue.d.ts +2 -1
- package/dist/components/GroupAssigner.vue.d.ts +2 -1
- package/dist/components/IconButton.vue.d.ts +7 -11
- package/dist/components/InstrumentStateBadge.vue.d.ts +4 -3
- package/dist/components/JobsStatusTray.vue.d.ts +13 -29
- package/dist/components/LayoutResizeHandle.vue.d.ts +2 -1
- package/dist/components/LoadingSpinner.vue.d.ts +2 -1
- package/dist/components/MobileSupportGate.vue.d.ts +5 -13
- package/dist/components/MoleculeInput.vue.d.ts +3 -4
- package/dist/components/MultiSelect.vue.d.ts +2 -1
- package/dist/components/NumberInput.vue.d.ts +2 -1
- package/dist/components/PlateEditor.vue.d.ts +147 -0
- package/dist/components/PlotlyChart.vue.d.ts +21 -31
- package/dist/components/PluginIcon.vue.d.ts +2 -1
- package/dist/components/PluginWorkspaceView.props.d.ts +2 -6
- package/dist/components/PluginWorkspaceView.vue.d.ts +23 -51
- package/dist/components/ProgressBar.vue.d.ts +4 -9
- package/dist/components/ProtocolStepEditor.vue.d.ts +7 -5
- package/dist/components/ProtocolStepParameterField.vue.d.ts +2 -1
- package/dist/components/ReagentEditor.vue.d.ts +2 -1
- package/dist/components/ReagentList.presentation.d.ts +2 -1
- package/dist/components/ReagentList.vue.d.ts +15 -17
- package/dist/components/ResourceCard.vue.d.ts +18 -17
- package/dist/components/RuntimeUpdateNotice.vue.d.ts +3 -2
- package/dist/components/SampleHierarchyTree.vue.d.ts +21 -15
- package/dist/components/SampleLegend.vue.d.ts +2 -1
- package/dist/components/SampleSelector.vue.d.ts +15 -21
- package/dist/components/SampleSelectorGroupNode.vue.d.ts +2 -1
- package/dist/components/SampleSelectorSampleRow.vue.d.ts +2 -5
- package/dist/components/ScheduleCalendar.vue.d.ts +34 -36
- package/dist/components/ScientificNumber.vue.d.ts +3 -2
- package/dist/components/SearchableSelect.vue.d.ts +14 -32
- package/dist/components/SecretInput.vue.d.ts +2 -1
- package/dist/components/SectionCard.vue.d.ts +13 -14
- package/dist/components/SegmentedControl.vue.d.ts +2 -1
- package/dist/components/SequenceInput.vue.d.ts +10 -21
- package/dist/components/SequenceProgressBar.vue.d.ts +2 -1
- package/dist/components/SettingsModal.schema.d.ts +1 -1
- package/dist/components/SettingsModal.vue.d.ts +28 -25
- package/dist/components/Skeleton.vue.d.ts +2 -1
- package/dist/components/SmartGroup.types.d.ts +2 -2
- package/dist/components/SmartGroupFieldRecipe.groups.d.ts +2 -0
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +4 -7
- package/dist/components/SmartGroupManual.vue.d.ts +3 -2
- package/dist/components/SmartGroupModal.vue.d.ts +6 -4
- package/dist/components/StatusIndicator.vue.d.ts +2 -1
- package/dist/components/StepWizard.vue.d.ts +26 -27
- package/dist/components/TagsInput.vue.d.ts +3 -5
- package/dist/components/ThemeToggle.vue.d.ts +2 -1
- package/dist/components/TimePicker.vue.d.ts +6 -6
- package/dist/components/TimeRangeInput.vue.d.ts +9 -2
- package/dist/components/Tooltip.vue.d.ts +8 -18
- package/dist/components/UnitInput.vue.d.ts +2 -1
- package/dist/components/WellPlate.colors.d.ts +2 -4
- package/dist/components/WellPlate.rendering.d.ts +7 -1
- package/dist/components/WellPlate.sampleDrop.d.ts +2 -0
- package/dist/components/WellPlate.vue.d.ts +22 -39
- package/dist/components/index.d.ts +34 -31
- package/dist/components/index.js +5 -8
- package/dist/components/internal/ActionItemInternal.vue.d.ts +11 -13
- package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +14 -23
- package/dist/components/internal/AppTopBarPillNavInternal.vue.d.ts +2 -1
- package/dist/components/internal/CalendarGridPanelInternal.vue.d.ts +2 -1
- package/dist/components/internal/DataStateInternal.vue.d.ts +7 -11
- package/dist/components/internal/{FilePickerTreeInternal.vue.d.ts → FilePickerListInternal.vue.d.ts} +9 -6
- package/dist/components/internal/FilePickerPreviewInternal.vue.d.ts +4 -3
- package/dist/components/internal/FormFieldRendererInternal.vue.d.ts +16 -18
- package/dist/components/internal/FormSectionRenderer.vue.d.ts +36 -32
- package/dist/components/internal/PlateEditorBindingInternal.vue.d.ts +8 -0
- package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +65 -0
- package/dist/components/internal/SettingsPaneHeadInternal.vue.d.ts +15 -0
- package/dist/components/internal/WellEditPopupInternal.vue.d.ts +12 -4
- package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +6 -13
- package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +19 -42
- package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +9 -19
- package/dist/components/internal/filePickerPresentation.d.ts +8 -0
- package/dist/components/internal/generatedJobsApi.d.ts +9 -0
- package/dist/{components-Bbg_gtzF.js → components-GDDvof8D.js} +7088 -5955
- package/dist/components-GDDvof8D.js.map +1 -0
- package/dist/composables/controlSchemaTypes.d.ts +4 -43
- package/dist/composables/createFilePickerAdapter.d.ts +5 -1
- package/dist/composables/filePicker/usePickerNavigation.d.ts +7 -2
- package/dist/composables/index.d.ts +2 -13
- package/dist/composables/index.js +7 -11
- package/dist/composables/pluginWorkspaceModel.d.ts +1 -3
- package/dist/composables/useAutoGroup.d.ts +10 -0
- package/dist/composables/useBioTemplateComponents.d.ts +15 -3
- package/dist/composables/useBioTemplateControls.d.ts +5 -1
- package/dist/composables/useBioTemplatePackWorkspace.d.ts +2 -0
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +5 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +5 -1
- package/dist/composables/useChemicalFormula.d.ts +2 -1
- package/dist/composables/useDropdownState.d.ts +5 -0
- package/dist/composables/useExperimentSave.d.ts +5 -1
- package/dist/composables/useFileBrowser.d.ts +3 -2
- package/dist/composables/useFilePicker.d.ts +25 -13
- package/dist/composables/useFocusTrap.d.ts +2 -1
- package/dist/composables/useGroupAssignment.d.ts +5 -1
- package/dist/composables/useJobsStatusTray.d.ts +5 -5
- package/dist/composables/useMenuKeyboard.d.ts +3 -3
- package/dist/composables/usePlatformContext.d.ts +3 -0
- package/dist/composables/usePluginClient.d.ts +1 -1
- package/dist/composables/useRackEditor.d.ts +36 -1
- package/dist/composables/useSequenceUtils.d.ts +8 -0
- package/dist/composables/useTemplateCollection.d.ts +5 -1
- package/dist/composables/workspaceRecipes.d.ts +1 -0
- package/dist/composables-D6uqp0UF.js +591 -0
- package/dist/composables-D6uqp0UF.js.map +1 -0
- package/dist/{errors-BLuqMee5.js → experiment-DgllIc3I.js} +89 -333
- package/dist/experiment-DgllIc3I.js.map +1 -0
- package/dist/{experiment-utils-CV2Wza3z.js → experiment-utils-DTANyVKV.js} +68 -37
- package/dist/experiment-utils-DTANyVKV.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +122 -17
- package/dist/index.js.map +1 -1
- package/dist/install.js +1 -2
- package/dist/install.js.map +1 -1
- package/dist/instrument.d.ts +6 -0
- package/dist/lcms.d.ts +11 -0
- package/dist/permissions.d.ts +2 -47
- package/dist/permissions.js +2 -26
- package/dist/permissions.js.map +1 -1
- package/dist/settings-BLr8lzw6.js +341 -0
- package/dist/settings-BLr8lzw6.js.map +1 -0
- package/dist/stores/auth.d.ts +2 -150
- package/dist/stores/experiment.d.ts +2 -106
- package/dist/stores/index.js +2 -2
- package/dist/stores/settings.d.ts +2 -44
- package/dist/styles.css +8608 -6950
- package/dist/tailwind.preset.js.map +1 -1
- package/dist/templates/adapters.d.ts +3 -0
- package/dist/templates/assayMatrixAdapters.d.ts +8 -1
- package/dist/templates/assayMatrixBuilder.d.ts +1 -0
- package/dist/templates/builderPresetControls.d.ts +1 -0
- package/dist/templates/calibrationCurveAdapters.d.ts +3 -0
- package/dist/templates/calibrationCurveBuilder.d.ts +1 -0
- package/dist/templates/catalog.d.ts +6 -2
- package/dist/templates/componentBindings.d.ts +31 -5
- package/dist/templates/componentPlateHelpers.d.ts +7 -1
- package/dist/templates/controlSchemas.d.ts +9 -0
- package/dist/templates/defaultBioTemplateBuilder.d.ts +1 -0
- package/dist/templates/doseResponseAdapters.d.ts +2 -0
- package/dist/templates/doseResponseBuilder.d.ts +1 -0
- package/dist/templates/elisaAssayCollectionBuilder.d.ts +1 -0
- package/dist/templates/flowCytometryAssayCollectionBuilder.d.ts +1 -0
- package/dist/templates/flowCytometryPanelBuilder.d.ts +1 -0
- package/dist/templates/flowPanelAdapters.d.ts +3 -0
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.js +2 -3
- package/dist/templates/instrumentRunAdapters.d.ts +13 -2
- package/dist/templates/instrumentRunBuilder.d.ts +1 -0
- package/dist/templates/lcmsBatchCollectionBuilder.d.ts +1 -0
- package/dist/templates/packs.d.ts +4 -0
- package/dist/templates/plateMapAdapters.d.ts +2 -0
- package/dist/templates/plateMapBuilder.d.ts +1 -0
- package/dist/templates/presets.d.ts +6 -2
- package/dist/templates/protocolAdapters.d.ts +4 -0
- package/dist/templates/protocolStepsBuilder.d.ts +1 -0
- package/dist/templates/qpcrAdapters.d.ts +4 -0
- package/dist/templates/qpcrExpressionCollectionBuilder.d.ts +1 -0
- package/dist/templates/qpcrPlateBuilder.d.ts +1 -0
- package/dist/templates/reagentAdapters.d.ts +4 -0
- package/dist/templates/reagentListBuilder.d.ts +1 -0
- package/dist/templates/samplePrepAdapters.d.ts +3 -0
- package/dist/templates/samplePrepBuilder.d.ts +1 -0
- package/dist/templates/sampleSheetAdapters.d.ts +4 -0
- package/dist/templates/sampleSheetBuilder.d.ts +1 -0
- package/dist/templates/targetedMetabolomicsCollectionBuilder.d.ts +1 -0
- package/dist/templates/templateEnvelopes.d.ts +8 -0
- package/dist/templates/templatePackCollectionBuilder.d.ts +1 -0
- package/dist/templates/templatePresetCollectionBuilder.d.ts +2 -0
- package/dist/templates/templateValidators.d.ts +12 -0
- package/dist/templates/timeCourseAdapters.d.ts +4 -0
- package/dist/templates/timeCourseBuilder.d.ts +1 -0
- package/dist/templates/wellPlateScreenCollectionBuilder.d.ts +1 -0
- package/dist/templates/westernBlotAssayCollectionBuilder.d.ts +1 -0
- package/dist/templates-BU1pet28.js +12304 -0
- package/dist/templates-BU1pet28.js.map +1 -0
- package/dist/types/auth.d.ts +0 -27
- package/dist/types/componentLabTypes.d.ts +14 -7
- package/dist/types/componentWorkflowTypes.d.ts +26 -0
- package/dist/types/components.d.ts +40 -17
- package/dist/types/fileBrowserTypes.d.ts +13 -0
- package/dist/types/filePicker.d.ts +19 -1
- package/dist/types/generated-ui.d.ts +13 -0
- package/dist/types/index.d.ts +4 -4
- package/dist/types/index.js +1 -1
- package/dist/types/instrument.d.ts +43 -15
- package/dist/types/platform.d.ts +2 -0
- package/dist/utils/color.d.ts +6 -1
- package/dist/utils/formModelSync.d.ts +1 -0
- package/dist/utils/plotlyTemplate.d.ts +3 -0
- package/dist/utils/rack.d.ts +16 -0
- package/dist/utils/sampleTypeMarker.d.ts +23 -0
- package/dist/{validation-ChMyixAf.js → validation-Dmp9Q5W0.js} +3032 -838
- package/dist/validation-Dmp9Q5W0.js.map +1 -0
- package/package.json +17 -23
- package/src/__stories__/experiment-helpers.ts +8 -4
- package/src/__tests__/components/ActionMenu.test.ts +66 -0
- package/src/__tests__/components/AlertBox.test.ts +21 -0
- package/src/__tests__/components/AppAvatarMenu.test.ts +25 -0
- package/src/__tests__/components/AppTopBar.test.ts +79 -0
- package/src/__tests__/components/AppTopBarPageSelector.test.ts +20 -0
- package/src/__tests__/components/AppTopBarPillNav.test.ts +74 -0
- package/src/__tests__/components/ArtifactSelectorModal.test.ts +20 -6
- package/src/__tests__/components/AuditTrail.test.ts +38 -0
- package/src/__tests__/components/BaseCheckbox.test.ts +43 -0
- package/src/__tests__/components/BaseInput.test.ts +43 -0
- package/src/__tests__/components/BaseModal.test.ts +90 -0
- package/src/__tests__/components/BaseRadioGroup.test.ts +8 -0
- package/src/__tests__/components/BaseSlider.test.ts +116 -0
- package/src/__tests__/components/BaseToggle.test.ts +7 -0
- package/src/__tests__/components/BatchProgressList.test.ts +22 -14
- package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +4 -4
- package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +1 -1
- package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +8 -8
- package/src/__tests__/components/BioTemplateRenderer.test.ts +2 -2
- package/src/__tests__/components/Breadcrumb.test.ts +45 -0
- package/src/__tests__/components/CalendarGridPanel.test.ts +14 -0
- package/src/__tests__/components/ChartContainer.test.ts +23 -0
- package/src/__tests__/components/ChemicalFormula.test.ts +15 -0
- package/src/__tests__/components/CollapsibleCard.test.ts +12 -9
- package/src/__tests__/components/ComponentBindingRenderer.test.ts +45 -17
- package/src/__tests__/components/ConcentrationInput.test.ts +17 -2
- package/src/__tests__/components/DataFrame.test.ts +107 -4
- package/src/__tests__/components/DataStates.test.ts +0 -16
- package/src/__tests__/components/DatePicker.test.ts +10 -0
- package/src/__tests__/components/DoseCalculator.test.ts +47 -0
- package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +37 -34
- package/src/__tests__/components/EmptyState.test.ts +16 -1
- package/src/__tests__/components/ExperimentSelectorModal.test.ts +31 -3
- package/src/__tests__/components/ExperimentTimeline.test.ts +45 -0
- package/src/__tests__/components/FilePicker.state.test.ts +2 -2
- package/src/__tests__/components/FilePicker.test.ts +360 -13
- package/src/__tests__/components/FileUploader.test.ts +15 -0
- package/src/__tests__/components/FitPanel.test.ts +96 -0
- package/src/__tests__/components/FormulaInput.test.ts +97 -0
- package/src/__tests__/components/GeneratedPathInput.test.ts +168 -41
- package/src/__tests__/components/GeneratedPlotlyResult.test.ts +1 -1
- package/src/__tests__/components/GeneratedResultRenderer.test.ts +47 -2
- package/src/__tests__/components/InstrumentStateBadge.test.ts +50 -0
- package/src/__tests__/components/JobsStatusTray.test.ts +27 -0
- package/src/__tests__/components/MoleculeInput.test.ts +78 -0
- package/src/__tests__/components/MultiSelect.test.ts +6 -0
- package/src/__tests__/components/PlateEditor.test.ts +1134 -0
- package/src/__tests__/components/PlateEditorBindingInternal.test.ts +64 -0
- package/src/__tests__/components/PlotlyChart.test.ts +147 -1
- package/src/__tests__/components/ProgressBar.test.ts +18 -0
- package/src/__tests__/components/ProtocolStepEditor.test.ts +106 -18
- package/src/__tests__/components/ProtocolStepParameterField.test.ts +74 -29
- package/src/__tests__/components/ReagentList.presentation.test.ts +10 -5
- package/src/__tests__/components/ReagentList.test.ts +23 -0
- package/src/__tests__/components/ResourcePickerListInternal.test.ts +141 -0
- package/src/__tests__/components/RetiredStyles.test.ts +0 -6
- package/src/__tests__/components/SampleHierarchyTree.test.ts +102 -0
- package/src/__tests__/components/SampleLegend.test.ts +18 -0
- package/src/__tests__/components/SampleSelector.test.ts +92 -1
- package/src/__tests__/components/SampleSelectorSampleRow.test.ts +5 -2
- package/src/__tests__/components/ScheduleCalendar.test.ts +32 -0
- package/src/__tests__/components/ScientificNumber.test.ts +113 -0
- package/src/__tests__/components/SearchableSelect.test.ts +21 -0
- package/src/__tests__/components/SecretInput.test.ts +6 -0
- package/src/__tests__/components/SegmentedControl.test.ts +14 -0
- package/src/__tests__/components/SequenceInput.test.ts +81 -0
- package/src/__tests__/components/SettingsModal.schema.test.ts +5 -5
- package/src/__tests__/components/SettingsModal.test.ts +371 -23
- package/src/__tests__/components/Skeleton.test.ts +23 -0
- package/src/__tests__/components/SmartGroupFieldRecipe.test.ts +12 -0
- package/src/__tests__/components/SmartGroupModal.test.ts +69 -0
- package/src/__tests__/components/StepWizard.test.ts +75 -0
- package/src/__tests__/components/TagsInput.test.ts +16 -0
- package/src/__tests__/components/TimePicker.test.ts +66 -0
- package/src/__tests__/components/TimeRangeInput.test.ts +83 -0
- package/src/__tests__/components/UnitInput.test.ts +147 -0
- package/src/__tests__/components/WellEditPopupInternal.test.ts +434 -2
- package/src/__tests__/components/WellPlate.decoration.test.ts +18 -0
- package/src/__tests__/components/WellPlate.geometry.test.ts +13 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +213 -0
- package/src/__tests__/components/WellPlate.keyboard.test.ts +24 -0
- package/src/__tests__/components/WellPlate.rendering.test.ts +38 -19
- package/src/__tests__/components/WellPlate.sampleDrop.test.ts +19 -0
- package/src/__tests__/components/WellPlate.sampleTypeIndicator.test.ts +58 -0
- package/src/__tests__/composables/createFilePickerAdapter.test.ts +114 -1
- package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +1 -1
- package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +1 -1
- package/src/__tests__/composables/useChemicalFormula.test.ts +60 -0
- package/src/__tests__/composables/useControlSchema.test.ts +15 -5
- package/src/__tests__/composables/useDropdownState.test.ts +20 -1
- package/src/__tests__/composables/useGeneratedAnalysis.test.ts +52 -0
- package/src/__tests__/composables/usePickerNavigation.test.ts +61 -1
- package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +18 -6
- package/src/__tests__/composables/useRackEditor.test.ts +270 -0
- package/src/__tests__/composables/useSequenceUtils.test.ts +28 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +44 -9
- package/src/__tests__/styles/baseControlsRules.test.ts +115 -0
- package/src/__tests__/styles/controlRefreshRules.test.ts +218 -0
- package/src/__tests__/styles/cssTokens.test.ts +7 -5
- package/src/__tests__/styles/darkModeRules.test.ts +47 -0
- package/src/__tests__/styles/menusShellRefresh.test.ts +75 -0
- package/src/__tests__/templates/templates.test.ts +65 -15
- package/src/__tests__/templates/templatesDeprecation.test.ts +78 -0
- package/src/__tests__/utils/permissions.test.ts +0 -68
- package/src/__tests__/utils/plotlyTemplate.test.ts +69 -0
- package/src/__tests__/utils/sampleTypeMarker.test.ts +54 -0
- package/src/components/ActionMenu.vue +44 -23
- package/src/components/AdductText.story.vue +18 -0
- package/src/components/AdductText.vue +7 -1
- package/src/components/AlertBox.vue +3 -7
- package/src/components/AppAvatarMenu.vue +12 -10
- package/src/components/AppSidebar.vue +1 -1
- package/src/components/AppTopBar.story.vue +47 -4
- package/src/components/AppTopBar.vue +16 -22
- package/src/components/ArtifactSelectorModal.story.vue +1 -1
- package/src/components/ArtifactSelectorModal.vue +48 -160
- package/src/components/AuditTrail.vue +26 -12
- package/src/components/AutoGroupModal.vue +1 -1
- package/src/components/BaseCheckbox.story.vue +58 -2
- package/src/components/BaseCheckbox.vue +21 -3
- package/src/components/BaseInput.story.vue +51 -0
- package/src/components/BaseInput.vue +62 -1
- package/src/components/BaseModal.story.vue +55 -0
- package/src/components/BaseModal.vue +83 -50
- package/src/components/BaseRadioGroup.vue +1 -1
- package/src/components/BaseSlider.story.vue +36 -0
- package/src/components/BaseSlider.vue +56 -8
- package/src/components/BaseToggle.vue +1 -7
- package/src/components/BatchProgressList.vue +50 -52
- package/src/components/BioTemplatePresetWorkspaceView.vue +1 -1
- package/src/components/Breadcrumb.story.vue +1 -1
- package/src/components/Breadcrumb.vue +50 -15
- package/src/components/ChartContainer.story.vue +42 -0
- package/src/components/ChartContainer.vue +22 -1
- package/src/components/ChemicalFormula.story.vue +11 -11
- package/src/components/ChemicalFormula.vue +1 -0
- package/src/components/CollapsibleCard.vue +12 -32
- package/src/components/ComponentBindingRenderer.vue +6 -8
- package/src/components/ConcentrationInput.story.vue +1 -1
- package/src/components/ConcentrationInput.vue +8 -14
- package/src/components/ControlWorkspaceView.vue +1 -1
- package/src/components/DataFrame.story.vue +45 -2
- package/src/components/DataFrame.vue +28 -13
- package/src/components/DatePicker.story.vue +11 -0
- package/src/components/DatePicker.vue +5 -5
- package/src/components/DateTimePicker.story.vue +1 -1
- package/src/components/DateTimePicker.vue +8 -5
- package/src/components/Divider.story.vue +1 -1
- package/src/components/DoseCalculator.vue +44 -51
- package/src/components/DoseCalculatorVolumeField.vue +22 -35
- package/src/components/DoseDesignWorkspaceView.vue +1 -1
- package/src/components/EmptyState.story.vue +4 -4
- package/src/components/EmptyState.vue +4 -2
- package/src/components/ExperimentPopover.story.vue +2 -2
- package/src/components/ExperimentPopover.vue +6 -4
- package/src/components/ExperimentSelectorModal.story.vue +2 -1
- package/src/components/ExperimentSelectorModal.vue +91 -261
- package/src/components/ExperimentTimeline.story.vue +29 -0
- package/src/components/ExperimentTimeline.vue +20 -0
- package/src/components/FilePicker.story.vue +65 -10
- package/src/components/FilePicker.vue +203 -121
- package/src/components/FitPanel.story.vue +72 -6
- package/src/components/FitPanel.vue +70 -18
- package/src/components/FormField.vue +2 -0
- package/src/components/FormulaInput.story.vue +6 -0
- package/src/components/FormulaInput.vue +49 -70
- package/src/components/GeneratedPathInput.story.vue +121 -8
- package/src/components/GeneratedPathInput.vue +155 -86
- package/src/components/GeneratedPlotlyResult.vue +1 -1
- package/src/components/GeneratedPluginView.vue +4 -4
- package/src/components/GeneratedResultRenderer.vue +19 -0
- package/src/components/InstrumentStateBadge.story.vue +41 -0
- package/src/components/InstrumentStateBadge.vue +24 -14
- package/src/components/JobsStatusTray.vue +30 -8
- package/src/components/MoleculeInput.vue +12 -7
- package/src/components/MultiSelect.vue +3 -3
- package/src/components/PlateEditor.story.vue +333 -0
- package/src/components/PlateEditor.vue +793 -0
- package/src/components/PlotlyChart.story.vue +37 -1
- package/src/components/PlotlyChart.vue +86 -16
- package/src/components/PluginWorkspaceView.props.ts +1 -7
- package/src/components/PluginWorkspaceView.vue +0 -4
- package/src/components/ProgressBar.story.vue +0 -30
- package/src/components/ProgressBar.vue +3 -53
- package/src/components/ProtocolStepEditor.story.vue +8 -0
- package/src/components/ProtocolStepEditor.vue +55 -145
- package/src/components/ProtocolStepParameterField.vue +55 -93
- package/src/components/ReagentList.presentation.ts +7 -6
- package/src/components/ReagentList.vue +18 -17
- package/src/components/SampleHierarchyTree.story.vue +19 -0
- package/src/components/SampleHierarchyTree.vue +47 -14
- package/src/components/SampleLegend.vue +12 -4
- package/src/components/SampleSelector.story.vue +5 -1
- package/src/components/SampleSelector.vue +25 -15
- package/src/components/SampleSelectorGroupNode.vue +10 -20
- package/src/components/SampleSelectorSampleRow.vue +7 -18
- package/src/components/ScheduleCalendar.story.vue +14 -0
- package/src/components/ScheduleCalendar.vue +13 -1
- package/src/components/ScientificNumber.story.vue +15 -2
- package/src/components/ScientificNumber.vue +22 -26
- package/src/components/SearchableSelect.vue +14 -9
- package/src/components/SecretInput.vue +1 -1
- package/src/components/SegmentedControl.story.vue +23 -0
- package/src/components/SegmentedControl.vue +6 -1
- package/src/components/SequenceInput.story.vue +27 -5
- package/src/components/SequenceInput.vue +74 -29
- package/src/components/SequenceProgressBar.vue +0 -1
- package/src/components/SettingsModal.schema.ts +4 -2
- package/src/components/SettingsModal.story.vue +83 -22
- package/src/components/SettingsModal.vue +338 -79
- package/src/components/Skeleton.vue +6 -35
- package/src/components/SmartGroup.types.ts +2 -2
- package/src/components/SmartGroupFieldRecipe.groups.ts +4 -1
- package/src/components/SmartGroupFieldRecipe.vue +14 -8
- package/src/components/SmartGroupManual.vue +14 -11
- package/src/components/StepWizard.vue +16 -19
- package/src/components/TagsInput.story.vue +16 -11
- package/src/components/TagsInput.vue +20 -3
- package/src/components/TimePicker.story.vue +38 -3
- package/src/components/TimePicker.vue +26 -11
- package/src/components/TimeRangeInput.story.vue +50 -1
- package/src/components/TimeRangeInput.vue +36 -17
- package/src/components/UnitInput.story.vue +1 -1
- package/src/components/UnitInput.vue +173 -107
- package/src/components/WellPlate.colors.ts +7 -6
- package/src/components/WellPlate.geometry.ts +3 -0
- package/src/components/WellPlate.interaction.ts +39 -5
- package/src/components/WellPlate.rendering.ts +21 -27
- package/src/components/WellPlate.sampleDrop.ts +20 -12
- package/src/components/WellPlate.story.vue +18 -7
- package/src/components/WellPlate.vue +28 -22
- package/src/components/index.ts +34 -31
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +13 -7
- package/src/components/internal/AppTopBarPillNavInternal.vue +12 -7
- package/src/components/internal/CalendarGridPanelInternal.vue +1 -1
- package/src/components/internal/FilePickerListInternal.vue +318 -0
- package/src/components/internal/FilePickerPreviewInternal.vue +54 -52
- package/src/components/internal/PlateEditorBindingInternal.vue +37 -0
- package/src/components/internal/ResourcePickerListInternal.vue +237 -0
- package/src/components/internal/SettingsPaneHeadInternal.vue +41 -0
- package/src/components/internal/WellEditPopupInternal.vue +219 -146
- package/src/components/internal/WorkspaceShellInternal.vue +0 -6
- package/src/components/internal/filePickerPresentation.ts +30 -0
- package/src/components/internal/generatedJobsApi.ts +12 -0
- package/src/composables/autoGroup/index.ts +3 -0
- package/src/composables/controlSchemaAdapters.ts +0 -4
- package/src/composables/controlSchemaFormFields.ts +0 -4
- package/src/composables/controlSchemaTypes.ts +4 -43
- package/src/composables/createFilePickerAdapter.ts +90 -22
- package/src/composables/filePicker/localFiles.ts +1 -0
- package/src/composables/filePicker/usePickerNavigation.ts +29 -2
- package/src/composables/index.ts +2 -71
- package/src/composables/pluginWorkspaceModel.ts +0 -3
- package/src/composables/useAutoGroup.ts +10 -0
- package/src/composables/useBioTemplateComponents.ts +15 -3
- package/src/composables/useBioTemplateControls.ts +5 -1
- package/src/composables/useBioTemplatePackWorkspace.ts +2 -0
- package/src/composables/useBioTemplatePresetWorkspace.ts +5 -1
- package/src/composables/useBioTemplateWorkspace.ts +5 -1
- package/src/composables/useChemicalFormula.ts +39 -13
- package/src/composables/useDropdownState.ts +6 -1
- package/src/composables/useExperimentSave.ts +5 -1
- package/src/composables/useFileBrowser.ts +3 -2
- package/src/composables/useFilePicker.ts +98 -13
- package/src/composables/useFocusTrap.ts +9 -6
- package/src/composables/useGeneratedAnalysis.ts +38 -0
- package/src/composables/useGroupAssignment.ts +5 -1
- package/src/composables/useJobsStatusTray.ts +5 -5
- package/src/composables/useMenuKeyboard.ts +5 -3
- package/src/composables/usePlatformFilePickerAdapter.ts +26 -4
- package/src/composables/usePluginClient.ts +1 -1
- package/src/composables/usePluginWorkspace.ts +0 -1
- package/src/composables/useRackEditor.ts +202 -12
- package/src/composables/useSequenceUtils.ts +20 -2
- package/src/composables/useTemplateCollection.ts +5 -1
- package/src/composables/workspaceRecipes.ts +1 -0
- package/src/index.ts +9 -3
- package/src/instrument.ts +6 -0
- package/src/lcms.ts +11 -0
- package/src/permissions.ts +2 -59
- package/src/styles/components/action-menu.css +9 -38
- package/src/styles/components/adduct-text.css +15 -3
- package/src/styles/components/alert-box.css +19 -70
- package/src/styles/components/app-avatar-menu.css +22 -57
- package/src/styles/components/app-container.css +4 -4
- package/src/styles/components/app-layout.css +18 -18
- package/src/styles/components/app-page-selector.css +50 -105
- package/src/styles/components/app-pill-nav.css +24 -42
- package/src/styles/components/app-sidebar.css +2 -2
- package/src/styles/components/app-top-bar.css +78 -55
- package/src/styles/components/artifact-selector-modal.css +1 -211
- package/src/styles/components/audit-trail.css +45 -38
- package/src/styles/components/avatar.css +1 -1
- package/src/styles/components/batch-progress-list.css +179 -95
- package/src/styles/components/breadcrumb.css +34 -15
- package/src/styles/components/calendar.css +2 -8
- package/src/styles/components/chart-container.css +81 -38
- package/src/styles/components/checkbox.css +8 -6
- package/src/styles/components/chemical-formula.css +8 -6
- package/src/styles/components/collapsible-card.css +6 -46
- package/src/styles/components/concentration-input.css +1 -10
- package/src/styles/components/dataframe.css +48 -67
- package/src/styles/components/date-picker.css +124 -62
- package/src/styles/components/datetime-picker.css +81 -47
- package/src/styles/components/divider.css +2 -4
- package/src/styles/components/dose-calculator.css +9 -135
- package/src/styles/components/empty-state.css +26 -35
- package/src/styles/components/experiment-code-badge.css +1 -1
- package/src/styles/components/experiment-popover.css +16 -83
- package/src/styles/components/experiment-selector-modal.css +9 -226
- package/src/styles/components/experiment-timeline.css +120 -50
- package/src/styles/components/file-picker.css +323 -299
- package/src/styles/components/file-uploader.css +4 -6
- package/src/styles/components/fit-panel.css +108 -22
- package/src/styles/components/form-field.css +6 -4
- package/src/styles/components/formula-input.css +121 -48
- package/src/styles/components/group-assigner.css +1 -3
- package/src/styles/components/icon-button.css +15 -37
- package/src/styles/components/input.css +91 -2
- package/src/styles/components/instrument-monitor.css +37 -447
- package/src/styles/components/jobs-status-tray.css +82 -61
- package/src/styles/components/loading-spinner.css +18 -0
- package/src/styles/components/mobile-support-gate.css +15 -17
- package/src/styles/components/modal.css +44 -33
- package/src/styles/components/molecule-input.css +31 -16
- package/src/styles/components/multi-select.css +33 -29
- package/src/styles/components/number-input.css +1 -1
- package/src/styles/components/plate-editor.css +497 -0
- package/src/styles/components/popover.css +162 -0
- package/src/styles/components/progress-bar.css +1 -70
- package/src/styles/components/protocol-step-editor.css +21 -333
- package/src/styles/components/radio-group.css +9 -18
- package/src/styles/components/reagent-editor.css +3 -7
- package/src/styles/components/reagent-list.css +63 -69
- package/src/styles/components/resource-card.css +4 -6
- package/src/styles/components/resource-picker-list.css +212 -0
- package/src/styles/components/sample-hierarchy-tree.css +83 -65
- package/src/styles/components/sample-legend.css +18 -7
- package/src/styles/components/sample-selector.css +41 -68
- package/src/styles/components/schedule-calendar.css +59 -54
- package/src/styles/components/scientific-number.css +72 -18
- package/src/styles/components/searchable-select.css +17 -26
- package/src/styles/components/secret-input.css +20 -23
- package/src/styles/components/section-card.css +3 -5
- package/src/styles/components/segmented-control.css +14 -1
- package/src/styles/components/select.css +19 -8
- package/src/styles/components/sequence-input.css +85 -37
- package/src/styles/components/sequence-progress-bar.css +18 -37
- package/src/styles/components/settings-modal.css +683 -34
- package/src/styles/components/skeleton.css +24 -23
- package/src/styles/components/slider.css +74 -8
- package/src/styles/components/smart-group.css +27 -39
- package/src/styles/components/status-indicator.css +1 -1
- package/src/styles/components/step-wizard.css +62 -99
- package/src/styles/components/tabs.css +2 -6
- package/src/styles/components/tags-input.css +64 -54
- package/src/styles/components/textarea.css +2 -2
- package/src/styles/components/time-picker.css +67 -38
- package/src/styles/components/time-range-input.css +16 -18
- package/src/styles/components/toast.css +1 -1
- package/src/styles/components/toggle.css +21 -31
- package/src/styles/components/tooltip.css +13 -15
- package/src/styles/components/unit-input.css +86 -36
- package/src/styles/components/well-edit-popup.css +111 -189
- package/src/styles/components/well-plate.css +7 -7
- package/src/styles/index.css +5 -7
- package/src/styles/variables.css +35 -7
- package/src/templates/adapters.ts +4 -0
- package/src/templates/assayMatrixAdapters.ts +8 -1
- package/src/templates/assayMatrixBuilder.ts +1 -0
- package/src/templates/builderPresetControls.ts +1 -0
- package/src/templates/calibrationCurveAdapters.ts +3 -0
- package/src/templates/calibrationCurveBuilder.ts +1 -0
- package/src/templates/catalog.ts +6 -2
- package/src/templates/componentBindingCatalog.ts +1 -1
- package/src/templates/componentBindings.ts +31 -5
- package/src/templates/componentDoseResponseProps.ts +0 -1
- package/src/templates/componentPlateHelpers.ts +22 -1
- package/src/templates/componentPlateMapProps.ts +7 -5
- package/src/templates/componentQpcrPlateProps.ts +0 -1
- package/src/templates/controlSchemas.ts +9 -0
- package/src/templates/defaultBioTemplateBuilder.ts +1 -0
- package/src/templates/doseResponseAdapters.ts +2 -0
- package/src/templates/doseResponseBuilder.ts +1 -0
- package/src/templates/elisaAssayCollectionBuilder.ts +1 -0
- package/src/templates/flowCytometryAssayCollectionBuilder.ts +1 -0
- package/src/templates/flowCytometryPanelBuilder.ts +1 -0
- package/src/templates/flowPanelAdapters.ts +3 -0
- package/src/templates/index.ts +4 -0
- package/src/templates/instrumentRunAdapters.ts +13 -2
- package/src/templates/instrumentRunBuilder.ts +1 -0
- package/src/templates/lcmsBatchCollectionBuilder.ts +1 -0
- package/src/templates/packs.ts +4 -0
- package/src/templates/plateMapAdapters.ts +2 -0
- package/src/templates/plateMapBuilder.ts +1 -0
- package/src/templates/presets.ts +6 -2
- package/src/templates/protocolAdapters.ts +4 -0
- package/src/templates/protocolStepsBuilder.ts +1 -0
- package/src/templates/qpcrAdapters.ts +4 -0
- package/src/templates/qpcrExpressionCollectionBuilder.ts +1 -0
- package/src/templates/qpcrPlateBuilder.ts +1 -0
- package/src/templates/reagentAdapters.ts +4 -0
- package/src/templates/reagentListBuilder.ts +1 -0
- package/src/templates/samplePrepAdapters.ts +3 -0
- package/src/templates/samplePrepBuilder.ts +1 -0
- package/src/templates/sampleSheetAdapters.ts +4 -0
- package/src/templates/sampleSheetBuilder.ts +1 -0
- package/src/templates/targetedMetabolomicsCollectionBuilder.ts +1 -0
- package/src/templates/templateEnvelopes.ts +8 -0
- package/src/templates/templatePackCollectionBuilder.ts +1 -0
- package/src/templates/templatePresetCollectionBuilder.ts +2 -0
- package/src/templates/templateValidators.ts +12 -0
- package/src/templates/timeCourseAdapters.ts +4 -0
- package/src/templates/timeCourseBuilder.ts +1 -0
- package/src/templates/wellPlateScreenCollectionBuilder.ts +1 -0
- package/src/templates/westernBlotAssayCollectionBuilder.ts +1 -0
- package/src/types/auth.ts +0 -37
- package/src/types/componentLabTypes.ts +16 -13
- package/src/types/componentWorkflowTypes.ts +29 -0
- package/src/types/components.ts +43 -20
- package/src/types/fileBrowserTypes.ts +15 -0
- package/src/types/filePicker.ts +19 -1
- package/src/types/generated-ui.ts +15 -0
- package/src/types/index.ts +12 -11
- package/src/types/instrument.ts +47 -15
- package/src/types/platform.ts +2 -0
- package/src/utils/color.ts +16 -1
- package/src/utils/formModelSync.ts +1 -1
- package/src/utils/plotlyTemplate.ts +39 -0
- package/src/utils/rack.ts +16 -0
- package/src/utils/sampleTypeMarker.ts +44 -0
- package/dist/ExperimentPopover-CJdNlCZb.js +0 -451
- package/dist/ExperimentPopover-CJdNlCZb.js.map +0 -1
- package/dist/ExperimentSelectorModal-DHE9k8TP.js +0 -507
- package/dist/ExperimentSelectorModal-DHE9k8TP.js.map +0 -1
- package/dist/SettingsModal-DELMkUFR.js +0 -492
- package/dist/SettingsModal-DELMkUFR.js.map +0 -1
- package/dist/Skeleton-jhF9wUkU.js +0 -485
- package/dist/Skeleton-jhF9wUkU.js.map +0 -1
- package/dist/__tests__/composables/usePluginConfig.test.d.ts +0 -15
- package/dist/chunk-BoAXSpZd.js +0 -33
- package/dist/components/AppPluginSwitcher.vue.d.ts +0 -40
- package/dist/components/ColorSlider.vue.d.ts +0 -34
- package/dist/components/DropdownButton.vue.d.ts +0 -49
- package/dist/components/FileBrowserModal.vue.d.ts +0 -99
- package/dist/components/InstrumentAlertLog.vue.d.ts +0 -22
- package/dist/components/InstrumentStatusCard.vue.d.ts +0 -13
- package/dist/components/LcmsSequenceTable.vue.d.ts +0 -54
- package/dist/components/PlateMapEditor.vue.d.ts +0 -50
- package/dist/components/RackEditor.vue.d.ts +0 -78
- package/dist/components/fileBrowserPresentation.d.ts +0 -10
- package/dist/components/internal/FileBrowserDialogInternal.vue.d.ts +0 -34
- package/dist/components/internal/FileBrowserListingInternal.vue.d.ts +0 -25
- package/dist/components/internal/FileBrowserToolbarInternal.vue.d.ts +0 -26
- package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +0 -28
- package/dist/components-Bbg_gtzF.js.map +0 -1
- package/dist/composables/useAsync.d.ts +0 -132
- package/dist/composables/useAuth.d.ts +0 -47
- package/dist/composables/useCommandHistory.d.ts +0 -33
- package/dist/composables/useOptimisticMutation.d.ts +0 -32
- package/dist/composables/usePasskey.d.ts +0 -10
- package/dist/composables/usePluginConfig.d.ts +0 -27
- package/dist/composables/useResourceCrud.d.ts +0 -44
- package/dist/composables/useWellPainting.d.ts +0 -39
- package/dist/composables/useWellPlateAdapter.d.ts +0 -40
- package/dist/composables/useWellPlateEditor.d.ts +0 -33
- package/dist/composables/useWellPlateValidation.d.ts +0 -31
- package/dist/composables-BCbYb2lA.js +0 -2356
- package/dist/composables-BCbYb2lA.js.map +0 -1
- package/dist/errors-BLuqMee5.js.map +0 -1
- package/dist/experiment-CVDRAnqp.js +0 -90
- package/dist/experiment-CVDRAnqp.js.map +0 -1
- package/dist/experiment-utils-CV2Wza3z.js.map +0 -1
- package/dist/plotly.min-c2Sez60c.js +0 -180634
- package/dist/plotly.min-c2Sez60c.js.map +0 -1
- package/dist/templates-NuaNHohp.js +0 -5260
- package/dist/templates-NuaNHohp.js.map +0 -1
- package/dist/useControlSchema-vLiRPUmj.js +0 -7301
- package/dist/useControlSchema-vLiRPUmj.js.map +0 -1
- package/dist/useDropdownState-C7hRiVQf.js +0 -63
- package/dist/useDropdownState-C7hRiVQf.js.map +0 -1
- package/dist/useExperimentSelector-G_CAOFTX.js +0 -328
- package/dist/useExperimentSelector-G_CAOFTX.js.map +0 -1
- package/dist/useFormBuilder-D37ajgS8.js +0 -729
- package/dist/useFormBuilder-D37ajgS8.js.map +0 -1
- package/dist/validation-ChMyixAf.js.map +0 -1
- package/src/__tests__/components/AppPluginSwitcher.test.ts +0 -44
- package/src/__tests__/components/DropdownButton.test.ts +0 -628
- package/src/__tests__/components/FileBrowserModal.test.ts +0 -460
- package/src/__tests__/components/InstrumentAlertLog.test.ts +0 -59
- package/src/__tests__/components/LcmsSequenceTable.test.ts +0 -79
- package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +0 -54
- package/src/__tests__/components/RackEditor.test.ts +0 -169
- package/src/__tests__/composables/useAuth.test.ts +0 -233
- package/src/__tests__/composables/useCommandHistory.test.ts +0 -43
- package/src/__tests__/composables/useOptimisticMutation.test.ts +0 -40
- package/src/__tests__/composables/usePluginConfig.test.ts +0 -497
- package/src/__tests__/composables/useResourceCrud.test.ts +0 -56
- package/src/__tests__/composables/useWellPainting.test.ts +0 -52
- package/src/__tests__/composables/useWellPlateAdapter.test.ts +0 -50
- package/src/__tests__/composables/useWellPlateValidation.test.ts +0 -42
- package/src/components/AppPluginSwitcher.story.vue +0 -87
- package/src/components/AppPluginSwitcher.vue +0 -134
- package/src/components/ColorSlider.story.vue +0 -120
- package/src/components/ColorSlider.vue +0 -160
- package/src/components/DropdownButton.story.vue +0 -102
- package/src/components/DropdownButton.vue +0 -181
- package/src/components/FileBrowserModal.story.vue +0 -203
- package/src/components/FileBrowserModal.vue +0 -384
- package/src/components/InstrumentAlertLog.vue +0 -187
- package/src/components/InstrumentStatusCard.vue +0 -184
- package/src/components/LcmsSequenceTable.vue +0 -222
- package/src/components/PlateMapEditor.story.vue +0 -92
- package/src/components/PlateMapEditor.vue +0 -423
- package/src/components/RackEditor.story.vue +0 -100
- package/src/components/RackEditor.vue +0 -429
- package/src/components/fileBrowserPresentation.ts +0 -39
- package/src/components/internal/FileBrowserDialogInternal.vue +0 -75
- package/src/components/internal/FileBrowserListingInternal.vue +0 -197
- package/src/components/internal/FileBrowserToolbarInternal.vue +0 -136
- package/src/components/internal/FilePickerTreeInternal.vue +0 -238
- package/src/components/internal/PlateMapEditorToolbarInternal.vue +0 -128
- package/src/composables/useAsync.ts +0 -327
- package/src/composables/useAuth.ts +0 -458
- package/src/composables/useCommandHistory.ts +0 -117
- package/src/composables/useOptimisticMutation.ts +0 -97
- package/src/composables/usePasskey.ts +0 -216
- package/src/composables/usePluginConfig.ts +0 -252
- package/src/composables/useResourceCrud.ts +0 -235
- package/src/composables/useWellPainting.ts +0 -167
- package/src/composables/useWellPlateAdapter.ts +0 -151
- package/src/composables/useWellPlateEditor.ts +0 -415
- package/src/composables/useWellPlateValidation.ts +0 -89
- package/src/plotly-dist.d.ts +0 -3
- package/src/styles/components/app-plugin-switcher.css +0 -196
- package/src/styles/components/color-slider.css +0 -110
- package/src/styles/components/dropdown-button.css +0 -280
- package/src/styles/components/file-browser-modal.css +0 -489
- package/src/styles/components/lcms-sequence-table.css +0 -204
- package/src/styles/components/plate-map-editor.css +0 -464
- package/src/styles/components/rack-editor.css +0 -287
- /package/dist/__tests__/components/{AppPluginSwitcher.test.d.ts → AlertBox.test.d.ts} +0 -0
- /package/dist/__tests__/components/{DropdownButton.test.d.ts → AuditTrail.test.d.ts} +0 -0
- /package/dist/__tests__/components/{FileBrowserModal.test.d.ts → BaseCheckbox.test.d.ts} +0 -0
- /package/dist/__tests__/components/{InstrumentAlertLog.test.d.ts → BaseSlider.test.d.ts} +0 -0
- /package/dist/__tests__/components/{LcmsSequenceTable.test.d.ts → DoseCalculator.test.d.ts} +0 -0
- /package/dist/__tests__/components/{PlateMapEditorToolbarInternal.test.d.ts → FileUploader.test.d.ts} +0 -0
- /package/dist/__tests__/components/{RackEditor.test.d.ts → FitPanel.test.d.ts} +0 -0
- /package/dist/__tests__/{composables/useAuth.test.d.ts → components/FormulaInput.test.d.ts} +0 -0
- /package/dist/__tests__/{composables/useCommandHistory.test.d.ts → components/InstrumentStateBadge.test.d.ts} +0 -0
- /package/dist/__tests__/{composables/useOptimisticMutation.test.d.ts → components/MoleculeInput.test.d.ts} +0 -0
- /package/dist/__tests__/{composables/useResourceCrud.test.d.ts → components/PlateEditor.test.d.ts} +0 -0
- /package/dist/__tests__/{composables/useWellPainting.test.d.ts → components/PlateEditorBindingInternal.test.d.ts} +0 -0
- /package/dist/__tests__/{composables/useWellPlateAdapter.test.d.ts → components/ResourcePickerListInternal.test.d.ts} +0 -0
- /package/dist/__tests__/{composables/useWellPlateValidation.test.d.ts → components/SampleLegend.test.d.ts} +0 -0
|
@@ -0,0 +1,1134 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { h, nextTick } from 'vue'
|
|
4
|
+
import PlateEditor from '../../components/PlateEditor.vue'
|
|
5
|
+
import WellPlate from '../../components/WellPlate.vue'
|
|
6
|
+
import ConfirmDialog from '../../components/ConfirmDialog.vue'
|
|
7
|
+
import ActionMenu from '../../components/ActionMenu.vue'
|
|
8
|
+
import WellEditPopupInternal from '../../components/internal/WellEditPopupInternal.vue'
|
|
9
|
+
import type { Rack, WellEditData } from '../../types'
|
|
10
|
+
|
|
11
|
+
function makeRacks(): Rack[] {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
id: 'rack-1',
|
|
15
|
+
name: 'Rack 1',
|
|
16
|
+
format: 54,
|
|
17
|
+
slot: 'R',
|
|
18
|
+
injectionVolume: 5,
|
|
19
|
+
wells: {
|
|
20
|
+
A1: { sampleType: 'sample', state: 'filled', metadata: { label: 'S001', injectionCount: 1 } },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{ id: 'rack-2', name: 'Rack 2', format: 96, slot: 'G', injectionVolume: 5, wells: {} },
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const editData: WellEditData = {
|
|
28
|
+
wellId: 'A2',
|
|
29
|
+
label: 'Renamed',
|
|
30
|
+
sampleType: 'qc',
|
|
31
|
+
injectionVolume: 5,
|
|
32
|
+
injectionCount: 2,
|
|
33
|
+
customMethod: '',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function mountEditor(props: Record<string, unknown> = {}, options: Record<string, unknown> = {}) {
|
|
37
|
+
return mount(PlateEditor, {
|
|
38
|
+
props: { modelValue: makeRacks(), ...props },
|
|
39
|
+
global: { stubs: { teleport: true } },
|
|
40
|
+
...options,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Mounts at a wide width, so the rail shows (happy-dom measures every box as 0 wide). */
|
|
45
|
+
async function mountWide(props: Record<string, unknown> = {}, options: Record<string, unknown> = {}) {
|
|
46
|
+
const spy = vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockReturnValue({ width: 960 } as DOMRect)
|
|
47
|
+
try {
|
|
48
|
+
const wrapper = mountEditor(props, options)
|
|
49
|
+
await nextTick()
|
|
50
|
+
return wrapper
|
|
51
|
+
} finally {
|
|
52
|
+
spy.mockRestore()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function plate(wrapper: ReturnType<typeof mountEditor>) {
|
|
57
|
+
return wrapper.findComponent(WellPlate)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function dropEvent(data: Record<string, string>): DragEvent {
|
|
61
|
+
return {
|
|
62
|
+
preventDefault: vi.fn(),
|
|
63
|
+
dataTransfer: { getData: (type: string) => data[type] ?? '' },
|
|
64
|
+
} as unknown as DragEvent
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function cell(wrapper: ReturnType<typeof mountEditor>, wellId: string) {
|
|
68
|
+
return wrapper.get(`[role="gridcell"][title^="${wellId}"]`)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function tabs(wrapper: ReturnType<typeof mountEditor>) {
|
|
72
|
+
return wrapper.findAll('[role="tab"]')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
describe('PlateEditor — controlled data', () => {
|
|
76
|
+
it('should leave modelValue untouched when a well is edited', async () => {
|
|
77
|
+
const racks = makeRacks()
|
|
78
|
+
const snapshot = structuredClone(racks)
|
|
79
|
+
const wrapper = mountEditor({ modelValue: racks, sampleDrop: true, fillSeries: true, clear: 'undo' })
|
|
80
|
+
|
|
81
|
+
plate(wrapper).vm.$emit('well-edit', 'A2', editData)
|
|
82
|
+
plate(wrapper).vm.$emit('well-move', 'A1', 'B1')
|
|
83
|
+
await wrapper.get('.mint-plate-editor__action--danger').trigger('click')
|
|
84
|
+
await wrapper.get('[data-action="fill-series"]').trigger('click')
|
|
85
|
+
|
|
86
|
+
expect(racks).toEqual(snapshot)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should emit well-edit with the active rack id', () => {
|
|
90
|
+
const wrapper = mountEditor()
|
|
91
|
+
|
|
92
|
+
plate(wrapper).vm.$emit('well-edit', 'A2', editData)
|
|
93
|
+
|
|
94
|
+
expect(wrapper.emitted('well-edit')?.[0]).toEqual(['rack-1', 'A2', editData])
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('should emit well-clear when a well is saved without a name', () => {
|
|
98
|
+
const wrapper = mountEditor()
|
|
99
|
+
|
|
100
|
+
plate(wrapper).vm.$emit('well-edit', 'A1', { ...editData, wellId: 'A1', label: '', sampleType: 'sample' })
|
|
101
|
+
|
|
102
|
+
expect(wrapper.emitted('well-clear')?.[0]).toEqual(['rack-1', 'A1'])
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should keep an unnamed blank well when it is saved', () => {
|
|
106
|
+
const wrapper = mountEditor()
|
|
107
|
+
|
|
108
|
+
plate(wrapper).vm.$emit('well-edit', 'A2', { ...editData, label: '', sampleType: 'blank' })
|
|
109
|
+
|
|
110
|
+
expect(wrapper.emitted('well-clear')).toBeUndefined()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should emit well-click in readonly mode', () => {
|
|
114
|
+
const wrapper = mountEditor({ readonly: true })
|
|
115
|
+
|
|
116
|
+
plate(wrapper).vm.$emit('well-click', 'A1', new MouseEvent('click'))
|
|
117
|
+
|
|
118
|
+
expect(wrapper.emitted('well-click')?.[0]).toEqual(['rack-1', 'A1'])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should hide the toolbar in readonly mode', () => {
|
|
122
|
+
const wrapper = mountEditor({ readonly: true, slots: true, fillSeries: true })
|
|
123
|
+
|
|
124
|
+
expect(wrapper.find('.mint-plate-editor__toolbar').exists()).toBe(false)
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
describe('PlateEditor — plate tabs', () => {
|
|
129
|
+
it('should emit update:activeRackId when a tab is clicked', async () => {
|
|
130
|
+
const wrapper = mountEditor()
|
|
131
|
+
|
|
132
|
+
await tabs(wrapper)[1].trigger('click')
|
|
133
|
+
|
|
134
|
+
expect(wrapper.emitted('update:activeRackId')?.[0]).toEqual(['rack-2'])
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('should show the second rack after its tab is clicked without a bound activeRackId', async () => {
|
|
138
|
+
const wrapper = mountEditor()
|
|
139
|
+
|
|
140
|
+
await tabs(wrapper)[1].trigger('click')
|
|
141
|
+
|
|
142
|
+
expect(plate(wrapper).props('format')).toBe(96)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should keep showing the bound rack when the parent ignores a tab change', async () => {
|
|
146
|
+
const wrapper = mountEditor({ activeRackId: 'rack-1', 'onUpdate:activeRackId': () => {} })
|
|
147
|
+
|
|
148
|
+
await tabs(wrapper)[1].trigger('click')
|
|
149
|
+
|
|
150
|
+
expect(plate(wrapper).props('format')).toBe(54)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should show the rack named by a new activeRackId', async () => {
|
|
154
|
+
const wrapper = mountEditor({ activeRackId: 'rack-1' })
|
|
155
|
+
|
|
156
|
+
await wrapper.setProps({ activeRackId: 'rack-2' })
|
|
157
|
+
|
|
158
|
+
expect(plate(wrapper).props('format')).toBe(96)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('should render the well count as a bare mono count', () => {
|
|
162
|
+
const wrapper = mountEditor()
|
|
163
|
+
|
|
164
|
+
expect(tabs(wrapper)[0].get('.mint-plate-editor__tab-count').text()).toBe('1')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('should underline the active tab together with its close button', () => {
|
|
168
|
+
const wrapper = mountEditor()
|
|
169
|
+
|
|
170
|
+
expect(wrapper.find('.mint-plate-editor__tab-item--active .mint-plate-editor__tab-close').exists()).toBe(true)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('should offer a close button on the active tab only', () => {
|
|
174
|
+
const wrapper = mountEditor()
|
|
175
|
+
|
|
176
|
+
expect(wrapper.findAll('.mint-plate-editor__tab-close')).toHaveLength(1)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('should emit rack-remove from the close button', async () => {
|
|
180
|
+
const wrapper = mountEditor()
|
|
181
|
+
|
|
182
|
+
await wrapper.get('.mint-plate-editor__tab-close').trigger('click')
|
|
183
|
+
|
|
184
|
+
expect(wrapper.emitted('rack-remove')?.[0]).toEqual(['rack-1'])
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should hide the close button at minRacks', () => {
|
|
188
|
+
const wrapper = mountEditor({ minRacks: 2 })
|
|
189
|
+
|
|
190
|
+
expect(wrapper.find('.mint-plate-editor__tab-close').exists()).toBe(false)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('should emit rack-add from the add button', async () => {
|
|
194
|
+
const wrapper = mountEditor()
|
|
195
|
+
|
|
196
|
+
await wrapper.get('.mint-plate-editor__add').trigger('click')
|
|
197
|
+
|
|
198
|
+
expect(wrapper.emitted('rack-add')).toHaveLength(1)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('should hide the add button at maxRacks', () => {
|
|
202
|
+
const wrapper = mountEditor({ maxRacks: 2 })
|
|
203
|
+
|
|
204
|
+
expect(wrapper.find('.mint-plate-editor__add').exists()).toBe(false)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('should emit rack-reorder on Alt+ArrowRight', async () => {
|
|
208
|
+
const wrapper = mountEditor()
|
|
209
|
+
|
|
210
|
+
await tabs(wrapper)[0].trigger('keydown', { key: 'ArrowRight', altKey: true })
|
|
211
|
+
|
|
212
|
+
expect(wrapper.emitted('rack-reorder')?.[0]).toEqual([['rack-2', 'rack-1']])
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('should not reorder when reorder is off', async () => {
|
|
216
|
+
const wrapper = mountEditor({ reorder: false })
|
|
217
|
+
|
|
218
|
+
await tabs(wrapper)[0].trigger('keydown', { key: 'ArrowRight', altKey: true })
|
|
219
|
+
|
|
220
|
+
expect(wrapper.emitted('rack-reorder')).toBeUndefined()
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('should render #tab-extra content', () => {
|
|
224
|
+
const wrapper = mountEditor({}, { slots: { 'tab-extra': () => h('span', { class: 'extra' }, 'x') } })
|
|
225
|
+
|
|
226
|
+
expect(wrapper.find('.extra').exists()).toBe(true)
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
describe('PlateEditor — toolbar', () => {
|
|
231
|
+
it('should name the format radio group', () => {
|
|
232
|
+
const wrapper = mountEditor()
|
|
233
|
+
|
|
234
|
+
expect(wrapper.get('.mint-plate-editor__toolbar [role="radiogroup"]').attributes('aria-label')).toBe('Format')
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('should emit update:format from the format control', async () => {
|
|
238
|
+
const wrapper = mountEditor()
|
|
239
|
+
|
|
240
|
+
await wrapper.findAll('.mint-plate-editor__toolbar [role="radio"]')[1].trigger('click')
|
|
241
|
+
|
|
242
|
+
expect(wrapper.emitted('update:format')?.[0]).toEqual(['rack-1', 96])
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('should hide the format control when only one format is allowed', () => {
|
|
246
|
+
const wrapper = mountEditor({ formats: [54] })
|
|
247
|
+
|
|
248
|
+
expect(wrapper.find('.mint-plate-editor__toolbar [role="radio"]').exists()).toBe(false)
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('should emit update:slot from a slot button', async () => {
|
|
252
|
+
const wrapper = mountEditor({ slots: true })
|
|
253
|
+
|
|
254
|
+
await wrapper.findAll('.mint-plate-editor__slot')[2].trigger('click')
|
|
255
|
+
|
|
256
|
+
expect(wrapper.emitted('update:slot')?.[0]).toEqual(['rack-1', 'B'])
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('should limit slot buttons to the listed slots', () => {
|
|
260
|
+
const wrapper = mountEditor({ slots: ['R', 'G'] })
|
|
261
|
+
|
|
262
|
+
expect(wrapper.findAll('.mint-plate-editor__slot')).toHaveLength(2)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('should mark the active slot as pressed', () => {
|
|
266
|
+
const wrapper = mountEditor({ slots: true })
|
|
267
|
+
|
|
268
|
+
expect(wrapper.get('.mint-plate-editor__slot--active').text()).toBe('R')
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('should emit fill-series from its button', async () => {
|
|
272
|
+
const wrapper = mountEditor({ fillSeries: true })
|
|
273
|
+
|
|
274
|
+
await wrapper.get('[data-action="fill-series"]').trigger('click')
|
|
275
|
+
|
|
276
|
+
expect(wrapper.emitted('fill-series')?.[0]).toEqual(['rack-1'])
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('should disable fill-series when the plate is full', () => {
|
|
280
|
+
const racks = makeRacks()
|
|
281
|
+
racks[0].format = 6
|
|
282
|
+
racks[0].wells = Object.fromEntries(['A1', 'A2', 'A3', 'B1', 'B2', 'B3'].map(id => [id, { sampleType: 'sample' }]))
|
|
283
|
+
const wrapper = mountEditor({ modelValue: racks, fillSeries: true })
|
|
284
|
+
|
|
285
|
+
expect(wrapper.get('[data-action="fill-series"]').attributes('disabled')).toBeDefined()
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
it('should ask for confirmation before clearing when clear is confirm', async () => {
|
|
289
|
+
const wrapper = mountEditor()
|
|
290
|
+
|
|
291
|
+
await wrapper.get('.mint-plate-editor__action--danger').trigger('click')
|
|
292
|
+
|
|
293
|
+
expect(wrapper.emitted('clear')).toBeUndefined()
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it('should emit clear once the confirmation is accepted', async () => {
|
|
297
|
+
const wrapper = mountEditor()
|
|
298
|
+
|
|
299
|
+
await wrapper.get('.mint-plate-editor__action--danger').trigger('click')
|
|
300
|
+
wrapper.findComponent(ConfirmDialog).vm.$emit('confirm')
|
|
301
|
+
|
|
302
|
+
expect(wrapper.emitted('clear')?.[0]).toEqual(['rack-1'])
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('should emit clear at once when clear is undo', async () => {
|
|
306
|
+
const wrapper = mountEditor({ clear: 'undo' })
|
|
307
|
+
|
|
308
|
+
await wrapper.get('.mint-plate-editor__action--danger').trigger('click')
|
|
309
|
+
|
|
310
|
+
expect(wrapper.emitted('clear')?.[0]).toEqual(['rack-1'])
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('should emit undo from the undo button', async () => {
|
|
314
|
+
const wrapper = mountEditor({ clear: 'undo', canUndo: true })
|
|
315
|
+
|
|
316
|
+
await wrapper.get('[aria-label="Undo"]').trigger('click')
|
|
317
|
+
|
|
318
|
+
expect(wrapper.emitted('undo')).toHaveLength(1)
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
it('should emit undo on Ctrl+Z inside the editor', async () => {
|
|
322
|
+
const wrapper = mountEditor({ clear: 'undo', canUndo: true })
|
|
323
|
+
|
|
324
|
+
await wrapper.get('.mint-plate-editor').trigger('keydown', { key: 'z', ctrlKey: true })
|
|
325
|
+
|
|
326
|
+
expect(wrapper.emitted('undo')).toHaveLength(1)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('should hide the clear button when clear is false', () => {
|
|
330
|
+
const wrapper = mountEditor({ clear: false })
|
|
331
|
+
|
|
332
|
+
expect(wrapper.find('.mint-plate-editor__action--danger').exists()).toBe(false)
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
it('should emit the chosen file from import', async () => {
|
|
336
|
+
const wrapper = mountEditor({ import: true })
|
|
337
|
+
const file = new File(['a,b'], 'plate.csv')
|
|
338
|
+
const input = wrapper.get('input[type="file"]')
|
|
339
|
+
Object.defineProperty(input.element, 'files', { value: [file] })
|
|
340
|
+
|
|
341
|
+
await input.trigger('change')
|
|
342
|
+
|
|
343
|
+
expect(wrapper.emitted('import')?.[0]).toEqual([file])
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
it('should render #toolbar-extra content', () => {
|
|
347
|
+
const wrapper = mountEditor({}, { slots: { 'toolbar-extra': () => h('span', { class: 'extra' }, 'x') } })
|
|
348
|
+
|
|
349
|
+
expect(wrapper.find('.mint-plate-editor__toolbar .extra').exists()).toBe(true)
|
|
350
|
+
})
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
describe('PlateEditor — wells', () => {
|
|
354
|
+
it('should switch the plate into drag mode from the Drag toggle', async () => {
|
|
355
|
+
const wrapper = mountEditor({ wellDrag: true })
|
|
356
|
+
|
|
357
|
+
await wrapper.get('[data-action="drag"]').trigger('click')
|
|
358
|
+
|
|
359
|
+
expect(plate(wrapper).props('selectionMode')).toBe('drag')
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
it('should emit well-move with the active rack id', () => {
|
|
363
|
+
const wrapper = mountEditor({ wellDrag: true })
|
|
364
|
+
|
|
365
|
+
plate(wrapper).vm.$emit('well-move', 'A1', 'B2')
|
|
366
|
+
|
|
367
|
+
expect(wrapper.emitted('well-move')?.[0]).toEqual(['rack-1', 'A1', 'B2'])
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('should emit sample-drop for a JSON sample payload', () => {
|
|
371
|
+
const wrapper = mountEditor({ sampleDrop: true })
|
|
372
|
+
const parser = plate(wrapper).props('sampleDropParser') as (e: DragEvent) => unknown
|
|
373
|
+
const data = parser(dropEvent({ 'application/json': JSON.stringify({ id: 's1', sampleName: 'Liver' }) }))
|
|
374
|
+
|
|
375
|
+
expect(data).toMatchObject({ id: 's1', sampleName: 'Liver' })
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
it('should reject a plain-text drop', () => {
|
|
379
|
+
const wrapper = mountEditor({ sampleDrop: true })
|
|
380
|
+
const parser = plate(wrapper).props('sampleDropParser') as (e: DragEvent) => unknown
|
|
381
|
+
|
|
382
|
+
expect(parser(dropEvent({ 'text/plain': 'Liver' }))).toBeNull()
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
it('should pass a custom drop parser through', () => {
|
|
386
|
+
const custom = vi.fn(() => null)
|
|
387
|
+
const wrapper = mountEditor({ sampleDrop: custom })
|
|
388
|
+
|
|
389
|
+
expect(plate(wrapper).props('sampleDropParser')).toBe(custom)
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
it('should forward sample-drop with the active rack id', () => {
|
|
393
|
+
const wrapper = mountEditor({ sampleDrop: true })
|
|
394
|
+
const event = dropEvent({})
|
|
395
|
+
|
|
396
|
+
plate(wrapper).vm.$emit('sample-drop', 'B1', { sampleName: 'Liver' }, event)
|
|
397
|
+
|
|
398
|
+
expect(wrapper.emitted('sample-drop')?.[0]).toEqual(['rack-1', 'B1', { sampleName: 'Liver' }, event])
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
it('should turn off the inspector when wellFields is false', () => {
|
|
402
|
+
const wrapper = mountEditor({ wellFields: false })
|
|
403
|
+
|
|
404
|
+
expect(plate(wrapper).props('editable')).toBe(false)
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
it('should pass rack-aware props to the #well-editor slot', async () => {
|
|
408
|
+
const wrapper = mountEditor({}, {
|
|
409
|
+
slots: { 'well-editor': (props: { rackId: string, wellId: string }) => h('i', { class: 'slot' }, `${props.rackId}/${props.wellId}`) },
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
await wrapper.get('[role="gridcell"]').trigger('click')
|
|
413
|
+
|
|
414
|
+
expect(wrapper.get('.slot').text()).toBe('rack-1/A1')
|
|
415
|
+
})
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
describe('PlateEditor — groups', () => {
|
|
419
|
+
const groups = [
|
|
420
|
+
{ id: 'ctrl', name: 'Control' },
|
|
421
|
+
{ id: 'trt', name: 'Treated', color: 'var(--mint-sample-3)' },
|
|
422
|
+
]
|
|
423
|
+
|
|
424
|
+
it('should show the selection bar once wells are selected', async () => {
|
|
425
|
+
const wrapper = mountEditor({ groups })
|
|
426
|
+
|
|
427
|
+
plate(wrapper).vm.$emit('selection-change', ['A1', 'A2'])
|
|
428
|
+
await nextTick()
|
|
429
|
+
|
|
430
|
+
expect(wrapper.get('.mint-plate-editor__selection').text()).toContain('2 selected')
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
it('should emit assign with the chosen group', async () => {
|
|
434
|
+
const wrapper = mountEditor({ groups })
|
|
435
|
+
plate(wrapper).vm.$emit('selection-change', ['A1', 'A2'])
|
|
436
|
+
await nextTick()
|
|
437
|
+
|
|
438
|
+
await wrapper.findAll('.mint-plate-editor__group')[1].trigger('click')
|
|
439
|
+
|
|
440
|
+
expect(wrapper.emitted('assign')?.[0]).toEqual(['rack-1', ['A1', 'A2'], 'trt'])
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
it('should emit assign without a group from the clear button', async () => {
|
|
444
|
+
const wrapper = mountEditor({ groups })
|
|
445
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
446
|
+
await nextTick()
|
|
447
|
+
|
|
448
|
+
await wrapper.get('[aria-label="Clear selected wells"]').trigger('click')
|
|
449
|
+
|
|
450
|
+
expect(wrapper.emitted('assign')?.[0]).toEqual(['rack-1', ['A1'], undefined])
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
it('should drop the selection bar on cancel', async () => {
|
|
454
|
+
const wrapper = mountEditor({ groups })
|
|
455
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
456
|
+
await nextTick()
|
|
457
|
+
|
|
458
|
+
await wrapper.get('[aria-label="Cancel selection"]').trigger('click')
|
|
459
|
+
|
|
460
|
+
expect(wrapper.find('.mint-plate-editor__selection').exists()).toBe(false)
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
it('should color wells by group id', () => {
|
|
464
|
+
const wrapper = mountEditor({ groups })
|
|
465
|
+
|
|
466
|
+
expect(plate(wrapper).props('sampleColors')).toEqual({ ctrl: 'var(--mint-sample-1)', trt: 'var(--mint-sample-3)' })
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
it('should emit selection-change', () => {
|
|
470
|
+
const wrapper = mountEditor({ groups })
|
|
471
|
+
|
|
472
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
473
|
+
|
|
474
|
+
expect(wrapper.emitted('selection-change')?.[0]).toEqual([['A1']])
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
it('should ignore selection without groups', async () => {
|
|
478
|
+
const wrapper = mountEditor()
|
|
479
|
+
|
|
480
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
481
|
+
await nextTick()
|
|
482
|
+
|
|
483
|
+
expect(wrapper.find('.mint-plate-editor__selection').exists()).toBe(false)
|
|
484
|
+
})
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
describe('PlateEditor — groups and the inspector', () => {
|
|
488
|
+
const groups = [{ id: 'ctrl', name: 'Control' }]
|
|
489
|
+
const grouped = (): Rack[] => [
|
|
490
|
+
{ id: 'rack-1', name: 'Rack 1', format: 96, slot: 'R', injectionVolume: 5, wells: { A1: { sampleType: 'sample', group: 'ctrl', state: 'filled' } } },
|
|
491
|
+
{ id: 'rack-2', name: 'Rack 2', format: 96, slot: 'G', injectionVolume: 5, wells: {} },
|
|
492
|
+
]
|
|
493
|
+
|
|
494
|
+
it('should keep a grouped well saved without a name', () => {
|
|
495
|
+
const wrapper = mountEditor({ modelValue: grouped(), groups })
|
|
496
|
+
|
|
497
|
+
plate(wrapper).vm.$emit('well-edit', 'A1', { ...editData, wellId: 'A1', label: '', sampleType: 'sample' })
|
|
498
|
+
|
|
499
|
+
expect(wrapper.emitted('well-clear')).toBeUndefined()
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
it('should keep the type field in the inspector when there are groups', () => {
|
|
503
|
+
const wrapper = mountEditor({ groups })
|
|
504
|
+
|
|
505
|
+
expect(plate(wrapper).props('editFields')).toContain('sampleType')
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
it('should clear the selection when the plate format changes', async () => {
|
|
509
|
+
const wrapper = mountEditor({ modelValue: grouped(), groups, formats: [96, 384] })
|
|
510
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
511
|
+
await nextTick()
|
|
512
|
+
|
|
513
|
+
await wrapper.setProps({ modelValue: grouped().map(rack => ({ ...rack, format: 384 as const })) })
|
|
514
|
+
|
|
515
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([[]])
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
it('should report the cleared selection when switching plates', async () => {
|
|
519
|
+
const wrapper = mountEditor({ modelValue: grouped(), groups })
|
|
520
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
521
|
+
await nextTick()
|
|
522
|
+
|
|
523
|
+
await tabs(wrapper)[1].trigger('click')
|
|
524
|
+
|
|
525
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([[]])
|
|
526
|
+
})
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
describe('PlateEditor — history and import / export (7a)', () => {
|
|
530
|
+
it('should hide undo and redo without history', () => {
|
|
531
|
+
const wrapper = mountEditor()
|
|
532
|
+
|
|
533
|
+
expect(wrapper.find('[aria-label="Redo"]').exists()).toBe(false)
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
it('should show redo with history', () => {
|
|
537
|
+
const wrapper = mountEditor({ history: true })
|
|
538
|
+
|
|
539
|
+
expect(wrapper.find('[aria-label="Redo"]').exists()).toBe(true)
|
|
540
|
+
})
|
|
541
|
+
|
|
542
|
+
it('should disable undo when canUndo is false', () => {
|
|
543
|
+
const wrapper = mountEditor({ history: true })
|
|
544
|
+
|
|
545
|
+
expect(wrapper.get('[aria-label="Undo"]').attributes('disabled')).toBeDefined()
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
it('should keep focus in the editor when undo runs out of steps', async () => {
|
|
549
|
+
const wrapper = mountEditor({ history: true, canUndo: true }, { attachTo: document.body })
|
|
550
|
+
;(wrapper.get('[aria-label="Undo"]').element as HTMLElement).focus()
|
|
551
|
+
|
|
552
|
+
await wrapper.setProps({ canUndo: false })
|
|
553
|
+
|
|
554
|
+
expect(document.activeElement).toBe(wrapper.get('.mint-plate-editor').element)
|
|
555
|
+
wrapper.unmount()
|
|
556
|
+
})
|
|
557
|
+
|
|
558
|
+
it('should keep focus in the editor when redo runs out of steps', async () => {
|
|
559
|
+
const wrapper = mountEditor({ history: true, canRedo: true }, { attachTo: document.body })
|
|
560
|
+
;(wrapper.get('[aria-label="Redo"]').element as HTMLElement).focus()
|
|
561
|
+
|
|
562
|
+
await wrapper.setProps({ canRedo: false })
|
|
563
|
+
|
|
564
|
+
expect(document.activeElement).toBe(wrapper.get('.mint-plate-editor').element)
|
|
565
|
+
wrapper.unmount()
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
it('should emit redo from the redo button', async () => {
|
|
569
|
+
const wrapper = mountEditor({ history: true, canRedo: true })
|
|
570
|
+
|
|
571
|
+
await wrapper.get('[aria-label="Redo"]').trigger('click')
|
|
572
|
+
|
|
573
|
+
expect(wrapper.emitted('redo')).toHaveLength(1)
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
it('should emit redo on Ctrl+Shift+Z inside the editor', async () => {
|
|
577
|
+
const wrapper = mountEditor({ history: true, canRedo: true })
|
|
578
|
+
|
|
579
|
+
await wrapper.get('.mint-plate-editor').trigger('keydown', { key: 'Z', ctrlKey: true, shiftKey: true })
|
|
580
|
+
|
|
581
|
+
expect(wrapper.emitted('redo')).toHaveLength(1)
|
|
582
|
+
})
|
|
583
|
+
|
|
584
|
+
it('should leave Ctrl+Z in a text field to the field', async () => {
|
|
585
|
+
const wrapper = mountEditor({ history: true, canUndo: true }, {
|
|
586
|
+
slots: { 'toolbar-extra': () => h('input', { class: 'field' }) },
|
|
587
|
+
})
|
|
588
|
+
|
|
589
|
+
await wrapper.get('.field').trigger('keydown', { key: 'z', ctrlKey: true })
|
|
590
|
+
|
|
591
|
+
expect(wrapper.emitted('undo')).toBeUndefined()
|
|
592
|
+
})
|
|
593
|
+
|
|
594
|
+
it('should label the menu Import / Export when both are on', () => {
|
|
595
|
+
const wrapper = mountEditor({ import: true, export: ['csv'] })
|
|
596
|
+
|
|
597
|
+
expect(wrapper.get('[data-action="import-export"]').text()).toBe('Import / Export')
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
it('should emit export with the chosen format', () => {
|
|
601
|
+
const wrapper = mountEditor({ export: ['csv', 'json'] })
|
|
602
|
+
|
|
603
|
+
wrapper.findComponent(ActionMenu).vm.$emit('select', { id: 'export:json', label: 'Export JSON' })
|
|
604
|
+
|
|
605
|
+
expect(wrapper.emitted('export')?.[0]).toEqual(['json'])
|
|
606
|
+
})
|
|
607
|
+
|
|
608
|
+
it('should open the file picker from the import menu item', () => {
|
|
609
|
+
const wrapper = mountEditor({ import: true, export: ['csv'] })
|
|
610
|
+
const click = vi.spyOn(HTMLInputElement.prototype, 'click').mockImplementation(() => {})
|
|
611
|
+
|
|
612
|
+
wrapper.findComponent(ActionMenu).vm.$emit('select', { id: 'import', label: 'Import file' })
|
|
613
|
+
|
|
614
|
+
expect(click).toHaveBeenCalledOnce()
|
|
615
|
+
click.mockRestore()
|
|
616
|
+
})
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
describe('PlateEditor — paste import', () => {
|
|
620
|
+
function pasteDialog(wrapper: ReturnType<typeof mountEditor>) {
|
|
621
|
+
return wrapper.findAllComponents(ConfirmDialog).find(dialog => dialog.props('title') === 'Paste wells')!
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
async function openPaste(wrapper: ReturnType<typeof mountEditor>) {
|
|
625
|
+
wrapper.findComponent(ActionMenu).vm.$emit('select', { id: 'paste', label: 'Paste…' })
|
|
626
|
+
await nextTick()
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
it('should offer Paste in the menu when importMode is both', () => {
|
|
630
|
+
const wrapper = mountEditor({ import: true, importMode: 'both' })
|
|
631
|
+
|
|
632
|
+
expect(wrapper.getComponent(ActionMenu).props('items').map((item: { id: string }) => item.id)).toEqual(['import', 'paste'])
|
|
633
|
+
})
|
|
634
|
+
|
|
635
|
+
it('should label the menu Import when only imports are offered', () => {
|
|
636
|
+
const wrapper = mountEditor({ import: true, importMode: 'both' })
|
|
637
|
+
|
|
638
|
+
expect(wrapper.get('[data-action="import-export"]').text()).toBe('Import')
|
|
639
|
+
})
|
|
640
|
+
|
|
641
|
+
it('should drop the file picker when importMode is paste', () => {
|
|
642
|
+
const wrapper = mountEditor({ import: true, importMode: 'paste', export: ['csv'] })
|
|
643
|
+
|
|
644
|
+
expect(wrapper.find('input[type="file"]').exists()).toBe(false)
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
it('should open the paste dialog from the Paste menu item', async () => {
|
|
648
|
+
const wrapper = mountEditor({ import: true, importMode: 'paste', export: ['csv'] })
|
|
649
|
+
|
|
650
|
+
await openPaste(wrapper)
|
|
651
|
+
|
|
652
|
+
expect(pasteDialog(wrapper).props('modelValue')).toBe(true)
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
it('should emit import-text with the pasted text on confirm', async () => {
|
|
656
|
+
const wrapper = mountEditor({ import: true, importMode: 'paste', export: ['csv'] })
|
|
657
|
+
await openPaste(wrapper)
|
|
658
|
+
await wrapper.get('textarea').setValue('A1\tS001')
|
|
659
|
+
|
|
660
|
+
pasteDialog(wrapper).vm.$emit('confirm')
|
|
661
|
+
|
|
662
|
+
expect(wrapper.emitted('import-text')?.[0]).toEqual(['A1\tS001'])
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
it('should not call a File import listener when text is pasted', async () => {
|
|
666
|
+
const files: File[] = []
|
|
667
|
+
// Typed as the file-only listener callers had before paste existed; it must still type-check.
|
|
668
|
+
const wrapper = mount(PlateEditor, {
|
|
669
|
+
props: { modelValue: makeRacks(), import: true, importMode: 'both', onImport: (file: File) => { files.push(file) } },
|
|
670
|
+
global: { stubs: { teleport: true } },
|
|
671
|
+
})
|
|
672
|
+
await openPaste(wrapper)
|
|
673
|
+
await wrapper.get('textarea').setValue('A1\tS001')
|
|
674
|
+
|
|
675
|
+
pasteDialog(wrapper).vm.$emit('confirm')
|
|
676
|
+
|
|
677
|
+
expect(files).toEqual([])
|
|
678
|
+
})
|
|
679
|
+
|
|
680
|
+
it('should not emit import-text when the pasted text is blank', async () => {
|
|
681
|
+
const wrapper = mountEditor({ import: true, importMode: 'paste', export: ['csv'] })
|
|
682
|
+
await openPaste(wrapper)
|
|
683
|
+
await wrapper.get('textarea').setValue(' ')
|
|
684
|
+
|
|
685
|
+
pasteDialog(wrapper).vm.$emit('confirm')
|
|
686
|
+
|
|
687
|
+
expect(wrapper.emitted('import-text')).toBeUndefined()
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
it('should close the paste dialog on confirm', async () => {
|
|
691
|
+
const wrapper = mountEditor({ import: true, importMode: 'paste', export: ['csv'] })
|
|
692
|
+
await openPaste(wrapper)
|
|
693
|
+
await wrapper.get('textarea').setValue('A1')
|
|
694
|
+
|
|
695
|
+
pasteDialog(wrapper).vm.$emit('confirm')
|
|
696
|
+
await nextTick()
|
|
697
|
+
|
|
698
|
+
expect(pasteDialog(wrapper).props('modelValue')).toBe(false)
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
it('should open an empty paste dialog when the previous paste was canceled', async () => {
|
|
702
|
+
const wrapper = mountEditor({ import: true, importMode: 'paste', export: ['csv'] })
|
|
703
|
+
await openPaste(wrapper)
|
|
704
|
+
await wrapper.get('textarea').setValue('A1\tS001')
|
|
705
|
+
pasteDialog(wrapper).vm.$emit('update:modelValue', false)
|
|
706
|
+
await nextTick()
|
|
707
|
+
|
|
708
|
+
await openPaste(wrapper)
|
|
709
|
+
|
|
710
|
+
expect((wrapper.get('textarea').element as HTMLTextAreaElement).value).toBe('')
|
|
711
|
+
})
|
|
712
|
+
})
|
|
713
|
+
|
|
714
|
+
describe('PlateEditor — Shift-click range', () => {
|
|
715
|
+
it('should emit the range between the edited well and a Shift-clicked well', async () => {
|
|
716
|
+
const wrapper = mountEditor({ groups: [{ id: 'ctrl', name: 'Control' }] })
|
|
717
|
+
|
|
718
|
+
await cell(wrapper, 'A1').trigger('click')
|
|
719
|
+
await cell(wrapper, 'B2').trigger('click', { shiftKey: true })
|
|
720
|
+
|
|
721
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([['A1', 'A2', 'B1', 'B2']])
|
|
722
|
+
})
|
|
723
|
+
|
|
724
|
+
it('should not range from a well clicked on another rack', async () => {
|
|
725
|
+
const wrapper = mountEditor({ groups: [{ id: 'ctrl', name: 'Control' }], activeRackId: 'rack-1' })
|
|
726
|
+
|
|
727
|
+
await cell(wrapper, 'A1').trigger('click')
|
|
728
|
+
await wrapper.setProps({ activeRackId: 'rack-2' })
|
|
729
|
+
await cell(wrapper, 'C3').trigger('click', { shiftKey: true })
|
|
730
|
+
|
|
731
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([['C3']])
|
|
732
|
+
})
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
describe('PlateEditor — compact wells (7a)', () => {
|
|
736
|
+
it('should default to the xs well size', () => {
|
|
737
|
+
const wrapper = mountEditor()
|
|
738
|
+
|
|
739
|
+
expect(plate(wrapper).props('size')).toBe('xs')
|
|
740
|
+
})
|
|
741
|
+
|
|
742
|
+
it('should hide well labels at the xs size', () => {
|
|
743
|
+
const wrapper = mountEditor()
|
|
744
|
+
|
|
745
|
+
expect(plate(wrapper).props('showWellLabels')).toBe(false)
|
|
746
|
+
})
|
|
747
|
+
})
|
|
748
|
+
|
|
749
|
+
describe('PlateEditor — rail (7a)', () => {
|
|
750
|
+
const groups = [
|
|
751
|
+
{ id: 'ctrl', name: 'Control' },
|
|
752
|
+
{ id: 'trt', name: 'Treated' },
|
|
753
|
+
]
|
|
754
|
+
const grouped = (): Rack[] => [
|
|
755
|
+
{ id: 'rack-1', name: 'P1', format: 96, slot: 'R', injectionVolume: 5, wells: { A1: { sampleType: 'sample', group: 'ctrl', state: 'filled' }, A2: { sampleType: 'qc', group: 'ctrl', state: 'filled' } } },
|
|
756
|
+
]
|
|
757
|
+
|
|
758
|
+
it('should collapse the rail in a narrow container', () => {
|
|
759
|
+
const wrapper = mountEditor({ groups })
|
|
760
|
+
|
|
761
|
+
expect(wrapper.find('.mint-plate-editor__rail').exists()).toBe(false)
|
|
762
|
+
})
|
|
763
|
+
|
|
764
|
+
it('should show the rail in a wide container', async () => {
|
|
765
|
+
const wrapper = await mountWide({ groups })
|
|
766
|
+
|
|
767
|
+
expect(wrapper.find('.mint-plate-editor__rail').exists()).toBe(true)
|
|
768
|
+
})
|
|
769
|
+
|
|
770
|
+
it('should count the wells of each group in the rail', async () => {
|
|
771
|
+
const wrapper = await mountWide({ modelValue: grouped(), groups })
|
|
772
|
+
|
|
773
|
+
expect(wrapper.get('.mint-plate-editor__rail-row .mint-plate-editor__rail-count').text()).toBe('2')
|
|
774
|
+
})
|
|
775
|
+
|
|
776
|
+
it('should emit group-add from the New group row', async () => {
|
|
777
|
+
const wrapper = await mountWide({ groups })
|
|
778
|
+
|
|
779
|
+
await wrapper.get('[data-action="group-add"]').trigger('click')
|
|
780
|
+
|
|
781
|
+
expect(wrapper.emitted('group-add')).toHaveLength(1)
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
it('should hide New group when readonly', async () => {
|
|
785
|
+
const wrapper = await mountWide({ groups, readonly: true })
|
|
786
|
+
|
|
787
|
+
expect(wrapper.find('[data-action="group-add"]').exists()).toBe(false)
|
|
788
|
+
})
|
|
789
|
+
|
|
790
|
+
it('should list the sample types, IQC included, when there are no groups', async () => {
|
|
791
|
+
const wrapper = await mountWide()
|
|
792
|
+
|
|
793
|
+
expect(wrapper.get('.mint-plate-editor__rail').text()).toContain('IQC')
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
it('should move the legend from under the plate into the rail', async () => {
|
|
797
|
+
const wrapper = await mountWide({ groups })
|
|
798
|
+
|
|
799
|
+
expect(plate(wrapper).props('showLegend')).toBe(false)
|
|
800
|
+
})
|
|
801
|
+
|
|
802
|
+
it('should keep the legend under the plate in a narrow container', () => {
|
|
803
|
+
const wrapper = mountEditor({ groups })
|
|
804
|
+
|
|
805
|
+
expect(plate(wrapper).props('showLegend')).toBe(true)
|
|
806
|
+
})
|
|
807
|
+
|
|
808
|
+
it('should not open the popover inspector beside a rail', async () => {
|
|
809
|
+
const wrapper = await mountWide()
|
|
810
|
+
|
|
811
|
+
expect(plate(wrapper).props('editable')).toBe(false)
|
|
812
|
+
})
|
|
813
|
+
|
|
814
|
+
it('should select only the clicked well on a plain click without groups', async () => {
|
|
815
|
+
const wrapper = await mountWide()
|
|
816
|
+
await cell(wrapper, 'A1').trigger('click', { ctrlKey: true })
|
|
817
|
+
|
|
818
|
+
await cell(wrapper, 'A2').trigger('click')
|
|
819
|
+
|
|
820
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([['A2']])
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
it('should add a well to the selection on Ctrl-click beside the rail without groups', async () => {
|
|
824
|
+
const wrapper = await mountWide()
|
|
825
|
+
await cell(wrapper, 'A1').trigger('click')
|
|
826
|
+
|
|
827
|
+
await cell(wrapper, 'A2').trigger('click', { ctrlKey: true })
|
|
828
|
+
|
|
829
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([['A1', 'A2']])
|
|
830
|
+
})
|
|
831
|
+
|
|
832
|
+
it('should add a well to the selection on Cmd-click beside the rail with groups', async () => {
|
|
833
|
+
const wrapper = await mountWide({ groups })
|
|
834
|
+
await cell(wrapper, 'A1').trigger('click')
|
|
835
|
+
|
|
836
|
+
await cell(wrapper, 'A2').trigger('click', { metaKey: true })
|
|
837
|
+
|
|
838
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([['A1', 'A2']])
|
|
839
|
+
})
|
|
840
|
+
|
|
841
|
+
it('should select wells by rectangle with groups', async () => {
|
|
842
|
+
const wrapper = await mountWide({ groups })
|
|
843
|
+
|
|
844
|
+
expect(plate(wrapper).props('selectionMode')).toBe('rectangle')
|
|
845
|
+
})
|
|
846
|
+
|
|
847
|
+
it('should show the inline inspector for one selected well', async () => {
|
|
848
|
+
const wrapper = await mountWide()
|
|
849
|
+
|
|
850
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
851
|
+
await nextTick()
|
|
852
|
+
|
|
853
|
+
expect(wrapper.getComponent(WellEditPopupInternal).props('inline')).toBe(true)
|
|
854
|
+
})
|
|
855
|
+
|
|
856
|
+
it('should emit well-edit from the inline inspector', async () => {
|
|
857
|
+
const wrapper = await mountWide()
|
|
858
|
+
plate(wrapper).vm.$emit('selection-change', ['A2'])
|
|
859
|
+
await nextTick()
|
|
860
|
+
|
|
861
|
+
wrapper.getComponent(WellEditPopupInternal).vm.$emit('save', editData)
|
|
862
|
+
|
|
863
|
+
expect(wrapper.emitted('well-edit')?.[0]).toEqual(['rack-1', 'A2', editData])
|
|
864
|
+
})
|
|
865
|
+
|
|
866
|
+
it('should deselect when the inline inspector closes', async () => {
|
|
867
|
+
const wrapper = await mountWide()
|
|
868
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
869
|
+
await nextTick()
|
|
870
|
+
|
|
871
|
+
wrapper.getComponent(WellEditPopupInternal).vm.$emit('close')
|
|
872
|
+
|
|
873
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([[]])
|
|
874
|
+
})
|
|
875
|
+
|
|
876
|
+
it('should deselect when Enter is pressed in the rail inspector name field', async () => {
|
|
877
|
+
const wrapper = await mountWide()
|
|
878
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
879
|
+
await nextTick()
|
|
880
|
+
|
|
881
|
+
await wrapper.get('.mint-plate-editor__rail [data-field="label"] input').trigger('keydown', { key: 'Enter' })
|
|
882
|
+
|
|
883
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([[]])
|
|
884
|
+
})
|
|
885
|
+
|
|
886
|
+
it('should save the edit to the inspected well when Enter closes the rail inspector', async () => {
|
|
887
|
+
const wrapper = await mountWide()
|
|
888
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
889
|
+
await nextTick()
|
|
890
|
+
const input = wrapper.get('.mint-plate-editor__rail [data-field="label"] input')
|
|
891
|
+
|
|
892
|
+
await input.setValue('S-0099')
|
|
893
|
+
await input.trigger('keydown', { key: 'Enter' })
|
|
894
|
+
|
|
895
|
+
expect(wrapper.emitted('well-edit')?.at(-1)).toEqual(['rack-1', 'A1', expect.objectContaining({ label: 'S-0099' })])
|
|
896
|
+
})
|
|
897
|
+
|
|
898
|
+
it('should deselect when Clear well is pressed in the rail inspector', async () => {
|
|
899
|
+
const wrapper = await mountWide()
|
|
900
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
901
|
+
await nextTick()
|
|
902
|
+
|
|
903
|
+
await wrapper.get('.mint-plate-editor__rail [data-action="clear"]').trigger('click')
|
|
904
|
+
|
|
905
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([[]])
|
|
906
|
+
})
|
|
907
|
+
|
|
908
|
+
it('should drop the selection when the rail collapses without groups', async () => {
|
|
909
|
+
let onResize: ((entries: Array<{ contentRect: { width: number } }>) => void) | undefined
|
|
910
|
+
vi.stubGlobal('ResizeObserver', class {
|
|
911
|
+
constructor(callback: typeof onResize) { onResize = callback }
|
|
912
|
+
observe() {}
|
|
913
|
+
disconnect() {}
|
|
914
|
+
})
|
|
915
|
+
try {
|
|
916
|
+
const wrapper = await mountWide()
|
|
917
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
918
|
+
await nextTick()
|
|
919
|
+
|
|
920
|
+
onResize?.([{ contentRect: { width: 640 } }])
|
|
921
|
+
await nextTick()
|
|
922
|
+
|
|
923
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([[]])
|
|
924
|
+
} finally {
|
|
925
|
+
vi.unstubAllGlobals()
|
|
926
|
+
}
|
|
927
|
+
})
|
|
928
|
+
|
|
929
|
+
it('should not select a well on a plain click beside the rail when wellFields is false and there are no groups', async () => {
|
|
930
|
+
const wrapper = await mountWide({ wellFields: false })
|
|
931
|
+
|
|
932
|
+
await cell(wrapper, 'A1').trigger('click')
|
|
933
|
+
|
|
934
|
+
expect(wrapper.emitted('selection-change')).toBeUndefined()
|
|
935
|
+
})
|
|
936
|
+
|
|
937
|
+
it('should keep the selection bar for several wells', async () => {
|
|
938
|
+
const wrapper = await mountWide({ groups })
|
|
939
|
+
|
|
940
|
+
plate(wrapper).vm.$emit('selection-change', ['A1', 'A2'])
|
|
941
|
+
await nextTick()
|
|
942
|
+
|
|
943
|
+
expect(wrapper.findComponent(WellEditPopupInternal).exists()).toBe(false)
|
|
944
|
+
})
|
|
945
|
+
|
|
946
|
+
it('should offer the group bar for one selected well beside the rail', async () => {
|
|
947
|
+
const wrapper = await mountWide({ groups })
|
|
948
|
+
plate(wrapper).vm.$emit('selection-change', ['D5'])
|
|
949
|
+
await nextTick()
|
|
950
|
+
|
|
951
|
+
await wrapper.get('.mint-plate-editor__group').trigger('click')
|
|
952
|
+
|
|
953
|
+
expect(wrapper.emitted('assign')?.[0]).toEqual(['rack-1', ['D5'], 'ctrl'])
|
|
954
|
+
})
|
|
955
|
+
|
|
956
|
+
it('should drop the selection when the editor turns readonly', async () => {
|
|
957
|
+
const wrapper = await mountWide()
|
|
958
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
959
|
+
await nextTick()
|
|
960
|
+
|
|
961
|
+
await wrapper.setProps({ readonly: true })
|
|
962
|
+
|
|
963
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([[]])
|
|
964
|
+
})
|
|
965
|
+
|
|
966
|
+
it('should keep the rail off while a 384-well plate would not fit beside it', async () => {
|
|
967
|
+
const wrapper = await mountWide({ modelValue: [{ id: 'rack-1', name: 'P1', format: 384, slot: 'R', injectionVolume: 5, wells: {} }] })
|
|
968
|
+
|
|
969
|
+
expect(wrapper.find('.mint-plate-editor__rail').exists()).toBe(false)
|
|
970
|
+
})
|
|
971
|
+
|
|
972
|
+
it('should show the rail once a 384-well plate fits beside it', async () => {
|
|
973
|
+
const spy = vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockReturnValue({ width: 1200 } as DOMRect)
|
|
974
|
+
try {
|
|
975
|
+
const wrapper = mountEditor({ modelValue: [{ id: 'rack-1', name: 'P1', format: 384, slot: 'R', injectionVolume: 5, wells: {} }] })
|
|
976
|
+
await nextTick()
|
|
977
|
+
|
|
978
|
+
expect(wrapper.find('.mint-plate-editor__rail').exists()).toBe(true)
|
|
979
|
+
} finally {
|
|
980
|
+
spy.mockRestore()
|
|
981
|
+
}
|
|
982
|
+
})
|
|
983
|
+
|
|
984
|
+
it('should show the rail beside a 96-well plate at size fill', async () => {
|
|
985
|
+
const wrapper = await mountWide({ modelValue: grouped(), size: 'fill' })
|
|
986
|
+
|
|
987
|
+
expect(wrapper.find('.mint-plate-editor__rail').exists()).toBe(true)
|
|
988
|
+
})
|
|
989
|
+
|
|
990
|
+
it('should render #well-editor in the rail with inline set', async () => {
|
|
991
|
+
const wrapper = await mountWide({}, {
|
|
992
|
+
slots: { 'well-editor': (props: { wellId: string, inline: boolean }) => h('i', { class: 'slot' }, `${props.wellId}/${props.inline}`) },
|
|
993
|
+
})
|
|
994
|
+
|
|
995
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
996
|
+
await nextTick()
|
|
997
|
+
|
|
998
|
+
expect(wrapper.get('.mint-plate-editor__rail .slot').text()).toBe('A1/true')
|
|
999
|
+
})
|
|
1000
|
+
})
|
|
1001
|
+
|
|
1002
|
+
describe('PlateEditor — inspector marker (7a)', () => {
|
|
1003
|
+
it('should tint the rail inspector marker with the group color', async () => {
|
|
1004
|
+
const racks: Rack[] = [{ id: 'rack-1', name: 'P1', format: 96, slot: 'R', injectionVolume: 5, wells: { A1: { sampleType: 'sample', group: 'trt', state: 'filled' } } }]
|
|
1005
|
+
const wrapper = await mountWide({ modelValue: racks, groups: [{ id: 'ctrl', name: 'Control' }, { id: 'trt', name: 'Treated' }] })
|
|
1006
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
1007
|
+
await nextTick()
|
|
1008
|
+
|
|
1009
|
+
expect(wrapper.getComponent(WellEditPopupInternal).props('markerColors')).toMatchObject({ trt: 'var(--mint-sample-2)' })
|
|
1010
|
+
})
|
|
1011
|
+
|
|
1012
|
+
it('should name the group of the well in the rail inspector', async () => {
|
|
1013
|
+
const racks: Rack[] = [{ id: 'rack-1', name: 'P1', format: 96, slot: 'R', injectionVolume: 5, wells: { A1: { sampleType: 'sample', group: 'trt', state: 'filled' } } }]
|
|
1014
|
+
const wrapper = await mountWide({ modelValue: racks, groups: [{ id: 'ctrl', name: 'Control' }, { id: 'trt', name: 'Treated' }] })
|
|
1015
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
1016
|
+
await nextTick()
|
|
1017
|
+
|
|
1018
|
+
expect(wrapper.getComponent(WellEditPopupInternal).props('groupLabel')).toBe('Treated')
|
|
1019
|
+
})
|
|
1020
|
+
|
|
1021
|
+
it('should give the rail inspector the Type field when there are groups', async () => {
|
|
1022
|
+
const wrapper = await mountWide({ groups: [{ id: 'ctrl', name: 'Control' }] })
|
|
1023
|
+
plate(wrapper).vm.$emit('selection-change', ['A1'])
|
|
1024
|
+
await nextTick()
|
|
1025
|
+
|
|
1026
|
+
expect(wrapper.getComponent(WellEditPopupInternal).props('editFields')).toContain('sampleType')
|
|
1027
|
+
})
|
|
1028
|
+
})
|
|
1029
|
+
|
|
1030
|
+
describe('PlateEditor — modifier clicks in the popover mode', () => {
|
|
1031
|
+
it('should add a well to the selection on Ctrl-click without groups', async () => {
|
|
1032
|
+
const wrapper = mountEditor()
|
|
1033
|
+
await cell(wrapper, 'A1').trigger('click', { ctrlKey: true })
|
|
1034
|
+
|
|
1035
|
+
await cell(wrapper, 'A2').trigger('click', { ctrlKey: true })
|
|
1036
|
+
|
|
1037
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([['A1', 'A2']])
|
|
1038
|
+
})
|
|
1039
|
+
|
|
1040
|
+
it('should toggle a well out of the selection on Cmd-click without groups', async () => {
|
|
1041
|
+
const wrapper = mountEditor()
|
|
1042
|
+
await cell(wrapper, 'A1').trigger('click', { metaKey: true })
|
|
1043
|
+
|
|
1044
|
+
await cell(wrapper, 'A1').trigger('click', { metaKey: true })
|
|
1045
|
+
|
|
1046
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([[]])
|
|
1047
|
+
})
|
|
1048
|
+
|
|
1049
|
+
it('should keep opening the popover on a plain click without groups', async () => {
|
|
1050
|
+
const wrapper = mountEditor()
|
|
1051
|
+
|
|
1052
|
+
await cell(wrapper, 'A1').trigger('click')
|
|
1053
|
+
|
|
1054
|
+
expect(wrapper.findComponent(WellEditPopupInternal).exists()).toBe(true)
|
|
1055
|
+
})
|
|
1056
|
+
|
|
1057
|
+
it('should not select a well on a plain click when wellFields is false and there are no groups', async () => {
|
|
1058
|
+
const wrapper = mountEditor({ wellFields: false })
|
|
1059
|
+
|
|
1060
|
+
await cell(wrapper, 'A1').trigger('click')
|
|
1061
|
+
|
|
1062
|
+
expect(wrapper.emitted('selection-change')).toBeUndefined()
|
|
1063
|
+
})
|
|
1064
|
+
|
|
1065
|
+
it('should add a well to the selection on Ctrl-click with groups', async () => {
|
|
1066
|
+
const wrapper = mountEditor({ groups: [{ id: 'ctrl', name: 'Control' }] })
|
|
1067
|
+
await cell(wrapper, 'A1').trigger('click', { ctrlKey: true })
|
|
1068
|
+
|
|
1069
|
+
await cell(wrapper, 'A2').trigger('click', { ctrlKey: true })
|
|
1070
|
+
|
|
1071
|
+
expect(wrapper.emitted('selection-change')?.at(-1)).toEqual([['A1', 'A2']])
|
|
1072
|
+
})
|
|
1073
|
+
})
|
|
1074
|
+
|
|
1075
|
+
describe('PlateEditor — designer options', () => {
|
|
1076
|
+
const fullRack = (): Rack[] => [{
|
|
1077
|
+
id: 'rack-1',
|
|
1078
|
+
name: 'Rack 1',
|
|
1079
|
+
format: 6,
|
|
1080
|
+
slot: 'R',
|
|
1081
|
+
injectionVolume: 5,
|
|
1082
|
+
wells: Object.fromEntries(['A1', 'A2', 'A3', 'B1', 'B2', 'B3'].map(id => [id, { sampleType: 'sample' }])),
|
|
1083
|
+
}]
|
|
1084
|
+
|
|
1085
|
+
it('should use the clearConfirm title in the confirmation', () => {
|
|
1086
|
+
const wrapper = mountEditor({ clearConfirm: { title: 'Unplace samples' } })
|
|
1087
|
+
|
|
1088
|
+
expect(wrapper.getComponent(ConfirmDialog).props('title')).toBe('Unplace samples')
|
|
1089
|
+
})
|
|
1090
|
+
|
|
1091
|
+
it('should use the clearConfirm message in the confirmation', () => {
|
|
1092
|
+
const wrapper = mountEditor({ clearConfirm: { message: 'All 1 placed samples go back to the Sample List unplaced.' } })
|
|
1093
|
+
|
|
1094
|
+
expect(wrapper.getComponent(ConfirmDialog).props('message')).toBe('All 1 placed samples go back to the Sample List unplaced.')
|
|
1095
|
+
})
|
|
1096
|
+
|
|
1097
|
+
it('should use the clearConfirm confirm label in the confirmation', () => {
|
|
1098
|
+
const wrapper = mountEditor({ clearConfirm: { confirmLabel: 'Unplace all' } })
|
|
1099
|
+
|
|
1100
|
+
expect(wrapper.getComponent(ConfirmDialog).props('confirmLabel')).toBe('Unplace all')
|
|
1101
|
+
})
|
|
1102
|
+
|
|
1103
|
+
it('should pass the clearConfirm variant to the confirmation', () => {
|
|
1104
|
+
const wrapper = mountEditor({ clearConfirm: { variant: 'warning' } })
|
|
1105
|
+
|
|
1106
|
+
expect(wrapper.getComponent(ConfirmDialog).props('variant')).toBe('warning')
|
|
1107
|
+
})
|
|
1108
|
+
|
|
1109
|
+
it('should keep the default clear message without clearConfirm', () => {
|
|
1110
|
+
const wrapper = mountEditor()
|
|
1111
|
+
|
|
1112
|
+
expect(wrapper.getComponent(ConfirmDialog).props('message')).toBe('Clear all 1 wells on Rack 1?')
|
|
1113
|
+
})
|
|
1114
|
+
|
|
1115
|
+
it('should keep fill-series enabled on a full plate when fillSeriesWhenFull is emit', () => {
|
|
1116
|
+
const wrapper = mountEditor({ modelValue: fullRack(), fillSeries: true, fillSeriesWhenFull: 'emit' })
|
|
1117
|
+
|
|
1118
|
+
expect(wrapper.get('[data-action="fill-series"]').attributes('disabled')).toBeUndefined()
|
|
1119
|
+
})
|
|
1120
|
+
|
|
1121
|
+
it('should emit fill-series on a full plate when fillSeriesWhenFull is emit', async () => {
|
|
1122
|
+
const wrapper = mountEditor({ modelValue: fullRack(), fillSeries: true, fillSeriesWhenFull: 'emit' })
|
|
1123
|
+
|
|
1124
|
+
await wrapper.get('[data-action="fill-series"]').trigger('click')
|
|
1125
|
+
|
|
1126
|
+
expect(wrapper.emitted('fill-series')?.[0]).toEqual(['rack-1'])
|
|
1127
|
+
})
|
|
1128
|
+
|
|
1129
|
+
it('should set the file input accept from importAccept', () => {
|
|
1130
|
+
const wrapper = mountEditor({ import: true, importAccept: '.csv,text/csv' })
|
|
1131
|
+
|
|
1132
|
+
expect(wrapper.get('input[type="file"]').attributes('accept')).toBe('.csv,text/csv')
|
|
1133
|
+
})
|
|
1134
|
+
})
|