fix(gui-v2): :key on RightDrawer dynamic body to remount on switch
Adds :key="component" to <component :is="Body"> so opening drawer B while A is open fully remounts (A's instance/state can't leak into B) — a real defect for a primary shell overlay. Same-name reopen still relies on the body reacting to prop changes (documented inline). Companion test asserts the cross-component switch swaps the body cleanly (A unmounts, B mounts). Addresses the Task 5 code-review Important finding. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -141,9 +141,17 @@ const Body = computed(() => resolveDrawerComponent(component.value))
|
||||
a minimal empty state — this must NOT crash on null (resolveDrawerComponent
|
||||
guarantees null for unknown names).
|
||||
-->
|
||||
<!--
|
||||
:key on the drawer-body name forces a full remount when the open
|
||||
drawer switches from one component to another (open A → open B),
|
||||
so component A's internal state never leaks into component B.
|
||||
Same-name reopen relies on the body reacting to prop changes
|
||||
(normal Vue); revisit if a real body ever needs a hard reset there.
|
||||
-->
|
||||
<component
|
||||
:is="Body"
|
||||
v-if="Body !== null"
|
||||
:key="component ?? ''"
|
||||
v-bind="bodyProps"
|
||||
/>
|
||||
|
||||
|
||||
@@ -226,4 +226,35 @@ describe('RightDrawer', () => {
|
||||
// Graceful-empty-state text must NOT appear when a component is rendered
|
||||
expect(wrapper.text()).not.toContain('Geen inhoud')
|
||||
})
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 7. Switching the open drawer component swaps the body cleanly
|
||||
// (the :key="component" remount guard — opening B while A is open
|
||||
// unmounts A and mounts B; A's DOM/state must not linger).
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
it('remounts the body when the open drawer switches to another component', async () => {
|
||||
const BodyA = defineComponent({ template: '<div class="body-a">A</div>' })
|
||||
const BodyB = defineComponent({ template: '<div class="body-b">B</div>' })
|
||||
const nameA = `${PREFIX}BodyA`
|
||||
const nameB = `${PREFIX}BodyB`
|
||||
|
||||
registerDrawerComponent(nameA, BodyA)
|
||||
registerDrawerComponent(nameB, BodyB)
|
||||
|
||||
const wrapper = mountDrawer()
|
||||
const shell = useShellUiStore()
|
||||
|
||||
shell.openDrawer(nameA, { title: 'A' })
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('.body-a').exists()).toBe(true)
|
||||
expect(wrapper.find('.body-b').exists()).toBe(false)
|
||||
|
||||
// Switch to B while the drawer is still open — :key change forces a
|
||||
// full remount so A is gone and B is mounted (no stale A instance).
|
||||
shell.openDrawer(nameB, { title: 'B' })
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('.body-b').exists()).toBe(true)
|
||||
expect(wrapper.find('.body-a').exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user