@morscherlab/mint-sdk 1.0.0-beta.3 → 1.0.0-beta.4
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 +9 -2
- package/dist/__tests__/composables/experiment-utils.test.d.ts +1 -0
- package/dist/__tests__/composables/useApi.test.d.ts +1 -0
- package/dist/components/AppContainer.vue.d.ts +1 -1
- package/dist/components/AppLayout.vue.d.ts +20 -1
- package/dist/components/AppSidebar.vue.d.ts +56 -4
- package/dist/components/AppTopBar.vue.d.ts +7 -25
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +3 -1
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -0
- package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +5 -0
- package/dist/components/ComponentBindingRenderer.vue.d.ts +44 -0
- package/dist/components/ControlWorkspaceView.vue.d.ts +24 -7
- package/dist/components/DoseDesignWorkspaceView.vue.d.ts +149 -0
- package/dist/components/ExperimentTimeline.vue.d.ts +1 -1
- package/dist/components/FormBuilder.vue.d.ts +9 -9
- package/dist/components/PlateMapEditor.vue.d.ts +1 -1
- package/dist/components/PluginWorkspaceView.vue.d.ts +310 -0
- package/dist/components/SettingsModal.vue.d.ts +1 -1
- package/dist/components/WellPlate.vue.d.ts +2 -2
- package/dist/components/index.d.ts +3 -12
- package/dist/components/index.js +3 -3
- package/dist/components/{AppPageSelector.vue.d.ts → internal/AppPageSelectorInternal.vue.d.ts} +1 -1
- package/dist/components/{AppPillNav.vue.d.ts → internal/AppPillNavInternal.vue.d.ts} +3 -1
- package/dist/components/{CalendarGridPanel.vue.d.ts → internal/CalendarGridPanelInternal.vue.d.ts} +1 -1
- package/dist/components/internal/FormSectionRenderer.vue.d.ts +4 -4
- package/dist/components/{WellEditPopup.vue.d.ts → internal/WellEditPopupInternal.vue.d.ts} +1 -1
- package/dist/{components-D_Sr0adg.js → components-BkGF4B4y.js} +4484 -3967
- package/dist/components-BkGF4B4y.js.map +1 -0
- package/dist/composables/experiment-utils.d.ts +8 -0
- package/dist/composables/index.d.ts +5 -7
- package/dist/composables/index.js +4 -4
- package/dist/composables/useAppExperiment.d.ts +31 -2
- package/dist/composables/useBioTemplateComponents.d.ts +5 -3
- package/dist/composables/useBioTemplatePackWorkspace.d.ts +3 -2
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +6 -5
- package/dist/composables/useBioTemplateWorkspace.d.ts +5 -4
- package/dist/composables/useControlSchema.d.ts +43 -21
- package/dist/composables/usePluginClient.d.ts +5 -2
- package/dist/{composables-C3dpXQN5.js → composables-CHsME9H1.js} +40 -28
- package/dist/composables-CHsME9H1.js.map +1 -0
- package/dist/index.d.ts +5 -12
- package/dist/index.js +5 -5
- package/dist/install.js +2 -2
- package/dist/styles.css +3625 -3651
- package/dist/templates/componentBindings.d.ts +13 -0
- package/dist/templates/index.d.ts +3 -3
- package/dist/templates/index.js +2 -2
- package/dist/{templates-50NPjaxL.js → templates-B5jmTWuk.js} +111 -56
- package/dist/templates-B5jmTWuk.js.map +1 -0
- package/dist/types/components.d.ts +6 -25
- package/dist/types/index.d.ts +1 -1
- package/dist/{useScheduleDrag-D4oWdh41.js → useScheduleDrag-BgzpQT53.js} +160 -117
- package/dist/useScheduleDrag-BgzpQT53.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/ActionItem.test.ts +6 -6
- package/src/__tests__/components/AppLayout.test.ts +44 -0
- package/src/__tests__/components/AppPageSelector.test.ts +8 -8
- package/src/__tests__/components/AppPillNav.test.ts +53 -6
- package/src/__tests__/components/AppSidebar.test.ts +126 -0
- package/src/__tests__/components/AppToastContainer.test.ts +0 -11
- package/src/__tests__/components/AppTopBar.test.ts +182 -119
- package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +7 -1
- package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +15 -1
- package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +26 -1
- package/src/__tests__/components/CalendarGridPanel.test.ts +3 -3
- package/src/__tests__/components/ComponentBindingRenderer.test.ts +161 -0
- package/src/__tests__/components/ControlWorkspaceView.test.ts +134 -63
- package/src/__tests__/components/DateTimePicker.test.ts +2 -2
- package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +185 -0
- package/src/__tests__/components/PluginWorkspaceView.test.ts +548 -0
- package/src/__tests__/composables/experiment-utils.test.ts +30 -0
- package/src/__tests__/composables/useApi.test.ts +30 -0
- package/src/__tests__/composables/useAppExperiment.test.ts +100 -1
- package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +6 -3
- package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +6 -6
- package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +6 -1
- package/src/__tests__/composables/useControlSchema.test.ts +150 -36
- package/src/__tests__/composables/usePluginClient.test.ts +99 -2
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +120 -25
- package/src/__tests__/templates/templates.test.ts +12 -0
- package/src/components/AppAvatarMenu.vue +3 -3
- package/src/components/AppLayout.story.vue +39 -0
- package/src/components/AppLayout.vue +83 -2
- package/src/components/AppPluginSwitcher.vue +5 -5
- package/src/components/AppSidebar.story.vue +113 -5
- package/src/components/AppSidebar.vue +144 -24
- package/src/components/AppTopBar.story.vue +2 -5
- package/src/components/AppTopBar.vue +35 -425
- package/src/components/BioTemplateExperimentWorkspaceView.story.vue +2 -2
- package/src/components/BioTemplateExperimentWorkspaceView.vue +6 -0
- package/src/components/BioTemplatePackWorkspaceView.story.vue +4 -4
- package/src/components/BioTemplatePackWorkspaceView.vue +1 -0
- package/src/components/BioTemplatePresetWorkspaceView.story.vue +14 -2
- package/src/components/BioTemplatePresetWorkspaceView.vue +11 -2
- package/src/components/BioTemplateRenderer.vue +15 -227
- package/src/components/ComponentBindingRenderer.story.vue +57 -0
- package/src/components/ComponentBindingRenderer.vue +308 -0
- package/src/components/ControlWorkspaceView.story.vue +20 -9
- package/src/components/ControlWorkspaceView.vue +43 -12
- package/src/components/DatePicker.vue +2 -2
- package/src/components/DateTimePicker.vue +2 -2
- package/src/components/DoseDesignWorkspaceView.story.vue +77 -0
- package/src/components/DoseDesignWorkspaceView.vue +255 -0
- package/src/components/ExperimentPopover.vue +2 -6
- package/src/components/ExperimentSelectorModal.vue +6 -5
- package/src/components/FormBuilder.story.vue +190 -0
- package/src/components/PluginWorkspaceView.story.vue +334 -0
- package/src/components/PluginWorkspaceView.vue +708 -0
- package/src/components/SettingsModal.story.vue +87 -0
- package/src/components/WellPlate.vue +2 -2
- package/src/components/index.ts +3 -12
- package/src/components/{AppPageSelector.vue → internal/AppPageSelectorInternal.vue} +9 -9
- package/src/components/internal/AppPillNavInternal.vue +194 -0
- package/src/components/{CalendarGridPanel.vue → internal/CalendarGridPanelInternal.vue} +1 -1
- package/src/components/{WellEditPopup.vue → internal/WellEditPopupInternal.vue} +3 -3
- package/src/composables/experiment-utils.ts +26 -0
- package/src/composables/index.ts +21 -7
- package/src/composables/useApi.ts +9 -2
- package/src/composables/useAppExperiment.ts +85 -13
- package/src/composables/useBioTemplateComponents.ts +12 -0
- package/src/composables/useBioTemplatePackWorkspace.ts +6 -2
- package/src/composables/useBioTemplatePresetWorkspace.ts +10 -21
- package/src/composables/useBioTemplateWorkspace.ts +6 -4
- package/src/composables/useControlSchema.ts +157 -69
- package/src/composables/usePluginClient.ts +50 -9
- package/src/index.ts +6 -563
- package/src/styles/components/app-layout.css +82 -0
- package/src/styles/components/app-pill-nav.css +70 -0
- package/src/styles/components/app-sidebar.css +119 -0
- package/src/styles/components/app-top-bar.css +0 -235
- package/src/styles/index.css +0 -1
- package/src/templates/componentBindings.ts +38 -0
- package/src/templates/index.ts +4 -0
- package/src/types/components.ts +6 -31
- package/src/types/index.ts +2 -6
- package/dist/__tests__/composables/usePluginApi.test.d.ts +0 -13
- package/dist/components/FormFieldRenderer.vue.d.ts +0 -28
- package/dist/components/FormSection.vue.d.ts +0 -30
- package/dist/components/GroupingModal.vue.d.ts +0 -12
- package/dist/components/SettingsButton.vue.d.ts +0 -30
- package/dist/components/ToastNotification.vue.d.ts +0 -2
- package/dist/components-D_Sr0adg.js.map +0 -1
- package/dist/composables/usePluginApi.d.ts +0 -22
- package/dist/composables-C3dpXQN5.js.map +0 -1
- package/dist/templates-50NPjaxL.js.map +0 -1
- package/dist/useScheduleDrag-D4oWdh41.js.map +0 -1
- package/src/__tests__/components/FormCompatibility.test.ts +0 -94
- package/src/__tests__/components/GroupingModal.test.ts +0 -73
- package/src/__tests__/components/SettingsButton.test.ts +0 -44
- package/src/__tests__/composables/usePluginApi.test.ts +0 -81
- package/src/components/AppPillNav.vue +0 -71
- package/src/components/FormFieldRenderer.vue +0 -35
- package/src/components/FormSection.vue +0 -37
- package/src/components/GroupingModal.story.vue +0 -52
- package/src/components/GroupingModal.vue +0 -61
- package/src/components/SettingsButton.story.vue +0 -58
- package/src/components/SettingsButton.vue +0 -64
- package/src/components/ToastNotification.vue +0 -9
- package/src/composables/usePluginApi.ts +0 -32
- package/src/styles/components/settings-button.css +0 -31
- /package/dist/__tests__/components/{FormCompatibility.test.d.ts → ComponentBindingRenderer.test.d.ts} +0 -0
- /package/dist/__tests__/components/{GroupingModal.test.d.ts → DoseDesignWorkspaceView.test.d.ts} +0 -0
- /package/dist/__tests__/components/{SettingsButton.test.d.ts → PluginWorkspaceView.test.d.ts} +0 -0
- /package/dist/components/{ActionItem.vue.d.ts → internal/ActionItemInternal.vue.d.ts} +0 -0
- /package/src/components/{ActionItem.vue → internal/ActionItemInternal.vue} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morscherlab/mint-sdk",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
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",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
2
|
import { defineComponent, h } from 'vue'
|
|
3
3
|
import { describe, expect, it, vi } from 'vitest'
|
|
4
|
-
import
|
|
4
|
+
import ActionItemInternal from '../../components/internal/ActionItemInternal.vue'
|
|
5
5
|
|
|
6
6
|
function createRouterLinkStub(navigate = vi.fn()) {
|
|
7
7
|
return defineComponent({
|
|
@@ -18,9 +18,9 @@ function createRouterLinkStub(navigate = vi.fn()) {
|
|
|
18
18
|
})
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
describe('
|
|
21
|
+
describe('ActionItemInternal', () => {
|
|
22
22
|
it('renders a normal button action and emits clicks', async () => {
|
|
23
|
-
const wrapper = mount(
|
|
23
|
+
const wrapper = mount(ActionItemInternal, {
|
|
24
24
|
slots: { default: 'Run' },
|
|
25
25
|
})
|
|
26
26
|
|
|
@@ -33,7 +33,7 @@ describe('ActionItem', () => {
|
|
|
33
33
|
})
|
|
34
34
|
|
|
35
35
|
it('renders disabled href actions without navigating or emitting', async () => {
|
|
36
|
-
const wrapper = mount(
|
|
36
|
+
const wrapper = mount(ActionItemInternal, {
|
|
37
37
|
props: {
|
|
38
38
|
href: '/disabled',
|
|
39
39
|
disabled: true,
|
|
@@ -52,7 +52,7 @@ describe('ActionItem', () => {
|
|
|
52
52
|
|
|
53
53
|
it('renders router actions through router-link and navigates after emitting', async () => {
|
|
54
54
|
const navigate = vi.fn()
|
|
55
|
-
const wrapper = mount(
|
|
55
|
+
const wrapper = mount(ActionItemInternal, {
|
|
56
56
|
props: { to: '/settings' },
|
|
57
57
|
slots: { default: 'Settings' },
|
|
58
58
|
global: {
|
|
@@ -73,7 +73,7 @@ describe('ActionItem', () => {
|
|
|
73
73
|
|
|
74
74
|
it('renders disabled router actions without href, navigation, or emitted clicks', async () => {
|
|
75
75
|
const navigate = vi.fn()
|
|
76
|
-
const wrapper = mount(
|
|
76
|
+
const wrapper = mount(ActionItemInternal, {
|
|
77
77
|
props: {
|
|
78
78
|
to: '/settings',
|
|
79
79
|
disabled: true,
|
|
@@ -124,6 +124,50 @@ describe('AppLayout', () => {
|
|
|
124
124
|
})
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
+
describe('responsive sidebar shell', () => {
|
|
128
|
+
it('does not render mobile toggle by default', () => {
|
|
129
|
+
const wrapper = mount(AppLayout, {
|
|
130
|
+
slots: { sidebar: '<div>Sidebar</div>' },
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
expect(wrapper.find('.mint-layout__sidebar-toggle').exists()).toBe(false)
|
|
134
|
+
expect(wrapper.find('.mint-layout--responsive-sidebar').exists()).toBe(false)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('renders a mobile toggle when responsiveSidebar is enabled', async () => {
|
|
138
|
+
const wrapper = mount(AppLayout, {
|
|
139
|
+
props: { responsiveSidebar: true },
|
|
140
|
+
slots: { sidebar: '<div>Sidebar</div>' },
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const toggle = wrapper.get('.mint-layout__sidebar-toggle')
|
|
144
|
+
expect(toggle.attributes('aria-expanded')).toBe('false')
|
|
145
|
+
expect(wrapper.find('.mint-layout__sidebar-backdrop').exists()).toBe(false)
|
|
146
|
+
|
|
147
|
+
await toggle.trigger('click')
|
|
148
|
+
|
|
149
|
+
expect(wrapper.find('.mint-layout--sidebar-open').exists()).toBe(true)
|
|
150
|
+
expect(wrapper.get('.mint-layout__sidebar-toggle').attributes('aria-expanded')).toBe('true')
|
|
151
|
+
expect(wrapper.find('.mint-layout__sidebar-backdrop').exists()).toBe(true)
|
|
152
|
+
expect(wrapper.emitted('update:sidebarOpen')).toEqual([[true]])
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('supports controlled mobile sidebar open state and closes from backdrop', async () => {
|
|
156
|
+
const wrapper = mount(AppLayout, {
|
|
157
|
+
props: {
|
|
158
|
+
responsiveSidebar: true,
|
|
159
|
+
sidebarOpen: true,
|
|
160
|
+
},
|
|
161
|
+
slots: { sidebar: '<div>Sidebar</div>' },
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
expect(wrapper.find('.mint-layout--sidebar-open').exists()).toBe(true)
|
|
165
|
+
await wrapper.get('.mint-layout__sidebar-backdrop').trigger('click')
|
|
166
|
+
|
|
167
|
+
expect(wrapper.emitted('update:sidebarOpen')).toEqual([[false]])
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
127
171
|
describe('integration: all slots', () => {
|
|
128
172
|
it('should render topbar, sidebar, and main content together', () => {
|
|
129
173
|
const wrapper = mount(AppLayout, {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
2
|
import { describe, expect, it } from 'vitest'
|
|
3
|
-
import
|
|
3
|
+
import AppPageSelectorInternal from '../../components/internal/AppPageSelectorInternal.vue'
|
|
4
4
|
|
|
5
|
-
describe('
|
|
5
|
+
describe('AppPageSelectorInternal', () => {
|
|
6
6
|
it('accepts string shorthand pages', async () => {
|
|
7
|
-
const wrapper = mount(
|
|
7
|
+
const wrapper = mount(AppPageSelectorInternal, {
|
|
8
8
|
props: {
|
|
9
9
|
currentPageId: 'Workspace',
|
|
10
10
|
pages: ['Workspace', 'Results'],
|
|
@@ -25,7 +25,7 @@ describe('AppPageSelector', () => {
|
|
|
25
25
|
})
|
|
26
26
|
|
|
27
27
|
it('closes linked page actions without emitting select', async () => {
|
|
28
|
-
const wrapper = mount(
|
|
28
|
+
const wrapper = mount(AppPageSelectorInternal, {
|
|
29
29
|
props: {
|
|
30
30
|
currentPageId: 'workspace',
|
|
31
31
|
pages: [
|
|
@@ -43,7 +43,7 @@ describe('AppPageSelector', () => {
|
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
it('keeps disabled linked page actions inert', async () => {
|
|
46
|
-
const wrapper = mount(
|
|
46
|
+
const wrapper = mount(AppPageSelectorInternal, {
|
|
47
47
|
props: {
|
|
48
48
|
currentPageId: 'workspace',
|
|
49
49
|
pages: [
|
|
@@ -66,7 +66,7 @@ describe('AppPageSelector', () => {
|
|
|
66
66
|
|
|
67
67
|
it('emits select for button page actions', async () => {
|
|
68
68
|
const page = { id: 'workspace', label: 'Workspace' }
|
|
69
|
-
const wrapper = mount(
|
|
69
|
+
const wrapper = mount(AppPageSelectorInternal, {
|
|
70
70
|
props: {
|
|
71
71
|
pages: [page],
|
|
72
72
|
},
|
|
@@ -81,7 +81,7 @@ describe('AppPageSelector', () => {
|
|
|
81
81
|
it('renders PluginIcon-compatible metadata icons and keeps symbolic icons as fallback initials', async () => {
|
|
82
82
|
const iconPath = 'M4 19h16M7 16V8m5 8V4m5 12v-6'
|
|
83
83
|
const pngIcon = 'data:image/png;base64,iVBORw0KGgo='
|
|
84
|
-
const wrapper = mount(
|
|
84
|
+
const wrapper = mount(AppPageSelectorInternal, {
|
|
85
85
|
props: {
|
|
86
86
|
pages: [
|
|
87
87
|
{ id: 'analysis', label: 'Analysis', icon: iconPath },
|
|
@@ -117,7 +117,7 @@ describe('AppPageSelector', () => {
|
|
|
117
117
|
|
|
118
118
|
it('renders https page icons through PluginIcon', async () => {
|
|
119
119
|
const httpsIcon = 'https://example.com/plugin-icon.png'
|
|
120
|
-
const wrapper = mount(
|
|
120
|
+
const wrapper = mount(AppPageSelectorInternal, {
|
|
121
121
|
props: {
|
|
122
122
|
pages: [
|
|
123
123
|
{ id: 'docs', label: 'Docs', icon: httpsIcon },
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
2
|
import { describe, expect, it } from 'vitest'
|
|
3
|
-
import
|
|
3
|
+
import AppPillNavInternal from '../../components/internal/AppPillNavInternal.vue'
|
|
4
4
|
|
|
5
|
-
describe('
|
|
5
|
+
describe('AppPillNavInternal', () => {
|
|
6
6
|
it('accepts string shorthand items', async () => {
|
|
7
|
-
const wrapper = mount(
|
|
7
|
+
const wrapper = mount(AppPillNavInternal, {
|
|
8
8
|
props: {
|
|
9
9
|
currentItemId: 'Overview',
|
|
10
10
|
items: ['Overview', 'Analysis'],
|
|
@@ -22,7 +22,7 @@ describe('AppPillNav', () => {
|
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
it('emits select for button items but not linked items', async () => {
|
|
25
|
-
const wrapper = mount(
|
|
25
|
+
const wrapper = mount(AppPillNavInternal, {
|
|
26
26
|
props: {
|
|
27
27
|
currentItemId: 'overview',
|
|
28
28
|
items: [
|
|
@@ -40,7 +40,7 @@ describe('AppPillNav', () => {
|
|
|
40
40
|
|
|
41
41
|
it('renders SVG icons from item metadata', () => {
|
|
42
42
|
const icon = ['M4 12h16', 'M12 4v16']
|
|
43
|
-
const wrapper = mount(
|
|
43
|
+
const wrapper = mount(AppPillNavInternal, {
|
|
44
44
|
props: {
|
|
45
45
|
items: [
|
|
46
46
|
{ id: 'run', label: 'Run', icon },
|
|
@@ -59,7 +59,7 @@ describe('AppPillNav', () => {
|
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
it('prevents disabled link navigation and selection', async () => {
|
|
62
|
-
const wrapper = mount(
|
|
62
|
+
const wrapper = mount(AppPillNavInternal, {
|
|
63
63
|
props: {
|
|
64
64
|
items: [
|
|
65
65
|
{ id: 'docs', label: 'Docs', href: '/docs', disabled: true },
|
|
@@ -75,4 +75,51 @@ describe('AppPillNav', () => {
|
|
|
75
75
|
|
|
76
76
|
expect(wrapper.emitted('select')).toBeUndefined()
|
|
77
77
|
})
|
|
78
|
+
|
|
79
|
+
it('supports dropdown child items for grouped pill navigation', async () => {
|
|
80
|
+
const wrapper = mount(AppPillNavInternal, {
|
|
81
|
+
props: {
|
|
82
|
+
currentItemId: 'pca',
|
|
83
|
+
items: [
|
|
84
|
+
{ id: 'charts', label: 'Charts' },
|
|
85
|
+
{
|
|
86
|
+
id: 'visualize',
|
|
87
|
+
label: 'Visualize',
|
|
88
|
+
children: [
|
|
89
|
+
{ id: 'pca', label: 'PCA', description: 'Principal component analysis' },
|
|
90
|
+
{ id: 'heatmap', label: 'Heatmap', description: 'Metabolite heatmap' },
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const visualizeButton = wrapper.findAll('.mint-pill-nav__item')[1]
|
|
98
|
+
expect(visualizeButton.classes()).toContain('mint-pill-nav__item--active')
|
|
99
|
+
expect(wrapper.find('.mint-pill-nav__dropdown').exists()).toBe(false)
|
|
100
|
+
|
|
101
|
+
await visualizeButton.trigger('click')
|
|
102
|
+
|
|
103
|
+
expect(wrapper.find('.mint-pill-nav__dropdown').exists()).toBe(true)
|
|
104
|
+
expect(wrapper.findAll('.mint-pill-nav__dropdown-item').map(item => item.text())).toEqual([
|
|
105
|
+
'PCAPrincipal component analysis',
|
|
106
|
+
'HeatmapMetabolite heatmap',
|
|
107
|
+
])
|
|
108
|
+
|
|
109
|
+
await wrapper.findAll('.mint-pill-nav__dropdown-item')[1].trigger('click')
|
|
110
|
+
|
|
111
|
+
expect(wrapper.emitted('option-select')).toEqual([
|
|
112
|
+
[
|
|
113
|
+
{ id: 'heatmap', label: 'Heatmap', description: 'Metabolite heatmap' },
|
|
114
|
+
{
|
|
115
|
+
id: 'visualize',
|
|
116
|
+
label: 'Visualize',
|
|
117
|
+
children: [
|
|
118
|
+
{ id: 'pca', label: 'PCA', description: 'Principal component analysis' },
|
|
119
|
+
{ id: 'heatmap', label: 'Heatmap', description: 'Metabolite heatmap' },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
])
|
|
124
|
+
})
|
|
78
125
|
})
|
|
@@ -145,6 +145,120 @@ describe('AppSidebar', () => {
|
|
|
145
145
|
})
|
|
146
146
|
})
|
|
147
147
|
|
|
148
|
+
describe('sidebar chrome and collapse state', () => {
|
|
149
|
+
it('applies analysis variant defaults for common plugin sidebars', () => {
|
|
150
|
+
const wrapper = mount(AppSidebar, {
|
|
151
|
+
props: {
|
|
152
|
+
panels: samplePanels,
|
|
153
|
+
activeView: 'analysis',
|
|
154
|
+
variant: 'analysis',
|
|
155
|
+
title: 'Peak Picking',
|
|
156
|
+
},
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
expect(wrapper.find('.mint-sidebar--analysis').exists()).toBe(true)
|
|
160
|
+
expect(wrapper.find('.mint-sidebar--static').exists()).toBe(true)
|
|
161
|
+
expect(wrapper.find('.mint-sidebar--floating').exists()).toBe(false)
|
|
162
|
+
expect(wrapper.find('.mint-sidebar').attributes('style')).toContain('width: 20rem')
|
|
163
|
+
expect(wrapper.find('.mint-sidebar__collapse-button').exists()).toBe(true)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('lets explicit props override analysis variant defaults', () => {
|
|
167
|
+
const wrapper = mount(AppSidebar, {
|
|
168
|
+
props: {
|
|
169
|
+
panels: samplePanels,
|
|
170
|
+
activeView: 'analysis',
|
|
171
|
+
variant: 'analysis',
|
|
172
|
+
floating: true,
|
|
173
|
+
width: '18rem',
|
|
174
|
+
collapsible: false,
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
expect(wrapper.find('.mint-sidebar--analysis').exists()).toBe(true)
|
|
179
|
+
expect(wrapper.find('.mint-sidebar--floating').exists()).toBe(true)
|
|
180
|
+
expect(wrapper.find('.mint-sidebar').attributes('style')).toContain('width: 18rem')
|
|
181
|
+
expect(wrapper.find('.mint-sidebar__collapse-button').exists()).toBe(false)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('renders title, subtitle, and badge in the built-in header', () => {
|
|
185
|
+
const wrapper = mount(AppSidebar, {
|
|
186
|
+
props: {
|
|
187
|
+
panels: samplePanels,
|
|
188
|
+
activeView: 'analysis',
|
|
189
|
+
title: 'Peak Picking',
|
|
190
|
+
subtitle: 'run-001.msd',
|
|
191
|
+
badge: 3,
|
|
192
|
+
},
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
expect(wrapper.find('.mint-sidebar__title').text()).toBe('Peak Picking')
|
|
196
|
+
expect(wrapper.find('.mint-sidebar__subtitle').text()).toBe('run-001.msd')
|
|
197
|
+
expect(wrapper.find('.mint-sidebar__badge').text()).toBe('3')
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('toggles uncontrolled collapsed state and emits the update', async () => {
|
|
201
|
+
const wrapper = mount(AppSidebar, {
|
|
202
|
+
props: {
|
|
203
|
+
panels: samplePanels,
|
|
204
|
+
activeView: 'analysis',
|
|
205
|
+
title: 'Peak Picking',
|
|
206
|
+
collapsible: true,
|
|
207
|
+
width: '18rem',
|
|
208
|
+
collapsedWidth: '3rem',
|
|
209
|
+
},
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
expect(wrapper.find('.mint-sidebar').attributes('style')).toContain('width: 18rem')
|
|
213
|
+
expect(wrapper.findAllComponents(CollapsibleCard)).toHaveLength(2)
|
|
214
|
+
|
|
215
|
+
await wrapper.get('.mint-sidebar__collapse-button').trigger('click')
|
|
216
|
+
|
|
217
|
+
expect(wrapper.find('.mint-sidebar--collapsed').exists()).toBe(true)
|
|
218
|
+
expect(wrapper.find('.mint-sidebar').attributes('style')).toContain('width: 3rem')
|
|
219
|
+
expect(wrapper.findAllComponents(CollapsibleCard)).toHaveLength(0)
|
|
220
|
+
expect(wrapper.emitted('update:collapsed')).toEqual([[true]])
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('supports controlled collapsed state and a collapsed slot', () => {
|
|
224
|
+
const wrapper = mount(AppSidebar, {
|
|
225
|
+
props: {
|
|
226
|
+
panels: samplePanels,
|
|
227
|
+
activeView: 'analysis',
|
|
228
|
+
collapsible: true,
|
|
229
|
+
collapsed: true,
|
|
230
|
+
},
|
|
231
|
+
slots: {
|
|
232
|
+
collapsed: '<button class="collapsed-action">2</button>',
|
|
233
|
+
},
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
expect(wrapper.find('.mint-sidebar--collapsed').exists()).toBe(true)
|
|
237
|
+
expect(wrapper.find('.collapsed-action').text()).toBe('2')
|
|
238
|
+
expect(wrapper.findAllComponents(CollapsibleCard)).toHaveLength(0)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('renders an empty analysis shell for route-owned sidebar content', () => {
|
|
242
|
+
const wrapper = mount(AppSidebar, {
|
|
243
|
+
props: {
|
|
244
|
+
variant: 'analysis',
|
|
245
|
+
title: 'Sequence',
|
|
246
|
+
contentId: 'seqgen-sidebar',
|
|
247
|
+
showWhenEmpty: true,
|
|
248
|
+
},
|
|
249
|
+
slots: {
|
|
250
|
+
footer: '<button class="generate-action">Generate</button>',
|
|
251
|
+
},
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
expect(wrapper.find('.mint-sidebar--hidden').exists()).toBe(false)
|
|
255
|
+
expect(wrapper.find('.mint-sidebar--analysis').exists()).toBe(true)
|
|
256
|
+
expect(wrapper.find('.mint-sidebar__sections').attributes('id')).toBe('seqgen-sidebar')
|
|
257
|
+
expect(wrapper.findAllComponents(CollapsibleCard)).toHaveLength(0)
|
|
258
|
+
expect(wrapper.find('.generate-action').exists()).toBe(true)
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
148
262
|
describe('panels and activeView', () => {
|
|
149
263
|
it('should render sections for active view', () => {
|
|
150
264
|
const wrapper = mount(AppSidebar, {
|
|
@@ -569,6 +683,18 @@ describe('AppSidebar', () => {
|
|
|
569
683
|
})
|
|
570
684
|
|
|
571
685
|
describe('slots', () => {
|
|
686
|
+
it('renders the default slot as sidebar content without panels', () => {
|
|
687
|
+
const wrapper = mount(AppSidebar, {
|
|
688
|
+
props: { variant: 'analysis' },
|
|
689
|
+
slots: {
|
|
690
|
+
default: '<div class="route-sidebar-content">Route controls</div>',
|
|
691
|
+
},
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
expect(wrapper.find('.mint-sidebar--hidden').exists()).toBe(false)
|
|
695
|
+
expect(wrapper.find('.route-sidebar-content').text()).toBe('Route controls')
|
|
696
|
+
})
|
|
697
|
+
|
|
572
698
|
it('should render header slot', () => {
|
|
573
699
|
const wrapper = mount(AppSidebar, {
|
|
574
700
|
props: { panels: samplePanels, activeView: 'analysis' },
|
|
@@ -2,7 +2,6 @@ import { mount } from '@vue/test-utils'
|
|
|
2
2
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
3
3
|
import { nextTick } from 'vue'
|
|
4
4
|
import AppToastContainer from '../../components/AppToastContainer.vue'
|
|
5
|
-
import ToastNotification from '../../components/ToastNotification.vue'
|
|
6
5
|
import { useToast } from '../../composables/useToast'
|
|
7
6
|
|
|
8
7
|
describe('AppToastContainer', () => {
|
|
@@ -35,14 +34,4 @@ describe('AppToastContainer', () => {
|
|
|
35
34
|
|
|
36
35
|
expect(toast.toasts.value).toHaveLength(0)
|
|
37
36
|
})
|
|
38
|
-
|
|
39
|
-
it('keeps ToastNotification as a compatibility wrapper', async () => {
|
|
40
|
-
const wrapper = mountContainer(ToastNotification)
|
|
41
|
-
|
|
42
|
-
toast.info('Legacy import still works', 10000)
|
|
43
|
-
await nextTick()
|
|
44
|
-
|
|
45
|
-
expect(wrapper.findComponent(AppToastContainer).exists()).toBe(true)
|
|
46
|
-
expect(wrapper.find('.mint-toast__message').text()).toBe('Legacy import still works')
|
|
47
|
-
})
|
|
48
37
|
})
|