@morscherlab/mint-sdk 1.4.0-beta.9 → 1.4.0
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 +14 -3
- package/dist/{AlertBox-B2Bgr3ei.js → AlertBox-DwgNTEqf.js} +3 -3
- package/dist/{AlertBox-B2Bgr3ei.js.map → AlertBox-DwgNTEqf.js.map} +1 -1
- package/dist/{AppSidebar-Dq9W1jE2.js → AppSidebar-CZZaxni0.js} +1103 -144
- package/dist/AppSidebar-CZZaxni0.js.map +1 -0
- package/dist/{ConfirmDialog-nbJj51is.js → ConfirmDialog-CjWoxAD1.js} +2 -2
- package/dist/{ConfirmDialog-nbJj51is.js.map → ConfirmDialog-CjWoxAD1.js.map} +1 -1
- package/dist/{ExperimentPopover-5T1fAq2L.js → ExperimentPopover-DujiuQIO.js} +5 -5
- package/dist/{ExperimentPopover-5T1fAq2L.js.map → ExperimentPopover-DujiuQIO.js.map} +1 -1
- package/dist/{ExperimentSelectorModal-65peG4Up.js → ExperimentSelectorModal-B4j4eUOJ.js} +114 -98
- package/dist/ExperimentSelectorModal-B4j4eUOJ.js.map +1 -0
- package/dist/{SegmentedControl-BtnnjWzs.js → SegmentedControl-DyUPH3I5.js} +2 -2
- package/dist/{SegmentedControl-BtnnjWzs.js.map → SegmentedControl-DyUPH3I5.js.map} +1 -1
- package/dist/{SettingsModal-483nyI6b.js → SettingsModal-D2MOOtuO.js} +26 -25
- package/dist/SettingsModal-D2MOOtuO.js.map +1 -0
- package/dist/__stories__/experiment-helpers.d.ts +10 -0
- package/dist/__tests__/components/AppTopBarOnboarding.test.d.ts +1 -0
- package/dist/__tests__/components/FloatingPanelsInModal.test.d.ts +1 -0
- package/dist/__tests__/composables/useFloatingPanel.test.d.ts +1 -0
- package/dist/__tests__/composables/useOnboarding.test.d.ts +1 -0
- package/dist/__tests__/styles/paletteTokens.test.d.ts +1 -0
- package/dist/__tests__/styles/v2AuditRules.test.d.ts +1 -0
- package/dist/components/AppTopBar.vue.d.ts +20 -5
- package/dist/components/BaseButton.vue.d.ts +4 -2
- package/dist/components/ExperimentSelectorModal.vue.d.ts +15 -1
- package/dist/components/JobsStatusTray.vue.d.ts +15 -4
- package/dist/components/PluginWorkspaceView.props.d.ts +3 -0
- package/dist/components/PluginWorkspaceView.vue.d.ts +3 -0
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +3 -3
- package/dist/components/internal/AppTopBarOnboardingInternal.vue.d.ts +7 -0
- package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -1
- package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +2 -2
- package/dist/components/internal/OnboardingLayerInternal.vue.d.ts +7 -0
- package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +8 -0
- package/dist/composables/pluginWorkspaceModel.d.ts +2 -2
- package/dist/composables/useFileImport.d.ts +7 -0
- package/dist/composables/useFloatingPanel.d.ts +29 -0
- package/dist/composables/useJobCenter.d.ts +41 -3
- package/dist/composables/useOnboarding.d.ts +119 -0
- package/dist/{controlSchemaModel-B8CTXagP.js → controlSchemaModel-Bsg0t5Ab.js} +2 -2
- package/dist/{controlSchemaModel-B8CTXagP.js.map → controlSchemaModel-Bsg0t5Ab.js.map} +1 -1
- package/dist/{data-CgILxDLW.js → data-Boeh4uPI.js} +13 -12
- package/dist/{data-CgILxDLW.js.map → data-Boeh4uPI.js.map} +1 -1
- package/dist/data.d.ts +1 -1
- package/dist/data.js +9 -9
- package/dist/{experiment-43OJtH7R.js → experiment-DyO54FQB.js} +25 -61
- package/dist/experiment-DyO54FQB.js.map +1 -0
- package/dist/{experimentStatus-Cqp6qNrR.js → experimentStatus-DCn2QKG7.js} +2 -2
- package/dist/{experimentStatus-Cqp6qNrR.js.map → experimentStatus-DCn2QKG7.js.map} +1 -1
- package/dist/index.js +20 -20
- package/dist/{install-BtLWTk6H.js → install-BEHp7Mo-.js} +15 -15
- package/dist/{install-BtLWTk6H.js.map → install-BEHp7Mo-.js.map} +1 -1
- package/dist/install.js +1 -1
- package/dist/{lab-MHzp-_J8.js → lab-D61eaX6f.js} +68 -61
- package/dist/{lab-MHzp-_J8.js.map → lab-D61eaX6f.js.map} +1 -1
- package/dist/lab.js +4 -4
- package/dist/{records-B6gpM_3e.js → records-D4aQXyZu.js} +193 -133
- package/dist/records-D4aQXyZu.js.map +1 -0
- package/dist/{runtime-7vfl_bI3.js → runtime-q-R0CNLh.js} +400 -33
- package/dist/runtime-q-R0CNLh.js.map +1 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +7 -7
- package/dist/{shell-Cs9OJMnm.js → shell-6CGFvUUw.js} +10 -5
- package/dist/{shell-Cs9OJMnm.js.map → shell-6CGFvUUw.js.map} +1 -1
- package/dist/shell.d.ts +1 -0
- package/dist/shell.js +6 -6
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js +2 -2
- package/dist/stores/settings.d.ts +4 -12
- package/dist/styles.css +1191 -479
- package/dist/types/components.d.ts +2 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/jobs.d.ts +9 -0
- package/dist/types/platform.d.ts +1 -1
- package/dist/{useDropdownState-8yWMu-7_.js → useDropdownState-uFIz6Yv8.js} +16 -4
- package/dist/useDropdownState-uFIz6Yv8.js.map +1 -0
- package/dist/{useExperimentSamples-DHnxKFAF.js → useExperimentSamples-QEFoq4tf.js} +48 -6
- package/dist/useExperimentSamples-QEFoq4tf.js.map +1 -0
- package/dist/{useListReorder-BitdUhxn.js → useListReorder-DsuzwWGS.js} +122 -172
- package/dist/useListReorder-DsuzwWGS.js.map +1 -0
- package/dist/{useRackEditor-DiwCt77M.js → useRackEditor-KpZ4iJ6o.js} +22 -32
- package/dist/useRackEditor-KpZ4iJ6o.js.map +1 -0
- package/dist/useRealtime-B58Lo-Tm.js +508 -0
- package/dist/useRealtime-B58Lo-Tm.js.map +1 -0
- package/dist/{useRequest-D506eHi8.js → useRequest-BuDmW8Jc.js} +8 -37
- package/dist/useRequest-BuDmW8Jc.js.map +1 -0
- package/package.json +3 -2
- package/src/__stories__/experiment-helpers.ts +3 -3
- package/src/__tests__/components/ActionMenu.test.ts +2 -2
- package/src/__tests__/components/AppTopBar.test.ts +206 -1
- package/src/__tests__/components/AppTopBarOnboarding.test.ts +160 -0
- package/src/__tests__/components/AppTopBarPageSelector.test.ts +125 -9
- package/src/__tests__/components/BaseButton.test.ts +19 -0
- package/src/__tests__/components/BaseModal.test.ts +13 -0
- package/src/__tests__/components/ExperimentSelectorModal.test.ts +34 -1
- package/src/__tests__/components/FloatingPanelsInModal.test.ts +94 -0
- package/src/__tests__/components/GeneratedPluginView.test.ts +1 -1
- package/src/__tests__/components/JobsStatusTray.test.ts +373 -71
- package/src/__tests__/components/PluginWorkspaceView.test.ts +63 -1
- package/src/__tests__/components/SampleSelector.test.ts +20 -0
- package/src/__tests__/components/SettingsModal.test.ts +9 -2
- package/src/__tests__/composables/useFileImport.test.ts +36 -0
- package/src/__tests__/composables/useFloatingPanel.test.ts +68 -0
- package/src/__tests__/composables/useJobCenter.test.ts +262 -3
- package/src/__tests__/composables/useOnboarding.test.ts +317 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +4 -3
- package/src/__tests__/fixtures/table-import/cells.xlsx +0 -0
- package/src/__tests__/fixtures/table-import/samples.xlsx +0 -0
- package/src/__tests__/stores/settings.test.ts +21 -21
- package/src/__tests__/styles/cssTokens.test.ts +4 -1
- package/src/__tests__/styles/menusShellRefresh.test.ts +8 -2
- package/src/__tests__/styles/paletteTokens.test.ts +44 -0
- package/src/__tests__/styles/v2AuditRules.test.ts +104 -0
- package/src/components/ActionMenu.vue +6 -37
- package/src/components/AppTopBar.onboarding.story.vue +69 -0
- package/src/components/AppTopBar.story.vue +59 -4
- package/src/components/AppTopBar.vue +132 -10
- package/src/components/BaseButton.vue +16 -2
- package/src/components/BaseModal.vue +2 -1
- package/src/components/ExperimentSelectorModal.story.vue +38 -0
- package/src/components/ExperimentSelectorModal.vue +20 -1
- package/src/components/GeneratedPluginView.vue +4 -9
- package/src/components/GeneratedResultRenderer.vue +3 -4
- package/src/components/JobsStatusTray.story.vue +97 -43
- package/src/components/JobsStatusTray.vue +287 -290
- package/src/components/LayoutResizeHandle.story.vue +65 -4
- package/src/components/PluginWorkspaceView.props.ts +3 -0
- package/src/components/PluginWorkspaceView.vue +17 -0
- package/src/components/SampleSelector.story.vue +21 -6
- package/src/components/SampleSelector.vue +10 -3
- package/src/components/SectionCard.story.vue +33 -0
- package/src/components/SettingsModal.vue +7 -12
- package/src/components/Tooltip.vue +9 -32
- package/src/components/internal/AppTopBarOnboardingInternal.vue +172 -0
- package/src/components/internal/AppTopBarPageSelectorInternal.vue +140 -72
- package/src/components/internal/DatePickerDateInternal.vue +7 -45
- package/src/components/internal/DatePickerDateTimeInternal.vue +7 -1
- package/src/components/internal/DatePickerTimeInternal.vue +8 -1
- package/src/components/internal/NumberUnitInputInternal.vue +4 -18
- package/src/components/internal/OnboardingLayerInternal.vue +173 -0
- package/src/components/internal/SelectMultipleInternal.vue +6 -1
- package/src/components/internal/SelectSearchableInternal.vue +6 -1
- package/src/components/internal/WellEditPopupInternal.vue +11 -22
- package/src/components/internal/WorkspaceShellInternal.vue +12 -0
- package/src/composables/pluginWorkspaceModel.ts +2 -2
- package/src/composables/useFileImport.ts +51 -1
- package/src/composables/useFloatingPanel.ts +107 -0
- package/src/composables/useJobCenter.ts +188 -6
- package/src/composables/useOnboarding.ts +465 -0
- package/src/composables/usePluginWorkspace.ts +1 -1
- package/src/data.ts +1 -0
- package/src/runtime.ts +2 -1
- package/src/shell.ts +17 -0
- package/src/stores/index.ts +0 -2
- package/src/stores/settings.ts +20 -50
- package/src/styles/components/action-menu.css +1 -1
- package/src/styles/components/app-layout.css +6 -3
- package/src/styles/components/app-page-selector.css +120 -45
- package/src/styles/components/app-top-bar.css +165 -3
- package/src/styles/components/button.css +8 -0
- package/src/styles/components/dataframe.css +1 -1
- package/src/styles/components/date-picker.css +4 -2
- package/src/styles/components/datetime-picker.css +4 -4
- package/src/styles/components/experiment-selector-modal.css +13 -0
- package/src/styles/components/jobs-status-tray.css +134 -296
- package/src/styles/components/layout-resize-handle.css +59 -16
- package/src/styles/components/modal.css +3 -3
- package/src/styles/components/number-input.css +1 -1
- package/src/styles/components/onboarding.css +368 -0
- package/src/styles/components/progress-bar.css +3 -3
- package/src/styles/components/sample-selector.css +43 -0
- package/src/styles/components/searchable-select.css +3 -7
- package/src/styles/components/section-card.css +30 -6
- package/src/styles/components/settings-modal.css +7 -2
- package/src/styles/components/time-picker.css +3 -6
- package/src/styles/components/toast.css +1 -1
- package/src/styles/components/tooltip.css +2 -2
- package/src/styles/components/unit-input.css +1 -1
- package/src/styles/components/well-edit-popup.css +2 -2
- package/src/styles/index.css +1 -0
- package/src/styles/variables.css +153 -15
- package/src/types/components.ts +2 -0
- package/src/types/index.ts +1 -0
- package/src/types/jobs.ts +10 -0
- package/src/types/platform.ts +1 -1
- package/dist/AppSidebar-Dq9W1jE2.js.map +0 -1
- package/dist/ExperimentSelectorModal-65peG4Up.js.map +0 -1
- package/dist/SettingsModal-483nyI6b.js.map +0 -1
- package/dist/experiment-43OJtH7R.js.map +0 -1
- package/dist/records-B6gpM_3e.js.map +0 -1
- package/dist/runtime-7vfl_bI3.js.map +0 -1
- package/dist/useDropdownState-8yWMu-7_.js.map +0 -1
- package/dist/useExperimentSamples-DHnxKFAF.js.map +0 -1
- package/dist/useListReorder-BitdUhxn.js.map +0 -1
- package/dist/useRackEditor-DiwCt77M.js.map +0 -1
- package/dist/useRealtime-C3iy9QkU.js +0 -699
- package/dist/useRealtime-C3iy9QkU.js.map +0 -1
- package/dist/useRequest-D506eHi8.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
|
+
import { nextTick } from 'vue'
|
|
2
3
|
import { describe, expect, it } from 'vitest'
|
|
3
4
|
import AppTopBarPageSelectorInternal from '../../components/internal/AppTopBarPageSelectorInternal.vue'
|
|
4
5
|
|
|
@@ -24,26 +25,139 @@ describe('AppTopBarPageSelectorInternal', () => {
|
|
|
24
25
|
expect(wrapper.emitted('select')).toEqual([[{ id: 'Results', label: 'Results' }]])
|
|
25
26
|
})
|
|
26
27
|
|
|
27
|
-
it('
|
|
28
|
+
it('should show each page hint under its label when the page has one', async () => {
|
|
28
29
|
const wrapper = mount(AppTopBarPageSelectorInternal, {
|
|
29
30
|
props: {
|
|
30
31
|
currentPageId: 'downloads',
|
|
31
32
|
pages: [
|
|
32
33
|
{ id: 'downloads', label: 'Downloads', hint: 'Paste links and queue downloads' },
|
|
33
|
-
{ id: 'settings', label: 'Settings'
|
|
34
|
+
{ id: 'settings', label: 'Settings' },
|
|
34
35
|
],
|
|
35
36
|
},
|
|
36
37
|
})
|
|
37
38
|
|
|
38
39
|
await wrapper.get('.mint-page-selector__trigger').trigger('click')
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
'
|
|
41
|
+
const rows = wrapper.findAll('.mint-page-selector__item')
|
|
42
|
+
expect(rows.map(row => row.find('.mint-page-selector__item-hint').exists() ? row.get('.mint-page-selector__item-hint').text() : null)).toEqual([
|
|
43
|
+
'Paste links and queue downloads',
|
|
44
|
+
null,
|
|
43
45
|
])
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it.each([
|
|
49
|
+
{ pluginName: 'LEAF', crumb: ['LEAF', '/'], title: 'LEAF pages' },
|
|
50
|
+
{ pluginName: undefined, crumb: [], title: 'Pages' },
|
|
51
|
+
])('should name the menu after the plugin when pluginName is $pluginName', async ({ pluginName, crumb, title }) => {
|
|
52
|
+
const wrapper = mount(AppTopBarPageSelectorInternal, {
|
|
53
|
+
props: { pluginName, currentPageId: 'Extract', pages: ['Extract', 'Analysis'] },
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
expect(wrapper.findAll('.mint-page-selector__plugin, .mint-page-selector__slash').map(el => el.text())).toEqual(crumb)
|
|
57
|
+
|
|
58
|
+
await wrapper.get('.mint-page-selector__trigger').trigger('click')
|
|
59
|
+
|
|
60
|
+
expect(wrapper.get('.mint-page-selector__menu-title').text()).toBe(title)
|
|
61
|
+
expect(wrapper.get('[role="menu"]').attributes('aria-label')).toBe(title)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should show number keys for the first nine pages only', async () => {
|
|
65
|
+
const pages = Array.from({ length: 10 }, (_, i) => `Page ${i + 1}`)
|
|
66
|
+
const wrapper = mount(AppTopBarPageSelectorInternal, { props: { pages } })
|
|
67
|
+
|
|
68
|
+
await wrapper.get('.mint-page-selector__trigger').trigger('click')
|
|
69
|
+
|
|
70
|
+
expect(wrapper.findAll('.mint-page-selector__item').map(row => row.find('.mint-page-selector__item-key').exists() ? row.get('.mint-page-selector__item-key').text() : null)).toEqual(
|
|
71
|
+
['1', '2', '3', '4', '5', '6', '7', '8', '9', null],
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('keyboard', () => {
|
|
76
|
+
const pages = [
|
|
77
|
+
{ id: 'check', label: 'Quick check' },
|
|
78
|
+
{ id: 'extract', label: 'Extract' },
|
|
79
|
+
{ id: 'locked', label: 'Locked', disabled: true },
|
|
80
|
+
{ id: 'analysis', label: 'Analysis' },
|
|
81
|
+
]
|
|
82
|
+
const focusedLabel = () => document.activeElement?.querySelector('.mint-page-selector__item-label')?.textContent
|
|
83
|
+
|
|
84
|
+
function mountAttached() {
|
|
85
|
+
return mount(AppTopBarPageSelectorInternal, {
|
|
86
|
+
props: { pages, currentPageId: 'extract' },
|
|
87
|
+
attachTo: document.body,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
it.each(['ArrowDown', 'Enter', ' '])('should open the menu on the current row when %j is pressed on the trigger', async (key) => {
|
|
92
|
+
const wrapper = mountAttached()
|
|
93
|
+
|
|
94
|
+
await wrapper.get('.mint-page-selector__trigger').trigger('keydown', { key })
|
|
95
|
+
await nextTick()
|
|
96
|
+
|
|
97
|
+
expect(wrapper.find('.mint-page-selector__menu').exists()).toBe(true)
|
|
98
|
+
expect(focusedLabel()).toBe('Extract')
|
|
99
|
+
wrapper.unmount()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should move between enabled rows with arrows, Home and End', async () => {
|
|
103
|
+
const wrapper = mountAttached()
|
|
104
|
+
await wrapper.get('.mint-page-selector__trigger').trigger('keydown', { key: 'ArrowDown' })
|
|
105
|
+
await nextTick()
|
|
106
|
+
const menu = wrapper.get('.mint-page-selector__menu')
|
|
107
|
+
|
|
108
|
+
const visited: Array<string | null | undefined> = []
|
|
109
|
+
for (const key of ['ArrowDown', 'ArrowDown', 'ArrowUp', 'Home', 'ArrowUp', 'End']) {
|
|
110
|
+
await menu.trigger('keydown', { key })
|
|
111
|
+
visited.push(focusedLabel())
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
expect(visited).toEqual(['Analysis', 'Quick check', 'Analysis', 'Quick check', 'Analysis', 'Analysis'])
|
|
115
|
+
wrapper.unmount()
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it.each(['Enter', ' '])('should activate the focused row and refocus the trigger when %j is pressed', async (key) => {
|
|
119
|
+
const wrapper = mountAttached()
|
|
120
|
+
await wrapper.get('.mint-page-selector__trigger').trigger('keydown', { key: 'ArrowDown' })
|
|
121
|
+
await nextTick()
|
|
122
|
+
const menu = wrapper.get('.mint-page-selector__menu')
|
|
123
|
+
|
|
124
|
+
await menu.trigger('keydown', { key: 'ArrowDown' })
|
|
125
|
+
await menu.trigger('keydown', { key })
|
|
126
|
+
|
|
127
|
+
expect(wrapper.emitted('select')).toEqual([[pages[3]]])
|
|
128
|
+
expect(wrapper.find('.mint-page-selector__menu').exists()).toBe(false)
|
|
129
|
+
expect(document.activeElement).toBe(wrapper.get('.mint-page-selector__trigger').element)
|
|
130
|
+
wrapper.unmount()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it.each([
|
|
134
|
+
{ key: '4', emitted: [[pages[3]]], open: false },
|
|
135
|
+
{ key: '3', emitted: undefined, open: true },
|
|
136
|
+
{ key: '9', emitted: undefined, open: true },
|
|
137
|
+
])('should handle digit $key as a jump to that page', async ({ key, emitted, open }) => {
|
|
138
|
+
const wrapper = mountAttached()
|
|
139
|
+
await wrapper.get('.mint-page-selector__trigger').trigger('keydown', { key: 'ArrowDown' })
|
|
140
|
+
await nextTick()
|
|
141
|
+
|
|
142
|
+
await wrapper.get('.mint-page-selector__menu').trigger('keydown', { key })
|
|
143
|
+
|
|
144
|
+
expect(wrapper.emitted('select')).toEqual(emitted)
|
|
145
|
+
expect(wrapper.find('.mint-page-selector__menu').exists()).toBe(open)
|
|
146
|
+
wrapper.unmount()
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('should close on Escape and return focus to the trigger', async () => {
|
|
150
|
+
const wrapper = mountAttached()
|
|
151
|
+
await wrapper.get('.mint-page-selector__trigger').trigger('keydown', { key: 'ArrowDown' })
|
|
152
|
+
await nextTick()
|
|
153
|
+
|
|
154
|
+
await wrapper.get('.mint-page-selector__menu').trigger('keydown', { key: 'Escape' })
|
|
155
|
+
await nextTick()
|
|
156
|
+
|
|
157
|
+
expect(wrapper.find('.mint-page-selector__menu').exists()).toBe(false)
|
|
158
|
+
expect(document.activeElement).toBe(wrapper.get('.mint-page-selector__trigger').element)
|
|
159
|
+
wrapper.unmount()
|
|
160
|
+
})
|
|
47
161
|
})
|
|
48
162
|
|
|
49
163
|
it('closes linked page actions without emitting select', async () => {
|
|
@@ -171,6 +285,8 @@ describe('AppTopBarPageSelectorInternal', () => {
|
|
|
171
285
|
|
|
172
286
|
await wrapper.get('.mint-page-selector__trigger').trigger('click')
|
|
173
287
|
|
|
174
|
-
|
|
288
|
+
const rows = wrapper.findAll('.mint-page-selector__item')
|
|
289
|
+
expect(rows[0].classes()).toContain('mint-page-selector__item--active')
|
|
290
|
+
expect(rows.map(row => row.attributes('aria-current'))).toEqual(['page', undefined])
|
|
175
291
|
})
|
|
176
292
|
})
|
|
@@ -35,6 +35,25 @@ describe('BaseButton icon', () => {
|
|
|
35
35
|
})
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
+
// Text-only labels get a span so the stylesheet can trim the text box to the cap
|
|
39
|
+
// height (Geist sits ~1.6px high in a flex-centred button otherwise). Element
|
|
40
|
+
// children keep their flex layout, so they are not wrapped.
|
|
41
|
+
describe('BaseButton label', () => {
|
|
42
|
+
it.each([
|
|
43
|
+
['plain text', 'Cancel', 'Cancel'],
|
|
44
|
+
['element children', '<svg /><span>Export</span>', null],
|
|
45
|
+
])('should wrap the label in a span only for a %s slot', (_case, slot, label) => {
|
|
46
|
+
const wrapper = mount(BaseButton, { slots: { default: slot } })
|
|
47
|
+
const span = wrapper.find('.mint-button__label')
|
|
48
|
+
expect(span.exists() ? span.text() : null).toBe(label)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should trim the label text box to the cap height', () => {
|
|
52
|
+
const css = readFileSync(resolve(__dirname, '../../styles/components/button.css'), 'utf8')
|
|
53
|
+
expect(css).toMatch(/\.mint-button__label \{\s*text-box: trim-both cap alphabetic;\s*\}/)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
38
57
|
// jsdom has no layout, so this pins the stylesheet contract instead: a bare
|
|
39
58
|
// <svg> rendered by an icon-only BaseButton must be sized by the icon box, otherwise it
|
|
40
59
|
// collapses to zero width as a flex item (FilePicker source toggles, beta.14).
|
|
@@ -196,6 +196,19 @@ describe('BaseModal', () => {
|
|
|
196
196
|
inner.unmount()
|
|
197
197
|
})
|
|
198
198
|
|
|
199
|
+
it('should stay open when a control inside it already handled the Escape', () => {
|
|
200
|
+
const wrapper = mount(BaseModal, {
|
|
201
|
+
props: { open: true, title: 'Settings' },
|
|
202
|
+
slots: { default: '<input class="handled" @keydown.escape.prevent>' },
|
|
203
|
+
attachTo: document.body,
|
|
204
|
+
})
|
|
205
|
+
document.querySelector('.handled')!.dispatchEvent(
|
|
206
|
+
new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true }),
|
|
207
|
+
)
|
|
208
|
+
expect(wrapper.emitted('update:open')).toBeUndefined()
|
|
209
|
+
wrapper.unmount()
|
|
210
|
+
})
|
|
211
|
+
|
|
199
212
|
it('should focus the first focusable element when mounted already open', async () => {
|
|
200
213
|
const attached = mount(BaseModal, {
|
|
201
214
|
props: { open: true, title: 'Settings' },
|
|
@@ -64,9 +64,10 @@ interface ModalProps {
|
|
|
64
64
|
currentExperimentId?: number | null
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
function mountModal(props: ModalProps) {
|
|
67
|
+
function mountModal(props: ModalProps, slots: Record<string, string> = {}) {
|
|
68
68
|
return mount(ExperimentSelectorModal, {
|
|
69
69
|
props,
|
|
70
|
+
slots,
|
|
70
71
|
global: {
|
|
71
72
|
stubs: {
|
|
72
73
|
BaseModal: {
|
|
@@ -202,6 +203,38 @@ describe('ExperimentSelectorModal', () => {
|
|
|
202
203
|
expect(wrapper.emitted('update:open')).toEqual([[false]])
|
|
203
204
|
})
|
|
204
205
|
|
|
206
|
+
it('should show the description slot above the experiment list', () => {
|
|
207
|
+
const wrapper = mountModal({ open: true }, { description: '<p class="why">Keep the result with its samples.</p>' })
|
|
208
|
+
|
|
209
|
+
const description = wrapper.get('.mint-experiment-selector__description')
|
|
210
|
+
expect(description.find('.why').text()).toBe('Keep the result with its samples.')
|
|
211
|
+
expect(description.element.compareDocumentPosition(wrapper.get('.mint-experiment-selector').element))
|
|
212
|
+
.toBe(Node.DOCUMENT_POSITION_FOLLOWING)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it.each([
|
|
216
|
+
[null, false],
|
|
217
|
+
[7, true],
|
|
218
|
+
])('should render the footer slot when the current experiment is %s, with the clear button %s', (currentExperimentId, hasClear) => {
|
|
219
|
+
const wrapper = mountModal({ open: true, currentExperimentId }, { footer: '<span class="extra">Skip</span>' })
|
|
220
|
+
|
|
221
|
+
const footer = wrapper.get('.mint-experiment-selector__footer')
|
|
222
|
+
expect(footer.find('.extra').exists()).toBe(true)
|
|
223
|
+
expect(footer.find('.mint-experiment-selector__clear-btn').exists()).toBe(hasClear)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('should close without touching the store from the footer slot close()', async () => {
|
|
227
|
+
const store = useExperimentStore()
|
|
228
|
+
const wrapper = mountModal({ open: true }, {
|
|
229
|
+
footer: '<template #footer="{ close }"><button class="skip" @click="close">Skip</button></template>',
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
await wrapper.get('.skip').trigger('click')
|
|
233
|
+
|
|
234
|
+
expect(wrapper.emitted('update:open')).toEqual([[false]])
|
|
235
|
+
expect(store.current).toBeNull()
|
|
236
|
+
})
|
|
237
|
+
|
|
205
238
|
it('should list each status with its count and check the current one', async () => {
|
|
206
239
|
statusCounts.value = { ongoing: 3, planned: 1, completed: 0, cancelled: 2 }
|
|
207
240
|
filters.status = 'ongoing'
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import { defineComponent, nextTick } from 'vue'
|
|
3
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
4
|
+
import BaseModal from '../../components/BaseModal.vue'
|
|
5
|
+
import BaseSelect from '../../components/BaseSelect.vue'
|
|
6
|
+
import DatePicker from '../../components/DatePicker.vue'
|
|
7
|
+
import WellEditPopupInternal from '../../components/internal/WellEditPopupInternal.vue'
|
|
8
|
+
|
|
9
|
+
const options = ['Orbitrap Exploris 480', 'Q Exactive HF', 'Vanquish UHPLC', 'Exploris 240', 'timsTOF Pro 2']
|
|
10
|
+
|
|
11
|
+
// [name, control markup, component root, trigger selector, panel selector]
|
|
12
|
+
const movedPanels = [
|
|
13
|
+
['multiple select', '<BaseSelect multiple :model-value="[]" :options="options" />', '.mint-multi-select', '.mint-multi-select__field', '.mint-searchable-select__panel'],
|
|
14
|
+
['searchable select', '<BaseSelect searchable :options="options" />', '.mint-searchable-select', '.mint-searchable-select__trigger', '.mint-searchable-select__panel'],
|
|
15
|
+
['date-time picker', '<DatePicker type="datetime" />', '.mint-datetime-picker', '.mint-datetime-picker__input', '.mint-datetime-picker__dropdown'],
|
|
16
|
+
['time picker', '<DatePicker type="time" />', '.mint-time-picker', '.mint-time-picker__input', '.mint-time-picker__dropdown'],
|
|
17
|
+
] as const
|
|
18
|
+
|
|
19
|
+
async function settle() {
|
|
20
|
+
for (let i = 0; i < 3; i++) await nextTick()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('floating panels inside BaseModal', () => {
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
document.body.innerHTML = ''
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it.each(movedPanels)(
|
|
29
|
+
'should keep the %s panel in its root after the trigger and position it fixed, so the modal body cannot clip it',
|
|
30
|
+
async (_, control, rootSelector, triggerSelector, panelSelector) => {
|
|
31
|
+
mount(defineComponent({
|
|
32
|
+
components: { BaseModal, BaseSelect, DatePicker },
|
|
33
|
+
setup: () => ({ options }),
|
|
34
|
+
template: `<BaseModal open title="New service token">${control}</BaseModal>`,
|
|
35
|
+
}), { attachTo: document.body })
|
|
36
|
+
await settle()
|
|
37
|
+
|
|
38
|
+
document.querySelector<HTMLElement>(triggerSelector)!.click()
|
|
39
|
+
await settle()
|
|
40
|
+
|
|
41
|
+
const panel = document.querySelector<HTMLElement>(panelSelector)!
|
|
42
|
+
const root = document.querySelector(rootSelector)!
|
|
43
|
+
expect(root.contains(panel)).toBe(true)
|
|
44
|
+
expect(root.querySelector(triggerSelector)!.compareDocumentPosition(panel) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
|
|
45
|
+
expect(panel.style.position).toBe('fixed')
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('Escape in a calendar panel inside BaseModal', () => {
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
document.body.innerHTML = ''
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it.each([
|
|
56
|
+
['date', '.mint-date-picker__input', '.mint-date-picker__dropdown'],
|
|
57
|
+
['datetime', '.mint-datetime-picker__input', '.mint-datetime-picker__dropdown'],
|
|
58
|
+
])('should close only the %s panel when Escape is pressed in it', async (type, triggerSelector, panelSelector) => {
|
|
59
|
+
const wrapper = mount(defineComponent({
|
|
60
|
+
components: { BaseModal, DatePicker },
|
|
61
|
+
emits: ['close'],
|
|
62
|
+
template: `<BaseModal open title="Edit experiment" @update:open="$emit('close')"><DatePicker type="${type}" /></BaseModal>`,
|
|
63
|
+
}), { attachTo: document.body })
|
|
64
|
+
await settle()
|
|
65
|
+
document.querySelector<HTMLElement>(triggerSelector)!.click()
|
|
66
|
+
await settle()
|
|
67
|
+
|
|
68
|
+
document.querySelector(panelSelector)!.querySelector('button')!.dispatchEvent(
|
|
69
|
+
new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true }),
|
|
70
|
+
)
|
|
71
|
+
await settle()
|
|
72
|
+
|
|
73
|
+
expect(document.querySelector(panelSelector)).toBeNull()
|
|
74
|
+
expect(wrapper.emitted('close')).toBeUndefined()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should close only the well popup when Escape is pressed in it', async () => {
|
|
78
|
+
const wrapper = mount(defineComponent({
|
|
79
|
+
components: { BaseModal, WellEditPopupInternal },
|
|
80
|
+
emits: ['close', 'popupClose'],
|
|
81
|
+
template: `<BaseModal open title="Plate" @update:open="$emit('close')">
|
|
82
|
+
<WellEditPopupInternal well-id="A1" :position="{ x: 40, y: 40 }" @close="$emit('popupClose')" />
|
|
83
|
+
</BaseModal>`,
|
|
84
|
+
}), { attachTo: document.body })
|
|
85
|
+
await settle()
|
|
86
|
+
|
|
87
|
+
document.querySelector('.mint-well-edit-popup input')!.dispatchEvent(
|
|
88
|
+
new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true }),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
expect(wrapper.emitted('popupClose')).toHaveLength(1)
|
|
92
|
+
expect(wrapper.emitted('close')).toBeUndefined()
|
|
93
|
+
})
|
|
94
|
+
})
|
|
@@ -147,7 +147,7 @@ describe('GeneratedPluginView', () => {
|
|
|
147
147
|
workspace: wrapper.findComponent(PluginWorkspaceView).props(),
|
|
148
148
|
result: wrapper.findComponent(GeneratedResultRenderer).text(),
|
|
149
149
|
jobs: wrapper.findComponent(JobsStatusTray).exists(),
|
|
150
|
-
jobSource: wrapper.findComponent(
|
|
150
|
+
jobSource: wrapper.findComponent(PluginWorkspaceView).props('spec')?.jobs,
|
|
151
151
|
}).toMatchObject({
|
|
152
152
|
workspace: {
|
|
153
153
|
spec: {
|