@morscherlab/mint-sdk 1.2.0-beta.1 → 1.2.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 +35 -5
- package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-oL0fg_A0.js} +6 -4
- package/dist/ExperimentPopover-oL0fg_A0.js.map +1 -0
- package/dist/{ExperimentSelectorModal-DyPAUCLw.js → ExperimentSelectorModal-BeEOomw_.js} +5 -5
- package/dist/ExperimentSelectorModal-BeEOomw_.js.map +1 -0
- package/dist/{SettingsModal-BgKY93zh.js → SettingsModal-Dphh3RrI.js} +5 -8
- package/dist/SettingsModal-Dphh3RrI.js.map +1 -0
- package/dist/{EmptyState-p90quq8Z.js → Skeleton-DxtwJ5gt.js} +111 -93
- package/dist/Skeleton-DxtwJ5gt.js.map +1 -0
- package/dist/__tests__/components/ExperimentTimeline.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.internal.test.d.ts +1 -0
- package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
- package/dist/__tests__/components/WellEditPopupInternal.test.d.ts +1 -0
- package/dist/__tests__/composables/useListReorder.test.d.ts +1 -0
- package/dist/__tests__/composables/usePluginConfig.test.d.ts +15 -0
- package/dist/__tests__/styles/cssTokens.test.d.ts +1 -0
- package/dist/components/ActionMenu.vue.d.ts +1 -1
- package/dist/components/AppAvatarMenu.vue.d.ts +4 -0
- package/dist/components/AppPluginSwitcher.vue.d.ts +2 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/AuditTrail.vue.d.ts +13 -0
- package/dist/components/AutoGroupModal.vue.d.ts +1 -1
- package/dist/components/BatchProgressList.vue.d.ts +17 -0
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/DataFrame.vue.d.ts +14 -2
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +2 -0
- package/dist/components/DropdownButton.vue.d.ts +2 -0
- package/dist/components/ExperimentDataViewer.vue.d.ts +26 -1
- package/dist/components/ExperimentPopover.vue.d.ts +1 -0
- package/dist/components/ExperimentTimeline.vue.d.ts +29 -1
- package/dist/components/FitPanel.vue.d.ts +1 -1
- package/dist/components/JobsStatusTray.vue.d.ts +9 -2
- package/dist/components/LcmsSequenceTable.vue.d.ts +29 -1
- package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
- package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStepEditor.vue.d.ts +1 -0
- package/dist/components/ReagentList.vue.d.ts +29 -1
- package/dist/components/SampleHierarchyTree.vue.d.ts +29 -1
- package/dist/components/SampleLegend.vue.d.ts +1 -1
- package/dist/components/SampleSelector.drag.d.ts +13 -0
- package/dist/components/SampleSelector.tree.d.ts +2 -0
- package/dist/components/SampleSelector.vue.d.ts +35 -2
- package/dist/components/ScheduleCalendar.vue.d.ts +20 -0
- package/dist/components/SecretInput.vue.d.ts +28 -0
- package/dist/components/SettingsModal.vue.d.ts +1 -1
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -1
- package/dist/components/TimePicker.vue.d.ts +1 -0
- package/dist/components/WellPlate.geometry.d.ts +15 -0
- package/dist/components/WellPlate.interaction.d.ts +13 -0
- package/dist/components/WellPlate.vue.d.ts +29 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.js +8 -8
- package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -0
- package/dist/components/internal/DataStateInternal.vue.d.ts +43 -0
- package/dist/components/internal/WellEditPopupInternal.vue.d.ts +3 -1
- package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
- package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
- package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
- package/dist/{components-DkHddRMl.js → components-CgAUzF8F.js} +2497 -2235
- package/dist/components-CgAUzF8F.js.map +1 -0
- package/dist/composables/controlSchemaTypes.d.ts +34 -0
- package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
- package/dist/composables/index.d.ts +9 -2
- package/dist/composables/index.js +10 -10
- package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
- package/dist/composables/useAsync.d.ts +5 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
- package/dist/composables/useCommandHistory.d.ts +5 -1
- package/dist/composables/useDropdownState.d.ts +18 -2
- package/dist/composables/useEventStream.d.ts +9 -0
- package/dist/composables/useFocusTrap.d.ts +21 -0
- package/dist/composables/useGeneratedAnalysis.d.ts +3 -1
- package/dist/composables/useJobsStatusTray.d.ts +25 -1
- package/dist/composables/useListReorder.d.ts +90 -0
- package/dist/composables/useMenuKeyboard.d.ts +29 -0
- package/dist/composables/useOptimisticMutation.d.ts +5 -1
- package/dist/composables/usePluginClient.d.ts +2 -0
- package/dist/composables/usePluginConfig.d.ts +27 -0
- package/dist/composables/usePluginWorkspace.d.ts +51 -0
- package/dist/composables/useRequestSyncState.d.ts +42 -2
- package/dist/composables/useResourceCrud.d.ts +5 -1
- package/dist/composables/useRovingFocus.d.ts +24 -0
- package/dist/composables/useScheduleDrag.d.ts +1 -0
- package/dist/composables/useWellPainting.d.ts +5 -1
- package/dist/composables/useWellPlateAdapter.d.ts +5 -1
- package/dist/composables/useWellPlateValidation.d.ts +5 -1
- package/dist/composables/workspaceRecipes.d.ts +188 -0
- package/dist/{composables-C_i90sGS.js → composables-B_pia3hO.js} +209 -82
- package/dist/composables-B_pia3hO.js.map +1 -0
- package/dist/{useApi-J6zFkHmn.js → errors-_W0-LbA6.js} +103 -3
- package/dist/errors-_W0-LbA6.js.map +1 -0
- package/dist/{experiment-Cq145oEW.js → experiment-Cwl9AYxw.js} +3 -8
- package/dist/experiment-Cwl9AYxw.js.map +1 -0
- package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-DV3DrvE_.js} +70 -51
- package/dist/experiment-utils-DV3DrvE_.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -15
- package/dist/install.js +2 -7
- package/dist/install.js.map +1 -1
- package/dist/permissions.d.ts +42 -1
- package/dist/permissions.js +20 -10
- package/dist/permissions.js.map +1 -1
- package/dist/stores/auth.d.ts +6 -0
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js +3 -3
- package/dist/stores/settings.d.ts +31 -1
- package/dist/styles.css +11247 -29501
- package/dist/templates/builderReadUtils.d.ts +1 -1
- package/dist/templates/index.js +2 -4
- package/dist/{templates-DsP51i-S.js → templates-Qq113pyZ.js} +303 -106
- package/dist/templates-Qq113pyZ.js.map +1 -0
- package/dist/types/auth.d.ts +1 -0
- package/dist/types/form-builder.d.ts +1 -1
- package/dist/{useControlSchema-BDx2Olha.js → useControlSchema-aMZlb4Sc.js} +462 -1130
- package/dist/useControlSchema-aMZlb4Sc.js.map +1 -0
- package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-DitCtrTT.js} +22 -6
- package/dist/useDropdownState-DitCtrTT.js.map +1 -0
- package/dist/{useExperimentSelector-CV4iNfMS.js → useExperimentSelector-I8HC8hUu.js} +4 -4
- package/dist/{useExperimentSelector-CV4iNfMS.js.map → useExperimentSelector-I8HC8hUu.js.map} +1 -1
- package/dist/{useFormBuilder-BLxG3BOX.js → useFormBuilder-TCxNnqeb.js} +2 -2
- package/dist/{useFormBuilder-BLxG3BOX.js.map → useFormBuilder-TCxNnqeb.js.map} +1 -1
- package/dist/{useProtocolTemplates-CS_SQl6U.js → useProtocolTemplates-BmX_iVBR.js} +766 -212
- package/dist/useProtocolTemplates-BmX_iVBR.js.map +1 -0
- package/dist/utils/color.d.ts +13 -0
- package/dist/utils/errors.d.ts +9 -0
- package/dist/utils/pluginSecrets.d.ts +53 -0
- package/dist/utils/records.d.ts +4 -0
- package/package.json +1 -1
- package/src/__tests__/components/ActionMenu.test.ts +94 -0
- package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
- package/src/__tests__/components/BaseTabs.test.ts +67 -0
- package/src/__tests__/components/DataFrame.test.ts +183 -11
- package/src/__tests__/components/DataStates.test.ts +176 -0
- package/src/__tests__/components/DatePicker.test.ts +76 -0
- package/src/__tests__/components/DateTimePicker.test.ts +36 -0
- package/src/__tests__/components/DropdownButton.test.ts +134 -0
- package/src/__tests__/components/ExperimentTimeline.test.ts +63 -0
- package/src/__tests__/components/GroupAssigner.test.ts +53 -0
- package/src/__tests__/components/LcmsSequenceTable.test.ts +22 -0
- package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -0
- package/src/__tests__/components/RackEditor.test.ts +35 -0
- package/src/__tests__/components/ReagentList.test.ts +64 -2
- package/src/__tests__/components/RetiredStyles.test.ts +31 -0
- package/src/__tests__/components/SampleSelector.drag.test.ts +51 -1
- package/src/__tests__/components/ScheduleCalendar.test.ts +63 -0
- package/src/__tests__/components/SecretInput.test.ts +87 -0
- package/src/__tests__/components/SegmentedControl.test.ts +88 -0
- package/src/__tests__/components/TimePicker.test.ts +61 -0
- package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +59 -0
- package/src/__tests__/composables/useAuth.test.ts +2 -0
- package/src/__tests__/composables/useDropdownState.test.ts +58 -1
- package/src/__tests__/composables/useListReorder.test.ts +145 -0
- package/src/__tests__/composables/usePluginClient.test.ts +140 -0
- package/src/__tests__/composables/usePluginConfig.test.ts +497 -0
- package/src/__tests__/composables/useRequestSyncState.test.ts +140 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +39 -7
- package/src/__tests__/stores/settings.test.ts +71 -1
- package/src/__tests__/styles/cssTokens.test.ts +131 -0
- package/src/__tests__/utils/permissions.test.ts +68 -0
- package/src/components/ActionMenu.vue +45 -88
- package/src/components/AlertBox.vue +0 -4
- package/src/components/AppAvatarMenu.vue +26 -7
- package/src/components/AppLayout.vue +0 -4
- package/src/components/AppPluginSwitcher.story.vue +87 -0
- package/src/components/AppPluginSwitcher.vue +2 -5
- package/src/components/AppSidebar.vue +0 -4
- package/src/components/AppToastContainer.vue +6 -8
- package/src/components/AppTopBar.vue +0 -4
- package/src/components/ArtifactSaveDialog.vue +0 -4
- package/src/components/ArtifactSelectorModal.vue +0 -4
- package/src/components/AuditTrail.story.vue +18 -0
- package/src/components/AuditTrail.vue +27 -7
- package/src/components/AutoGroupModal.vue +0 -4
- package/src/components/Avatar.vue +0 -4
- package/src/components/BaseButton.vue +0 -4
- package/src/components/BaseCheckbox.vue +0 -4
- package/src/components/BaseInput.vue +0 -4
- package/src/components/BaseModal.vue +8 -60
- package/src/components/BasePill.vue +0 -4
- package/src/components/BaseRadioGroup.vue +0 -4
- package/src/components/BaseSelect.vue +0 -4
- package/src/components/BaseSlider.vue +0 -4
- package/src/components/BaseTabs.vue +26 -5
- package/src/components/BaseTextarea.vue +0 -4
- package/src/components/BaseToggle.vue +0 -4
- package/src/components/BatchProgressList.story.vue +18 -0
- package/src/components/BatchProgressList.vue +32 -4
- package/src/components/BioTemplateExperimentWorkspaceView.vue +10 -21
- package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
- package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
- package/src/components/Breadcrumb.vue +0 -4
- package/src/components/Calendar.vue +0 -4
- package/src/components/ChartContainer.story.vue +1 -1
- package/src/components/ChartContainer.vue +0 -4
- package/src/components/ChemicalFormula.vue +0 -4
- package/src/components/CollapsibleCard.vue +0 -4
- package/src/components/ColorSlider.vue +0 -4
- package/src/components/ComponentBindingRenderer.vue +1 -3
- package/src/components/ConcentrationInput.vue +0 -4
- package/src/components/ConfirmDialog.vue +0 -4
- package/src/components/ControlWorkspaceView.vue +11 -22
- package/src/components/DataFrame.story.vue +14 -0
- package/src/components/DataFrame.vue +97 -23
- package/src/components/DatePicker.vue +73 -27
- package/src/components/DateTimePicker.vue +45 -17
- package/src/components/Divider.vue +0 -4
- package/src/components/DoseCalculator.vue +0 -4
- package/src/components/DoseDesignWorkspaceView.vue +6 -25
- package/src/components/DropdownButton.vue +24 -6
- package/src/components/EmptyState.vue +0 -4
- package/src/components/ExperimentCodeBadge.vue +0 -4
- package/src/components/ExperimentDataViewer.story.vue +3 -0
- package/src/components/ExperimentDataViewer.vue +33 -13
- package/src/components/ExperimentPopover.vue +2 -5
- package/src/components/ExperimentSelectorModal.vue +0 -4
- package/src/components/ExperimentTimeline.story.vue +18 -0
- package/src/components/ExperimentTimeline.vue +81 -75
- package/src/components/FileUploader.vue +0 -4
- package/src/components/FitPanel.vue +0 -4
- package/src/components/FormActions.story.vue +96 -0
- package/src/components/FormActions.vue +0 -4
- package/src/components/FormBuilder.vue +0 -4
- package/src/components/FormField.vue +0 -4
- package/src/components/FormulaInput.vue +0 -4
- package/src/components/GeneratedPathInput.story.vue +68 -0
- package/src/components/GeneratedPlotlyResult.story.vue +96 -0
- package/src/components/GeneratedPlotlyResult.vue +3 -3
- package/src/components/GeneratedPluginView.story.vue +145 -0
- package/src/components/GeneratedPluginView.vue +10 -10
- package/src/components/GeneratedResultRenderer.story.vue +117 -0
- package/src/components/GeneratedResultRenderer.vue +4 -4
- package/src/components/GroupAssigner.vue +67 -5
- package/src/components/IconButton.vue +0 -4
- package/src/components/InstrumentAlertLog.vue +0 -4
- package/src/components/InstrumentStateBadge.vue +0 -4
- package/src/components/InstrumentStatusCard.vue +0 -4
- package/src/components/JobsStatusTray.vue +16 -5
- package/src/components/LcmsSequenceTable.vue +81 -50
- package/src/components/LoadingSpinner.vue +0 -4
- package/src/components/MobileSupportGate.vue +0 -4
- package/src/components/MoleculeInput.vue +0 -4
- package/src/components/MultiSelect.vue +0 -4
- package/src/components/NumberInput.vue +0 -4
- package/src/components/PlateMapEditor.vue +3 -14
- package/src/components/PluginIcon.vue +0 -4
- package/src/components/PluginWorkspaceView.props.ts +38 -0
- package/src/components/PluginWorkspaceView.vue +53 -224
- package/src/components/ProgressBar.vue +0 -4
- package/src/components/ProtocolStepEditor.vue +2 -4
- package/src/components/RackEditor.vue +51 -70
- package/src/components/ReagentEditor.vue +0 -4
- package/src/components/ReagentList.story.vue +18 -0
- package/src/components/ReagentList.vue +127 -82
- package/src/components/ResourceCard.vue +0 -4
- package/src/components/RuntimeUpdateNotice.story.vue +49 -0
- package/src/components/RuntimeUpdateNotice.vue +4 -4
- package/src/components/SampleHierarchyTree.story.vue +18 -0
- package/src/components/SampleHierarchyTree.vue +32 -9
- package/src/components/SampleLegend.vue +2 -8
- package/src/components/SampleSelector.drag.ts +51 -0
- package/src/components/SampleSelector.story.vue +6 -0
- package/src/components/SampleSelector.tree.ts +2 -0
- package/src/components/SampleSelector.vue +49 -10
- package/src/components/SampleSelectorGroupNode.vue +19 -0
- package/src/components/ScheduleCalendar.story.vue +6 -0
- package/src/components/ScheduleCalendar.vue +82 -7
- package/src/components/ScientificNumber.vue +0 -4
- package/src/components/SecretInput.story.vue +79 -0
- package/src/components/SecretInput.vue +150 -0
- package/src/components/SectionCard.vue +0 -4
- package/src/components/SegmentedControl.vue +48 -5
- package/src/components/SequenceInput.vue +0 -4
- package/src/components/SequenceProgressBar.story.vue +110 -0
- package/src/components/SequenceProgressBar.vue +0 -4
- package/src/components/SettingsModal.vue +0 -4
- package/src/components/Skeleton.vue +0 -4
- package/src/components/SmartGroupFieldRecipe.vue +8 -6
- package/src/components/SmartGroupManual.vue +0 -4
- package/src/components/StatusIndicator.vue +0 -4
- package/src/components/StepWizard.vue +0 -4
- package/src/components/TagsInput.vue +4 -6
- package/src/components/ThemeToggle.vue +0 -4
- package/src/components/TimePicker.vue +36 -7
- package/src/components/TimeRangeInput.vue +0 -4
- package/src/components/Tooltip.story.vue +3 -3
- package/src/components/Tooltip.vue +0 -4
- package/src/components/UnitInput.vue +0 -4
- package/src/components/WellPlate.geometry.ts +60 -1
- package/src/components/WellPlate.interaction.ts +44 -0
- package/src/components/WellPlate.story.vue +8 -0
- package/src/components/WellPlate.vue +53 -7
- package/src/components/index.ts +9 -0
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -5
- package/src/components/internal/AppTopBarPillNavInternal.vue +0 -4
- package/src/components/internal/DataStateInternal.vue +74 -0
- package/src/components/internal/FormSectionRenderer.vue +0 -4
- package/src/components/internal/WellEditPopupInternal.vue +20 -5
- package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
- package/src/components/internal/WorkspaceShellInternal.vue +125 -0
- package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
- package/src/composables/controlSchemaTypes.ts +34 -0
- package/src/composables/experimentDesignData.ts +1 -3
- package/src/composables/flattenWorkspaceBindings.ts +104 -0
- package/src/composables/formBuilderRegistry.ts +5 -0
- package/src/composables/index.ts +43 -0
- package/src/composables/pluginWorkspaceModel.ts +182 -0
- package/src/composables/useAsync.ts +5 -1
- package/src/composables/useAuth.ts +3 -0
- package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
- package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
- package/src/composables/useBioTemplateWorkspace.ts +18 -43
- package/src/composables/useCommandHistory.ts +5 -1
- package/src/composables/useDropdownState.ts +35 -5
- package/src/composables/useEventStream.ts +14 -2
- package/src/composables/useFileImport.ts +2 -7
- package/src/composables/useFocusTrap.ts +91 -0
- package/src/composables/useGeneratedAnalysis.ts +22 -10
- package/src/composables/useJobsStatusTray.ts +184 -17
- package/src/composables/useListReorder.story.vue +143 -0
- package/src/composables/useListReorder.ts +242 -0
- package/src/composables/useMenuKeyboard.ts +90 -0
- package/src/composables/useOptimisticMutation.ts +6 -16
- package/src/composables/usePluginClient.ts +47 -4
- package/src/composables/usePluginConfig.ts +252 -0
- package/src/composables/usePluginWorkspace.ts +208 -0
- package/src/composables/useRackEditor.ts +2 -11
- package/src/composables/useRequestSyncState.ts +98 -28
- package/src/composables/useResourceCrud.ts +6 -16
- package/src/composables/useRovingFocus.ts +66 -0
- package/src/composables/useScheduleDrag.ts +37 -0
- package/src/composables/useWellPainting.ts +10 -30
- package/src/composables/useWellPlateAdapter.ts +5 -1
- package/src/composables/useWellPlateEditor.ts +1 -11
- package/src/composables/useWellPlateValidation.ts +5 -1
- package/src/composables/workspaceRecipes.ts +267 -0
- package/src/index.ts +16 -0
- package/src/permissions.ts +48 -9
- package/src/stores/auth.ts +1 -0
- package/src/stores/experiment.ts +2 -7
- package/src/stores/index.ts +9 -1
- package/src/stores/settings.ts +93 -1
- package/src/styles/components/audit-trail.css +5 -5
- package/src/styles/components/batch-progress-list.css +7 -0
- package/src/styles/components/color-slider.css +2 -2
- package/src/styles/components/data-state.css +30 -0
- package/src/styles/components/dataframe.css +29 -35
- package/src/styles/components/experiment-data-viewer.css +4 -4
- package/src/styles/components/experiment-timeline.css +3 -30
- package/src/styles/components/form-builder.css +5 -5
- package/src/styles/components/lcms-sequence-table.css +16 -1
- package/src/styles/components/multi-select.css +1 -1
- package/src/styles/components/reagent-list.css +38 -31
- package/src/styles/components/sample-hierarchy-tree.css +5 -19
- package/src/styles/components/sample-selector.css +9 -2
- package/src/styles/components/schedule-calendar.css +8 -0
- package/src/styles/components/scientific-number.css +1 -1
- package/src/styles/components/secret-input.css +60 -0
- package/src/styles/components/well-plate.css +8 -0
- package/src/styles/index.css +9 -0
- package/src/styles/variables.css +29 -0
- package/src/templates/builderPresetControls.ts +1 -1
- package/src/templates/builderReadUtils.ts +1 -3
- package/src/templates/componentPlateHelpers.ts +3 -1
- package/src/templates/componentTargetResolvers.ts +1 -3
- package/src/templates/controlSchemaTargets.ts +1 -3
- package/src/templates/doseResponseAdapters.ts +2 -13
- package/src/templates/elisaAssayCollectionBuilder.ts +2 -2
- package/src/templates/plateMapBuilder.ts +2 -2
- package/src/templates/qpcrPlateBuilder.ts +1 -1
- package/src/templates/templateValidators.ts +1 -1
- package/src/templates/timeCourseBuilder.ts +2 -2
- package/src/templates/wellPlateScreenCollectionBuilder.ts +2 -2
- package/src/types/auth.ts +1 -0
- package/src/types/form-builder.ts +1 -0
- package/src/utils/color.ts +51 -0
- package/src/utils/errors.ts +29 -0
- package/src/utils/formModelSync.ts +1 -3
- package/src/utils/pluginIcon.ts +1 -3
- package/src/utils/pluginSecrets.ts +102 -0
- package/src/utils/records.ts +9 -0
- package/dist/EmptyState-p90quq8Z.js.map +0 -1
- package/dist/ExperimentPopover-tLwJ7XF4.js.map +0 -1
- package/dist/ExperimentSelectorModal-DyPAUCLw.js.map +0 -1
- package/dist/SettingsModal-BgKY93zh.js.map +0 -1
- package/dist/components-DkHddRMl.js.map +0 -1
- package/dist/composables/autoGroupConstants.d.ts +0 -1
- package/dist/composables/autoGroupGrouping.d.ts +0 -3
- package/dist/composables/useAutoGroupInputSources.d.ts +0 -32
- package/dist/composables-C_i90sGS.js.map +0 -1
- package/dist/experiment-Cq145oEW.js.map +0 -1
- package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
- package/dist/templates/builderDefaults.d.ts +0 -1
- package/dist/templates/plateGeometry.d.ts +0 -4
- package/dist/templates-DsP51i-S.js.map +0 -1
- package/dist/useApi-J6zFkHmn.js.map +0 -1
- package/dist/useControlSchema-BDx2Olha.js.map +0 -1
- package/dist/useDropdownState-DSb3BC3N.js.map +0 -1
- package/dist/useProtocolTemplates-CS_SQl6U.js.map +0 -1
- package/src/__tests__/composables/useAutoGroupInputSources.test.ts +0 -127
- package/src/composables/autoGroupConstants.ts +0 -4
- package/src/composables/autoGroupGrouping.ts +0 -148
- package/src/composables/useAutoGroupInputSources.ts +0 -147
- package/src/templates/builderDefaults.ts +0 -11
- package/src/templates/plateGeometry.ts +0 -62
- /package/dist/__tests__/{composables/useAutoGroupInputSources.test.d.ts → components/DataStates.test.d.ts} +0 -0
package/dist/utils/color.d.ts
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
import { SlotPosition } from '../types/componentLabTypes';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical categorical scale for sample types, plate maps, dose-response
|
|
4
|
+
* series and legends. Mirrors `--mint-sample-1` … `--mint-sample-9`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SAMPLE_COLOR_SCALE: readonly string[];
|
|
7
|
+
/**
|
|
8
|
+
* Slot-position colors shared by `PlateMapEditor` and `RackEditor`.
|
|
9
|
+
* Mirrors `--mint-slot-r`, `--mint-slot-g`, `--mint-slot-b`, `--mint-slot-y`.
|
|
10
|
+
*/
|
|
11
|
+
export declare const SLOT_COLORS: Record<SlotPosition, string>;
|
|
12
|
+
/** Slot positions in their canonical display order. */
|
|
13
|
+
export declare const SLOT_ORDER: readonly SlotPosition[];
|
|
1
14
|
/**
|
|
2
15
|
* Color utilities for deriving same-hue-family shades.
|
|
3
16
|
*
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place a thrown value is turned into a developer-facing message.
|
|
3
|
+
*
|
|
4
|
+
* Five copies of this logic drifted apart across the composables and stores
|
|
5
|
+
* (some checked a bare `{ message }` object, some did not); this export is the
|
|
6
|
+
* union of what they all accepted, so every caller now reads the same message
|
|
7
|
+
* from the same value.
|
|
8
|
+
*/
|
|
9
|
+
export declare function readErrorMessage(value: unknown, fallback?: string): string;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable references for plugin secret settings.
|
|
3
|
+
*
|
|
4
|
+
* A settings response never carries a secret's value. Every secret leaf is a
|
|
5
|
+
* marker instead:
|
|
6
|
+
*
|
|
7
|
+
* - `{ "$secret": "sec_…", "set": true }` — a stored secret, referenced by a
|
|
8
|
+
* stable opaque id. Send the marker back unchanged to keep the stored value.
|
|
9
|
+
* - `null` — nothing stored at that position.
|
|
10
|
+
* - `{ "$secret": null, "locked": true }` — the effective value comes from the
|
|
11
|
+
* environment and is never written back.
|
|
12
|
+
*
|
|
13
|
+
* On save each secret leaf accepts the reference, a plain string (a new value),
|
|
14
|
+
* or `null` (clear). Payloads carry `secret_format: "ref"` so a `null` reads as
|
|
15
|
+
* a deliberate clear rather than the deprecated "unchanged" placeholder.
|
|
16
|
+
*/
|
|
17
|
+
/** Reserved marker key that references a stored secret. */
|
|
18
|
+
export declare const PLUGIN_SECRET_REF_KEY = "$secret";
|
|
19
|
+
/** Reserved payload key declaring which secret wire format a client speaks. */
|
|
20
|
+
export declare const PLUGIN_SECRET_FORMAT_KEY = "secret_format";
|
|
21
|
+
/** Wire format published and understood by this SDK. */
|
|
22
|
+
export declare const PLUGIN_SECRET_FORMAT_REF = "ref";
|
|
23
|
+
/** One `$secret` marker as published by a settings response. */
|
|
24
|
+
export interface PluginSecretRef {
|
|
25
|
+
$secret: string | null;
|
|
26
|
+
set?: boolean;
|
|
27
|
+
locked?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** What a settings form should tell the user about one secret field. */
|
|
30
|
+
export type PluginSecretState = 'set' | 'unset' | 'environment';
|
|
31
|
+
/** Return whether one settings value is a `$secret` reference marker. */
|
|
32
|
+
export declare function isPluginSecretRef(value: unknown): value is PluginSecretRef;
|
|
33
|
+
/** Return the stored secret id behind a marker, or `null` when nothing is stored. */
|
|
34
|
+
export declare function pluginSecretId(value: unknown): string | null;
|
|
35
|
+
/** Classify one secret field for display: stored, unset, or environment-provided. */
|
|
36
|
+
export declare function pluginSecretState(value: unknown): PluginSecretState;
|
|
37
|
+
/** Human label for one secret field's state. */
|
|
38
|
+
export declare function pluginSecretLabel(value: unknown): string;
|
|
39
|
+
/** Whether a secret field is read-only because the environment supplies it. */
|
|
40
|
+
export declare function isPluginSecretLocked(value: unknown): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Value to submit for an untouched secret field: the reference, unchanged.
|
|
43
|
+
*
|
|
44
|
+
* A locked marker round-trips too, which is how the platform keeps a stored
|
|
45
|
+
* value that an environment variable currently hides.
|
|
46
|
+
*/
|
|
47
|
+
export declare function keepPluginSecret(current: unknown): PluginSecretRef | null;
|
|
48
|
+
/** Value to submit when the user typed a new secret. */
|
|
49
|
+
export declare function setPluginSecret(value: string): string;
|
|
50
|
+
/** Value to submit when the user cleared a secret. */
|
|
51
|
+
export declare function clearPluginSecret(): null;
|
|
52
|
+
/** Whether a settings response declares the `$secret` reference wire format. */
|
|
53
|
+
export declare function usesPluginSecretReferences(response: unknown): boolean;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Narrow an unknown value to a plain object (arrays and null excluded). */
|
|
2
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
3
|
+
/** Narrow an unknown value to any non-null object, arrays included. */
|
|
4
|
+
export declare function isObjectLike(value: unknown): value is Record<string, unknown>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morscherlab/mint-sdk",
|
|
3
|
-
"version": "1.2.0-beta.
|
|
3
|
+
"version": "1.2.0-beta.10",
|
|
4
4
|
"description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec INtegrated Toolkit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -102,6 +102,100 @@ describe('ActionMenu', () => {
|
|
|
102
102
|
expect(document.body.querySelector('.mint-action-menu__panel')).toBeNull()
|
|
103
103
|
})
|
|
104
104
|
|
|
105
|
+
it('should open from the trigger keyboard shortcuts and focus the first item', async () => {
|
|
106
|
+
const wrapper = mount(ActionMenu, {
|
|
107
|
+
attachTo: document.body,
|
|
108
|
+
props: { label: 'More user actions', items },
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
await wrapper.get('button').trigger('keydown', { key: 'ArrowDown' })
|
|
112
|
+
await wrapper.vm.$nextTick()
|
|
113
|
+
|
|
114
|
+
const menuItems = document.body.querySelectorAll<HTMLElement>('.mint-action-menu__item')
|
|
115
|
+
expect(menuItems).toHaveLength(2)
|
|
116
|
+
expect(document.activeElement).toBe(menuItems[0])
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should open focused on the last item with ArrowUp', async () => {
|
|
120
|
+
const wrapper = mount(ActionMenu, {
|
|
121
|
+
attachTo: document.body,
|
|
122
|
+
props: { label: 'More user actions', items },
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
await wrapper.get('button').trigger('keydown', { key: 'ArrowUp' })
|
|
126
|
+
await wrapper.vm.$nextTick()
|
|
127
|
+
|
|
128
|
+
const menuItems = document.body.querySelectorAll<HTMLElement>('.mint-action-menu__item')
|
|
129
|
+
expect(document.activeElement).toBe(menuItems[1])
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('should cycle items with the arrow keys and jump with Home/End', async () => {
|
|
133
|
+
const wrapper = mount(ActionMenu, {
|
|
134
|
+
attachTo: document.body,
|
|
135
|
+
props: { label: 'More user actions', items },
|
|
136
|
+
})
|
|
137
|
+
await wrapper.get('button').trigger('click')
|
|
138
|
+
await wrapper.vm.$nextTick()
|
|
139
|
+
|
|
140
|
+
const panel = document.body.querySelector<HTMLElement>('.mint-action-menu__panel')
|
|
141
|
+
const menuItems = document.body.querySelectorAll<HTMLElement>('.mint-action-menu__item')
|
|
142
|
+
|
|
143
|
+
function press(key: string) {
|
|
144
|
+
panel?.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true }))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
press('ArrowDown')
|
|
148
|
+
expect(document.activeElement).toBe(menuItems[1])
|
|
149
|
+
press('ArrowDown')
|
|
150
|
+
expect(document.activeElement).toBe(menuItems[0])
|
|
151
|
+
press('End')
|
|
152
|
+
expect(document.activeElement).toBe(menuItems[1])
|
|
153
|
+
press('Home')
|
|
154
|
+
expect(document.activeElement).toBe(menuItems[0])
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should close on Tab without forcing focus back to the trigger', async () => {
|
|
158
|
+
const wrapper = mount(ActionMenu, {
|
|
159
|
+
attachTo: document.body,
|
|
160
|
+
props: { label: 'More user actions', items },
|
|
161
|
+
})
|
|
162
|
+
await wrapper.get('button').trigger('click')
|
|
163
|
+
|
|
164
|
+
document.body.querySelector<HTMLElement>('.mint-action-menu__panel')?.dispatchEvent(
|
|
165
|
+
new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }),
|
|
166
|
+
)
|
|
167
|
+
await wrapper.vm.$nextTick()
|
|
168
|
+
|
|
169
|
+
expect(document.body.querySelector('.mint-action-menu__panel')).toBeNull()
|
|
170
|
+
expect(document.activeElement).not.toBe(wrapper.get('button').element)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('should not open from the keyboard while disabled', async () => {
|
|
174
|
+
const wrapper = mount(ActionMenu, {
|
|
175
|
+
attachTo: document.body,
|
|
176
|
+
props: { label: 'More user actions', items, disabled: true },
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
await wrapper.get('button').trigger('keydown', { key: 'ArrowDown' })
|
|
180
|
+
await wrapper.vm.$nextTick()
|
|
181
|
+
|
|
182
|
+
expect(document.body.querySelector('.mint-action-menu__panel')).toBeNull()
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('should close when a click lands outside the trigger and panel', async () => {
|
|
186
|
+
const wrapper = mount(ActionMenu, {
|
|
187
|
+
attachTo: document.body,
|
|
188
|
+
props: { label: 'More user actions', items },
|
|
189
|
+
})
|
|
190
|
+
await wrapper.get('button').trigger('click')
|
|
191
|
+
expect(document.body.querySelector('.mint-action-menu__panel')).not.toBeNull()
|
|
192
|
+
|
|
193
|
+
document.body.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
194
|
+
await wrapper.vm.$nextTick()
|
|
195
|
+
|
|
196
|
+
expect(document.body.querySelector('.mint-action-menu__panel')).toBeNull()
|
|
197
|
+
})
|
|
198
|
+
|
|
105
199
|
it('should return focus to the trigger when Escape closes the menu', async () => {
|
|
106
200
|
const wrapper = mount(ActionMenu, {
|
|
107
201
|
attachTo: document.body,
|
|
@@ -35,4 +35,127 @@ describe('AppAvatarMenu', () => {
|
|
|
35
35
|
expect(wrapper.emitted('select')).toEqual([[docs]])
|
|
36
36
|
expect(wrapper.find('.mint-avatar-menu__panel').exists()).toBe(false)
|
|
37
37
|
})
|
|
38
|
+
|
|
39
|
+
describe('keyboard handling (menu button pattern)', () => {
|
|
40
|
+
const items = [{ id: 'profile', label: 'Profile' }, { id: 'settings', label: 'Settings' }]
|
|
41
|
+
|
|
42
|
+
function mountMenu() {
|
|
43
|
+
return mount(AppAvatarMenu, {
|
|
44
|
+
props: { userName: 'Ada Lovelace', items, showSignOut: true },
|
|
45
|
+
attachTo: document.body,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
it('sets aria-expanded on the trigger and toggles it open/closed', async () => {
|
|
50
|
+
const wrapper = mountMenu()
|
|
51
|
+
const trigger = wrapper.get('.mint-avatar-menu__trigger')
|
|
52
|
+
expect(trigger.attributes('aria-expanded')).toBe('false')
|
|
53
|
+
|
|
54
|
+
await trigger.trigger('click')
|
|
55
|
+
expect(trigger.attributes('aria-expanded')).toBe('true')
|
|
56
|
+
|
|
57
|
+
wrapper.unmount()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('opens and focuses the first item on ArrowDown', async () => {
|
|
61
|
+
const wrapper = mountMenu()
|
|
62
|
+
await wrapper.get('.mint-avatar-menu__trigger').trigger('keydown', { key: 'ArrowDown' })
|
|
63
|
+
await wrapper.vm.$nextTick()
|
|
64
|
+
|
|
65
|
+
expect(wrapper.find('.mint-avatar-menu__panel').exists()).toBe(true)
|
|
66
|
+
expect(document.activeElement).toBe(wrapper.findAll('[role="menuitem"]')[0].element)
|
|
67
|
+
|
|
68
|
+
wrapper.unmount()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('opens and focuses the last item on ArrowUp', async () => {
|
|
72
|
+
const wrapper = mountMenu()
|
|
73
|
+
await wrapper.get('.mint-avatar-menu__trigger').trigger('keydown', { key: 'ArrowUp' })
|
|
74
|
+
await wrapper.vm.$nextTick()
|
|
75
|
+
|
|
76
|
+
const menuItems = wrapper.findAll('[role="menuitem"]')
|
|
77
|
+
expect(document.activeElement).toBe(menuItems[menuItems.length - 1].element)
|
|
78
|
+
|
|
79
|
+
wrapper.unmount()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('opens and focuses the first item on Enter and Space', async () => {
|
|
83
|
+
for (const key of ['Enter', ' ']) {
|
|
84
|
+
const wrapper = mountMenu()
|
|
85
|
+
await wrapper.get('.mint-avatar-menu__trigger').trigger('keydown', { key })
|
|
86
|
+
await wrapper.vm.$nextTick()
|
|
87
|
+
|
|
88
|
+
expect(document.activeElement).toBe(wrapper.findAll('[role="menuitem"]')[0].element)
|
|
89
|
+
wrapper.unmount()
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('cycles item focus with ArrowDown/ArrowUp, wrapping at the ends', async () => {
|
|
94
|
+
const wrapper = mountMenu()
|
|
95
|
+
await wrapper.get('.mint-avatar-menu__trigger').trigger('keydown', { key: 'ArrowDown' })
|
|
96
|
+
await wrapper.vm.$nextTick()
|
|
97
|
+
|
|
98
|
+
const menuItems = wrapper.findAll('[role="menuitem"]')
|
|
99
|
+
const panel = wrapper.get('.mint-avatar-menu__panel')
|
|
100
|
+
|
|
101
|
+
await panel.trigger('keydown', { key: 'ArrowDown' })
|
|
102
|
+
expect(document.activeElement).toBe(menuItems[1].element)
|
|
103
|
+
|
|
104
|
+
await panel.trigger('keydown', { key: 'ArrowDown' })
|
|
105
|
+
expect(document.activeElement).toBe(menuItems[menuItems.length - 1].element)
|
|
106
|
+
|
|
107
|
+
await panel.trigger('keydown', { key: 'ArrowDown' })
|
|
108
|
+
expect(document.activeElement).toBe(menuItems[0].element)
|
|
109
|
+
|
|
110
|
+
await panel.trigger('keydown', { key: 'ArrowUp' })
|
|
111
|
+
expect(document.activeElement).toBe(menuItems[menuItems.length - 1].element)
|
|
112
|
+
|
|
113
|
+
wrapper.unmount()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('jumps to the first item on Home and the last on End', async () => {
|
|
117
|
+
const wrapper = mountMenu()
|
|
118
|
+
await wrapper.get('.mint-avatar-menu__trigger').trigger('keydown', { key: 'ArrowDown' })
|
|
119
|
+
await wrapper.vm.$nextTick()
|
|
120
|
+
|
|
121
|
+
const menuItems = wrapper.findAll('[role="menuitem"]')
|
|
122
|
+
const panel = wrapper.get('.mint-avatar-menu__panel')
|
|
123
|
+
|
|
124
|
+
await panel.trigger('keydown', { key: 'End' })
|
|
125
|
+
expect(document.activeElement).toBe(menuItems[menuItems.length - 1].element)
|
|
126
|
+
|
|
127
|
+
await panel.trigger('keydown', { key: 'Home' })
|
|
128
|
+
expect(document.activeElement).toBe(menuItems[0].element)
|
|
129
|
+
|
|
130
|
+
wrapper.unmount()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('closes on Escape and returns focus to the trigger', async () => {
|
|
134
|
+
const wrapper = mountMenu()
|
|
135
|
+
const trigger = wrapper.get('.mint-avatar-menu__trigger')
|
|
136
|
+
await trigger.trigger('keydown', { key: 'ArrowDown' })
|
|
137
|
+
await wrapper.vm.$nextTick()
|
|
138
|
+
|
|
139
|
+
await wrapper.get('.mint-avatar-menu__panel').trigger('keydown', { key: 'Escape' })
|
|
140
|
+
await wrapper.vm.$nextTick()
|
|
141
|
+
|
|
142
|
+
expect(wrapper.find('.mint-avatar-menu__panel').exists()).toBe(false)
|
|
143
|
+
expect(document.activeElement).toBe(trigger.element)
|
|
144
|
+
|
|
145
|
+
wrapper.unmount()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('closes on Tab without forcing focus', async () => {
|
|
149
|
+
const wrapper = mountMenu()
|
|
150
|
+
await wrapper.get('.mint-avatar-menu__trigger').trigger('keydown', { key: 'ArrowDown' })
|
|
151
|
+
await wrapper.vm.$nextTick()
|
|
152
|
+
|
|
153
|
+
await wrapper.get('.mint-avatar-menu__panel').trigger('keydown', { key: 'Tab' })
|
|
154
|
+
await wrapper.vm.$nextTick()
|
|
155
|
+
|
|
156
|
+
expect(wrapper.find('.mint-avatar-menu__panel').exists()).toBe(false)
|
|
157
|
+
|
|
158
|
+
wrapper.unmount()
|
|
159
|
+
})
|
|
160
|
+
})
|
|
38
161
|
})
|
|
@@ -37,4 +37,71 @@ describe('BaseTabs', () => {
|
|
|
37
37
|
expect(wrapper.findAll('svg.mint-tab__icon--svg')).toHaveLength(2)
|
|
38
38
|
expect(wrapper.findAll('path')).toHaveLength(3)
|
|
39
39
|
})
|
|
40
|
+
|
|
41
|
+
describe('keyboard navigation (WAI-ARIA tabs pattern)', () => {
|
|
42
|
+
const tabs = ['Run', 'Results', { id: 'settings', label: 'Settings', disabled: true }, 'Logs']
|
|
43
|
+
|
|
44
|
+
it('puts tabindex="0" only on the active tab', () => {
|
|
45
|
+
const wrapper = mount(BaseTabs, { props: { modelValue: 'Results', tabs } })
|
|
46
|
+
const buttons = wrapper.findAll('.mint-tab')
|
|
47
|
+
|
|
48
|
+
expect(buttons.map(b => b.attributes('tabindex'))).toEqual(['-1', '0', '-1', '-1'])
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('moves to and activates the next enabled tab on ArrowRight, skipping disabled tabs', async () => {
|
|
52
|
+
const wrapper = mount(BaseTabs, { props: { modelValue: 'Results', tabs } })
|
|
53
|
+
|
|
54
|
+
await wrapper.findAll('.mint-tab')[1].trigger('keydown', { key: 'ArrowRight' })
|
|
55
|
+
|
|
56
|
+
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['Logs'])
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('moves to the previous enabled tab on ArrowLeft, wrapping past the start', async () => {
|
|
60
|
+
const wrapper = mount(BaseTabs, { props: { modelValue: 'Run', tabs } })
|
|
61
|
+
|
|
62
|
+
await wrapper.findAll('.mint-tab')[0].trigger('keydown', { key: 'ArrowLeft' })
|
|
63
|
+
|
|
64
|
+
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['Logs'])
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('wraps past the end on ArrowRight', async () => {
|
|
68
|
+
const wrapper = mount(BaseTabs, { props: { modelValue: 'Logs', tabs } })
|
|
69
|
+
|
|
70
|
+
await wrapper.findAll('.mint-tab')[3].trigger('keydown', { key: 'ArrowRight' })
|
|
71
|
+
|
|
72
|
+
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['Run'])
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('jumps to the first tab on Home and the last on End', async () => {
|
|
76
|
+
const wrapper = mount(BaseTabs, { props: { modelValue: 'Results', tabs } })
|
|
77
|
+
|
|
78
|
+
await wrapper.findAll('.mint-tab')[1].trigger('keydown', { key: 'End' })
|
|
79
|
+
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['Logs'])
|
|
80
|
+
|
|
81
|
+
await wrapper.findAll('.mint-tab')[1].trigger('keydown', { key: 'Home' })
|
|
82
|
+
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['Run'])
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('moves DOM focus onto the newly-activated tab', async () => {
|
|
86
|
+
const wrapper = mount(BaseTabs, {
|
|
87
|
+
props: { modelValue: 'Run', tabs },
|
|
88
|
+
attachTo: document.body,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
await wrapper.findAll('.mint-tab')[0].trigger('keydown', { key: 'ArrowRight' })
|
|
92
|
+
await wrapper.setProps({ modelValue: 'Results' })
|
|
93
|
+
await wrapper.vm.$nextTick()
|
|
94
|
+
|
|
95
|
+
expect(document.activeElement).toBe(wrapper.findAll('.mint-tab')[1].element)
|
|
96
|
+
wrapper.unmount()
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('keeps aria-selected and aria-controls consistent with the active tab', () => {
|
|
100
|
+
const wrapper = mount(BaseTabs, { props: { modelValue: 'Results', tabs } })
|
|
101
|
+
const active = wrapper.findAll('.mint-tab')[1]
|
|
102
|
+
|
|
103
|
+
expect(active.attributes('aria-selected')).toBe('true')
|
|
104
|
+
expect(active.attributes('id')).toBe(active.attributes('aria-controls')?.replace('tabpanel', 'tab'))
|
|
105
|
+
})
|
|
106
|
+
})
|
|
40
107
|
})
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
1
|
+
import { afterEach, describe, it, expect } from 'vitest'
|
|
2
2
|
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { createPinia, setActivePinia } from 'pinia'
|
|
3
4
|
import DataFrame from '../../components/DataFrame.vue'
|
|
5
|
+
import { useSettingsStore } from '../../stores/settings'
|
|
4
6
|
import type { DataFrameColumn, SortState, PaginationState } from '../../types'
|
|
5
7
|
|
|
6
8
|
describe('DataFrame', () => {
|
|
@@ -167,7 +169,7 @@ describe('DataFrame', () => {
|
|
|
167
169
|
props: { data: mockData, columns: sortableColumns },
|
|
168
170
|
})
|
|
169
171
|
const nameHeader = wrapper.findAll('.mint-dataframe__th')[0]
|
|
170
|
-
await nameHeader.trigger('click')
|
|
172
|
+
await nameHeader.get('.mint-dataframe__th-button').trigger('click')
|
|
171
173
|
|
|
172
174
|
expect(wrapper.emitted('update:sort')).toHaveLength(1)
|
|
173
175
|
const sortState = wrapper.emitted('update:sort')?.[0]?.[0] as SortState
|
|
@@ -184,7 +186,7 @@ describe('DataFrame', () => {
|
|
|
184
186
|
},
|
|
185
187
|
})
|
|
186
188
|
const nameHeader = wrapper.findAll('.mint-dataframe__th')[0]
|
|
187
|
-
await nameHeader.trigger('click')
|
|
189
|
+
await nameHeader.get('.mint-dataframe__th-button').trigger('click')
|
|
188
190
|
|
|
189
191
|
const sortState = wrapper.emitted('update:sort')?.[0]?.[0] as SortState
|
|
190
192
|
expect(sortState.direction).toBe('desc')
|
|
@@ -199,7 +201,7 @@ describe('DataFrame', () => {
|
|
|
199
201
|
},
|
|
200
202
|
})
|
|
201
203
|
const nameHeader = wrapper.findAll('.mint-dataframe__th')[0]
|
|
202
|
-
await nameHeader.trigger('click')
|
|
204
|
+
await nameHeader.get('.mint-dataframe__th-button').trigger('click')
|
|
203
205
|
|
|
204
206
|
const sortState = wrapper.emitted('update:sort')?.[0]?.[0]
|
|
205
207
|
expect(sortState).toBeNull()
|
|
@@ -210,7 +212,7 @@ describe('DataFrame', () => {
|
|
|
210
212
|
props: { data: mockData, columns: sortableColumns },
|
|
211
213
|
})
|
|
212
214
|
const nameHeader = wrapper.findAll('.mint-dataframe__th')[0]
|
|
213
|
-
await nameHeader.trigger('click')
|
|
215
|
+
await nameHeader.get('.mint-dataframe__th-button').trigger('click')
|
|
214
216
|
|
|
215
217
|
// After sorting, first row should have name starting with 'A'
|
|
216
218
|
const firstRow = wrapper.findAll('.mint-dataframe__row')[0]
|
|
@@ -223,7 +225,7 @@ describe('DataFrame', () => {
|
|
|
223
225
|
props: { data: mockData, columns: sortableColumns },
|
|
224
226
|
})
|
|
225
227
|
const ageHeader = wrapper.findAll('.mint-dataframe__th')[1]
|
|
226
|
-
await ageHeader.trigger('click')
|
|
228
|
+
await ageHeader.get('.mint-dataframe__th-button').trigger('click')
|
|
227
229
|
|
|
228
230
|
// After sorting ascending, first row should have age 25
|
|
229
231
|
const firstRow = wrapper.findAll('.mint-dataframe__row')[0]
|
|
@@ -553,28 +555,38 @@ describe('DataFrame', () => {
|
|
|
553
555
|
const wrapper = mount(DataFrame, {
|
|
554
556
|
props: { data: [], columns: mockColumns },
|
|
555
557
|
})
|
|
556
|
-
expect(wrapper.find('.mint-
|
|
558
|
+
expect(wrapper.find('.mint-dataframe__state').exists()).toBe(true)
|
|
559
|
+
expect(wrapper.find('[data-state="empty"]').exists()).toBe(true)
|
|
557
560
|
})
|
|
558
561
|
|
|
559
562
|
it('should display empty text', () => {
|
|
560
563
|
const wrapper = mount(DataFrame, {
|
|
561
564
|
props: { data: [], columns: mockColumns },
|
|
562
565
|
})
|
|
563
|
-
expect(wrapper.find('.mint-
|
|
566
|
+
expect(wrapper.find('.mint-empty-state__title').text()).toBe('No data')
|
|
564
567
|
})
|
|
565
568
|
|
|
566
569
|
it('should use custom empty text', () => {
|
|
567
570
|
const wrapper = mount(DataFrame, {
|
|
568
571
|
props: { data: [], columns: mockColumns, emptyText: 'No records found' },
|
|
569
572
|
})
|
|
570
|
-
expect(wrapper.find('.mint-
|
|
573
|
+
expect(wrapper.find('.mint-empty-state__title').text()).toBe('No records found')
|
|
571
574
|
})
|
|
572
575
|
|
|
573
576
|
it('should not show empty state when loading', () => {
|
|
574
577
|
const wrapper = mount(DataFrame, {
|
|
575
578
|
props: { data: [], columns: mockColumns, loading: true },
|
|
576
579
|
})
|
|
577
|
-
expect(wrapper.find('
|
|
580
|
+
expect(wrapper.find('[data-state="empty"]').exists()).toBe(false)
|
|
581
|
+
})
|
|
582
|
+
|
|
583
|
+
it('should render the empty slot instead of the default placeholder', () => {
|
|
584
|
+
const wrapper = mount(DataFrame, {
|
|
585
|
+
props: { data: [], columns: mockColumns },
|
|
586
|
+
slots: { empty: '<p class="custom-empty">Nothing here</p>' },
|
|
587
|
+
})
|
|
588
|
+
expect(wrapper.find('.custom-empty').exists()).toBe(true)
|
|
589
|
+
expect(wrapper.find('[data-state="empty"]').exists()).toBe(false)
|
|
578
590
|
})
|
|
579
591
|
})
|
|
580
592
|
|
|
@@ -590,7 +602,40 @@ describe('DataFrame', () => {
|
|
|
590
602
|
const wrapper = mount(DataFrame, {
|
|
591
603
|
props: { data: mockData, columns: mockColumns, loading: true },
|
|
592
604
|
})
|
|
593
|
-
expect(wrapper.find('.mint-
|
|
605
|
+
expect(wrapper.find('.mint-spinner').exists()).toBe(true)
|
|
606
|
+
})
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
describe('error state', () => {
|
|
610
|
+
it('should render nothing extra when error is not passed', () => {
|
|
611
|
+
const wrapper = mount(DataFrame, {
|
|
612
|
+
props: { data: mockData, columns: mockColumns },
|
|
613
|
+
})
|
|
614
|
+
expect(wrapper.find('[data-state="error"]').exists()).toBe(false)
|
|
615
|
+
})
|
|
616
|
+
|
|
617
|
+
it('should replace the empty state with an error state', () => {
|
|
618
|
+
const wrapper = mount(DataFrame, {
|
|
619
|
+
props: { data: [], columns: mockColumns, error: 'Request failed' },
|
|
620
|
+
})
|
|
621
|
+
expect(wrapper.find('[data-state="error"]').exists()).toBe(true)
|
|
622
|
+
expect(wrapper.find('[data-state="empty"]').exists()).toBe(false)
|
|
623
|
+
expect(wrapper.find('.mint-empty-state__title').text()).toBe('Request failed')
|
|
624
|
+
})
|
|
625
|
+
|
|
626
|
+
it('should take precedence over the empty state even with rows present', () => {
|
|
627
|
+
const wrapper = mount(DataFrame, {
|
|
628
|
+
props: { data: mockData, columns: mockColumns, error: 'Boom' },
|
|
629
|
+
})
|
|
630
|
+
expect(wrapper.find('[data-state="error"]').exists()).toBe(true)
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
it('should render the error slot with the message', () => {
|
|
634
|
+
const wrapper = mount(DataFrame, {
|
|
635
|
+
props: { data: [], columns: mockColumns, error: 'Boom' },
|
|
636
|
+
slots: { error: '<p class="custom-error">custom error</p>' },
|
|
637
|
+
})
|
|
638
|
+
expect(wrapper.find('.custom-error').exists()).toBe(true)
|
|
594
639
|
})
|
|
595
640
|
})
|
|
596
641
|
|
|
@@ -911,5 +956,132 @@ describe('DataFrame', () => {
|
|
|
911
956
|
await wrapper.find('.mint-dataframe__resize-handle').trigger('click')
|
|
912
957
|
expect(wrapper.emitted('update:sort')).toBeUndefined()
|
|
913
958
|
})
|
|
959
|
+
|
|
960
|
+
it('should expose the resize handle as a slider with its current width', () => {
|
|
961
|
+
const wrapper = mount(DataFrame, {
|
|
962
|
+
props: {
|
|
963
|
+
data: mockData,
|
|
964
|
+
columns: [{ key: 'name', label: 'Name', minWidth: 80 }],
|
|
965
|
+
resizable: true,
|
|
966
|
+
columnWidths: { name: 240 },
|
|
967
|
+
},
|
|
968
|
+
})
|
|
969
|
+
|
|
970
|
+
const handle = wrapper.get('.mint-dataframe__resize-handle')
|
|
971
|
+
expect(handle.attributes('role')).toBe('slider')
|
|
972
|
+
expect(handle.attributes('tabindex')).toBe('0')
|
|
973
|
+
expect(handle.attributes('aria-label')).toBe('Resize Name column')
|
|
974
|
+
expect(handle.attributes('aria-valuenow')).toBe('240')
|
|
975
|
+
expect(handle.attributes('aria-valuemin')).toBe('80')
|
|
976
|
+
})
|
|
977
|
+
|
|
978
|
+
it('should resize a column with the arrow keys, clamped to the minimum width', async () => {
|
|
979
|
+
const wrapper = mount(DataFrame, {
|
|
980
|
+
props: {
|
|
981
|
+
data: mockData,
|
|
982
|
+
columns: [{ key: 'name', label: 'Name' }],
|
|
983
|
+
resizable: true,
|
|
984
|
+
columnWidths: { name: 120 },
|
|
985
|
+
},
|
|
986
|
+
})
|
|
987
|
+
|
|
988
|
+
const handle = wrapper.get('.mint-dataframe__resize-handle')
|
|
989
|
+
await handle.trigger('keydown', { key: 'ArrowRight' })
|
|
990
|
+
expect(wrapper.emitted('update:columnWidths')?.[0]).toEqual([{ name: 136 }])
|
|
991
|
+
|
|
992
|
+
await handle.trigger('keydown', { key: 'ArrowLeft' })
|
|
993
|
+
expect(wrapper.emitted('update:columnWidths')?.[1]).toEqual([{ name: 104 }])
|
|
994
|
+
|
|
995
|
+
await wrapper.setProps({ columnWidths: { name: 50 } })
|
|
996
|
+
await handle.trigger('keydown', { key: 'ArrowLeft' })
|
|
997
|
+
expect(wrapper.emitted('update:columnWidths')?.[2]).toEqual([{ name: 48 }])
|
|
998
|
+
})
|
|
999
|
+
})
|
|
1000
|
+
|
|
1001
|
+
describe('sortable header accessibility', () => {
|
|
1002
|
+
const sortableColumn = [{ key: 'name', label: 'Name', sortable: true }]
|
|
1003
|
+
|
|
1004
|
+
it('should render sortable headers as buttons and plain headers without one', () => {
|
|
1005
|
+
const wrapper = mount(DataFrame, {
|
|
1006
|
+
props: {
|
|
1007
|
+
data: mockData,
|
|
1008
|
+
columns: [...sortableColumn, { key: 'age', label: 'Age' }],
|
|
1009
|
+
},
|
|
1010
|
+
})
|
|
1011
|
+
|
|
1012
|
+
const headers = wrapper.findAll('.mint-dataframe__th')
|
|
1013
|
+
expect(headers[0].get('.mint-dataframe__th-button').element.tagName).toBe('BUTTON')
|
|
1014
|
+
expect(headers[1].get('.mint-dataframe__th-button').element.tagName).toBe('DIV')
|
|
1015
|
+
})
|
|
1016
|
+
|
|
1017
|
+
it('should reflect the sort direction with aria-sort', async () => {
|
|
1018
|
+
const wrapper = mount(DataFrame, {
|
|
1019
|
+
props: {
|
|
1020
|
+
data: mockData,
|
|
1021
|
+
columns: [...sortableColumn, { key: 'age', label: 'Age' }],
|
|
1022
|
+
},
|
|
1023
|
+
})
|
|
1024
|
+
|
|
1025
|
+
const nameHeader = () => wrapper.findAll('.mint-dataframe__th')[0]
|
|
1026
|
+
expect(nameHeader().attributes('aria-sort')).toBe('none')
|
|
1027
|
+
expect(wrapper.findAll('.mint-dataframe__th')[1].attributes('aria-sort')).toBeUndefined()
|
|
1028
|
+
|
|
1029
|
+
await nameHeader().get('.mint-dataframe__th-button').trigger('click')
|
|
1030
|
+
expect(nameHeader().attributes('aria-sort')).toBe('ascending')
|
|
1031
|
+
|
|
1032
|
+
await nameHeader().get('.mint-dataframe__th-button').trigger('click')
|
|
1033
|
+
expect(nameHeader().attributes('aria-sort')).toBe('descending')
|
|
1034
|
+
|
|
1035
|
+
await nameHeader().get('.mint-dataframe__th-button').trigger('click')
|
|
1036
|
+
expect(nameHeader().attributes('aria-sort')).toBe('none')
|
|
1037
|
+
})
|
|
1038
|
+
|
|
1039
|
+
it('should sort from the keyboard with Enter and Space', async () => {
|
|
1040
|
+
const wrapper = mount(DataFrame, {
|
|
1041
|
+
props: { data: mockData, columns: sortableColumn },
|
|
1042
|
+
})
|
|
1043
|
+
|
|
1044
|
+
const button = wrapper.get('.mint-dataframe__th-button')
|
|
1045
|
+
await button.trigger('keydown', { key: 'Enter' })
|
|
1046
|
+
expect(wrapper.emitted('update:sort')?.[0]?.[0]).toEqual({ key: 'name', direction: 'asc' })
|
|
1047
|
+
|
|
1048
|
+
await button.trigger('keydown', { key: ' ' })
|
|
1049
|
+
expect(wrapper.emitted('update:sort')?.[1]?.[0]).toEqual({ key: 'name', direction: 'desc' })
|
|
1050
|
+
})
|
|
1051
|
+
})
|
|
1052
|
+
|
|
1053
|
+
describe('table density default', () => {
|
|
1054
|
+
afterEach(() => {
|
|
1055
|
+
setActivePinia(undefined)
|
|
1056
|
+
})
|
|
1057
|
+
|
|
1058
|
+
it('falls back to md when no settings store is installed', () => {
|
|
1059
|
+
setActivePinia(undefined)
|
|
1060
|
+
const wrapper = mount(DataFrame, {
|
|
1061
|
+
props: { data: mockData, columns: mockColumns },
|
|
1062
|
+
})
|
|
1063
|
+
expect(wrapper.find('.mint-dataframe__td--md').exists()).toBe(true)
|
|
1064
|
+
})
|
|
1065
|
+
|
|
1066
|
+
it('follows the tableDensity setting when size is not passed', () => {
|
|
1067
|
+
setActivePinia(createPinia())
|
|
1068
|
+
useSettingsStore().tableDensity = 'compact'
|
|
1069
|
+
|
|
1070
|
+
const wrapper = mount(DataFrame, {
|
|
1071
|
+
props: { data: mockData, columns: mockColumns },
|
|
1072
|
+
})
|
|
1073
|
+
expect(wrapper.find('.mint-dataframe__td--sm').exists()).toBe(true)
|
|
1074
|
+
expect(wrapper.find('.mint-dataframe__td--md').exists()).toBe(false)
|
|
1075
|
+
})
|
|
1076
|
+
|
|
1077
|
+
it('lets an explicit size prop win over the density setting', () => {
|
|
1078
|
+
setActivePinia(createPinia())
|
|
1079
|
+
useSettingsStore().tableDensity = 'compact'
|
|
1080
|
+
|
|
1081
|
+
const wrapper = mount(DataFrame, {
|
|
1082
|
+
props: { data: mockData, columns: mockColumns, size: 'lg' },
|
|
1083
|
+
})
|
|
1084
|
+
expect(wrapper.find('.mint-dataframe__td--lg').exists()).toBe(true)
|
|
1085
|
+
})
|
|
914
1086
|
})
|
|
915
1087
|
})
|