Skip to content

Commit 23ba7e6

Browse files
[Manager] Fix version selector popover not closing when selecting different pack (#4176)
1 parent 1e2b16f commit 23ba7e6

File tree

6 files changed

+182
-13
lines changed

6 files changed

+182
-13
lines changed

src/components/dialog/content/manager/PackVersionBadge.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ describe('PackVersionBadge', () => {
6262
return mount(PackVersionBadge, {
6363
props: {
6464
nodePack: mockNodePack,
65+
isSelected: false,
6566
...props
6667
},
6768
global: {
@@ -162,4 +163,58 @@ describe('PackVersionBadge', () => {
162163
// Verify that the hide method was called
163164
expect(mockHide).toHaveBeenCalled()
164165
})
166+
167+
describe('selection state changes', () => {
168+
it('closes the popover when card is deselected', async () => {
169+
const wrapper = mountComponent({
170+
props: { isSelected: true }
171+
})
172+
173+
// Change isSelected from true to false
174+
await wrapper.setProps({ isSelected: false })
175+
await nextTick()
176+
177+
// Verify that the hide method was called
178+
expect(mockHide).toHaveBeenCalled()
179+
})
180+
181+
it('does not close the popover when card is selected', async () => {
182+
const wrapper = mountComponent({
183+
props: { isSelected: false }
184+
})
185+
186+
// Change isSelected from false to true
187+
await wrapper.setProps({ isSelected: true })
188+
await nextTick()
189+
190+
// Verify that the hide method was NOT called
191+
expect(mockHide).not.toHaveBeenCalled()
192+
})
193+
194+
it('does not close the popover when isSelected remains false', async () => {
195+
const wrapper = mountComponent({
196+
props: { isSelected: false }
197+
})
198+
199+
// Change isSelected from false to false (no change)
200+
await wrapper.setProps({ isSelected: false })
201+
await nextTick()
202+
203+
// Verify that the hide method was NOT called
204+
expect(mockHide).not.toHaveBeenCalled()
205+
})
206+
207+
it('does not close the popover when isSelected remains true', async () => {
208+
const wrapper = mountComponent({
209+
props: { isSelected: true }
210+
})
211+
212+
// Change isSelected from true to true (no change)
213+
await wrapper.setProps({ isSelected: true })
214+
await nextTick()
215+
216+
// Verify that the hide method was NOT called
217+
expect(mockHide).not.toHaveBeenCalled()
218+
})
219+
})
165220
})

src/components/dialog/content/manager/PackVersionBadge.vue

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
<script setup lang="ts">
3434
import Button from 'primevue/button'
3535
import Popover from 'primevue/popover'
36-
import { computed, ref } from 'vue'
36+
import { computed, ref, watch } from 'vue'
3737
3838
import PackVersionSelectorPopover from '@/components/dialog/content/manager/PackVersionSelectorPopover.vue'
3939
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
@@ -43,8 +43,9 @@ import { isSemVer } from '@/utils/formatUtil'
4343
4444
const TRUNCATED_HASH_LENGTH = 7
4545
46-
const { nodePack } = defineProps<{
46+
const { nodePack, isSelected } = defineProps<{
4747
nodePack: components['schemas']['Node']
48+
isSelected: boolean
4849
}>()
4950
5051
const popoverRef = ref()
@@ -69,4 +70,14 @@ const toggleVersionSelector = (event: Event) => {
6970
const closeVersionSelector = () => {
7071
popoverRef.value.hide()
7172
}
73+
74+
// If the card is unselected, automatically close the version selector popover
75+
watch(
76+
() => isSelected,
77+
(isSelected, wasSelected) => {
78+
if (wasSelected && !isSelected) {
79+
closeVersionSelector()
80+
}
81+
}
82+
)
7283
</script>

src/components/dialog/content/manager/PackVersionSelectorPopover.test.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,100 @@ describe('PackVersionSelectorPopover', () => {
191191
expect(mockGetPackVersions).toHaveBeenCalledWith(newNodePack.id)
192192
})
193193

194+
describe('nodePack.id changes', () => {
195+
it('re-fetches versions when nodePack.id changes', async () => {
196+
// Set up the mock for the initial fetch
197+
mockGetPackVersions.mockResolvedValueOnce(defaultMockVersions)
198+
199+
const wrapper = mountComponent()
200+
await waitForPromises()
201+
202+
// Verify initial fetch
203+
expect(mockGetPackVersions).toHaveBeenCalledTimes(1)
204+
expect(mockGetPackVersions).toHaveBeenCalledWith(mockNodePack.id)
205+
206+
// Set up the mock for the second fetch
207+
const newVersions = [
208+
{ version: '2.0.0', createdAt: '2023-06-01' },
209+
{ version: '1.9.0', createdAt: '2023-05-01' }
210+
]
211+
mockGetPackVersions.mockResolvedValueOnce(newVersions)
212+
213+
// Update the nodePack with a new ID
214+
const newNodePack = {
215+
...mockNodePack,
216+
id: 'different-pack',
217+
name: 'Different Pack'
218+
}
219+
await wrapper.setProps({ nodePack: newNodePack })
220+
await waitForPromises()
221+
222+
// Should fetch versions for the new nodePack
223+
expect(mockGetPackVersions).toHaveBeenCalledTimes(2)
224+
expect(mockGetPackVersions).toHaveBeenLastCalledWith(newNodePack.id)
225+
226+
// Check that new versions are displayed
227+
const listbox = wrapper.findComponent(Listbox)
228+
const options = listbox.props('options')!
229+
expect(options.some((o) => o.value === '2.0.0')).toBe(true)
230+
expect(options.some((o) => o.value === '1.9.0')).toBe(true)
231+
})
232+
233+
it('does not re-fetch when nodePack changes but id remains the same', async () => {
234+
// Set up the mock for the initial fetch
235+
mockGetPackVersions.mockResolvedValueOnce(defaultMockVersions)
236+
237+
const wrapper = mountComponent()
238+
await waitForPromises()
239+
240+
// Verify initial fetch
241+
expect(mockGetPackVersions).toHaveBeenCalledTimes(1)
242+
243+
// Update the nodePack with same ID but different properties
244+
const updatedNodePack = {
245+
...mockNodePack,
246+
name: 'Updated Test Pack',
247+
description: 'New description'
248+
}
249+
await wrapper.setProps({ nodePack: updatedNodePack })
250+
await waitForPromises()
251+
252+
// Should NOT fetch versions again
253+
expect(mockGetPackVersions).toHaveBeenCalledTimes(1)
254+
})
255+
256+
it('maintains selected version when switching to a new pack', async () => {
257+
// Set up the mock for the initial fetch
258+
mockGetPackVersions.mockResolvedValueOnce(defaultMockVersions)
259+
260+
const wrapper = mountComponent()
261+
await waitForPromises()
262+
263+
// Select a specific version
264+
const listbox = wrapper.findComponent(Listbox)
265+
await listbox.setValue('0.9.0')
266+
expect(listbox.props('modelValue')).toBe('0.9.0')
267+
268+
// Set up the mock for the second fetch
269+
mockGetPackVersions.mockResolvedValueOnce([
270+
{ version: '3.0.0', createdAt: '2023-07-01' },
271+
{ version: '0.9.0', createdAt: '2023-04-01' }
272+
])
273+
274+
// Update to a new pack that also has version 0.9.0
275+
const newNodePack = {
276+
id: 'another-pack',
277+
name: 'Another Pack',
278+
latest_version: { version: '3.0.0' }
279+
}
280+
await wrapper.setProps({ nodePack: newNodePack })
281+
await waitForPromises()
282+
283+
// Selected version should remain the same if available
284+
expect(listbox.props('modelValue')).toBe('0.9.0')
285+
})
286+
})
287+
194288
describe('Unclaimed GitHub packs handling', () => {
195289
it('falls back to nightly when no versions exist', async () => {
196290
// Set up the mock to return versions

src/components/dialog/content/manager/PackVersionSelectorPopover.vue

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ import { whenever } from '@vueuse/core'
6262
import Button from 'primevue/button'
6363
import Listbox from 'primevue/listbox'
6464
import ProgressSpinner from 'primevue/progressspinner'
65-
import { onMounted, onUnmounted, ref } from 'vue'
65+
import { onMounted, ref } from 'vue'
6666
import { useI18n } from 'vue-i18n'
6767
6868
import ContentDivider from '@/components/common/ContentDivider.vue'
@@ -161,9 +161,11 @@ const onNodePackChange = async () => {
161161
}
162162
163163
whenever(
164-
() => nodePack,
165-
() => {
166-
void onNodePackChange()
164+
() => nodePack.id,
165+
(nodePackId, oldNodePackId) => {
166+
if (nodePackId !== oldNodePackId) {
167+
void onNodePackChange()
168+
}
167169
},
168170
{ deep: true, immediate: true }
169171
)
@@ -182,8 +184,4 @@ const handleSubmit = async () => {
182184
isQueueing.value = false
183185
emit('submit')
184186
}
185-
186-
onUnmounted(() => {
187-
managerStore.installPack.clear()
188-
})
189187
</script>

src/components/dialog/content/manager/infoPanel/InfoPanel.vue

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
/>
3333
</MetadataRow>
3434
<MetadataRow :label="t('manager.version')">
35-
<PackVersionBadge :node-pack="nodePack" />
35+
<PackVersionBadge :node-pack="nodePack" :is-selected="true" />
3636
</MetadataRow>
3737
</div>
3838
<div class="mb-6 overflow-hidden">
@@ -118,7 +118,15 @@ const onNodePackChange = () => {
118118
y.value = 0
119119
}
120120
121-
whenever(() => nodePack, onNodePackChange, { immediate: true, deep: true })
121+
whenever(
122+
() => nodePack.id,
123+
(nodePackId, oldNodePackId) => {
124+
if (nodePackId !== oldNodePackId) {
125+
onNodePackChange()
126+
}
127+
},
128+
{ immediate: true }
129+
)
122130
</script>
123131
<style scoped>
124132
.hidden-scrollbar {

src/components/dialog/content/manager/packCard/PackCard.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@
7070
>
7171
<i class="pi pi-arrow-circle-up text-blue-600" />
7272
</div>
73-
<PackVersionBadge :node-pack="nodePack" />
73+
<PackVersionBadge
74+
:node-pack="nodePack"
75+
:is-selected="isSelected"
76+
/>
7477
</div>
7578
<div
7679
v-if="formattedLatestVersionDate"

0 commit comments

Comments
 (0)