Skip to content

Commit 9ac929a

Browse files
authored
fix(transition-group): handle v-if dynamic slots (#14628)
1 parent c615e05 commit 9ac929a

3 files changed

Lines changed: 97 additions & 17 deletions

File tree

packages-private/vapor-e2e-test/__tests__/transition-group.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,59 @@ describe('vapor transition-group', () => {
265265
E2E_TIMEOUT,
266266
)
267267

268+
test(
269+
'dynamic slot with v-if',
270+
async () => {
271+
const btnSelector = '.dynamic-slot-with-v-if > button.toggle'
272+
const addBtnSelector = '.dynamic-slot-with-v-if > button.add'
273+
const containerSelector = '.dynamic-slot-with-v-if > div'
274+
expect(await html(containerSelector)).toBe(`<ul></ul>`)
275+
276+
expect(
277+
(await transitionStart(btnSelector, containerSelector)).innerHTML,
278+
).toBe(
279+
`<ul>` +
280+
`<li class="test v-enter-from v-enter-active">0</li>` +
281+
`<li class="test v-enter-from v-enter-active">1</li>` +
282+
`</ul>`,
283+
)
284+
285+
await waitForInnerHTML(
286+
containerSelector,
287+
`<ul>` +
288+
`<li class="test v-enter-active v-enter-to">0</li>` +
289+
`<li class="test v-enter-active v-enter-to">1</li>` +
290+
`</ul>`,
291+
)
292+
293+
await waitForInnerHTML(
294+
containerSelector,
295+
`<ul><li class="test">0</li><li class="test">1</li></ul>`,
296+
)
297+
298+
// add a new item
299+
expect(
300+
(await transitionStart(addBtnSelector, containerSelector)).innerHTML,
301+
).toBe(
302+
`<ul>` +
303+
`<li class="test">0</li>` +
304+
`<li class="test">1</li>` +
305+
`<li class="test v-enter-from v-enter-active">2</li>` +
306+
`</ul>`,
307+
)
308+
309+
await waitForInnerHTML(
310+
containerSelector,
311+
`<ul>` +
312+
`<li class="test">0</li>` +
313+
`<li class="test">1</li>` +
314+
`<li class="test">2</li>` +
315+
`</ul>`,
316+
)
317+
},
318+
E2E_TIMEOUT,
319+
)
320+
268321
test(
269322
'leave',
270323
async () => {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup vapor>
2+
import { ref } from 'vue'
3+
4+
const show = ref(false)
5+
const list = ref([0, 1])
6+
</script>
7+
8+
<template>
9+
<div class="dynamic-slot-with-v-if">
10+
<button class="toggle" @click="show = !show">toggle button</button>
11+
<button class="add" @click="list.push(list.length)">add button</button>
12+
13+
<div>
14+
<TransitionGroup tag="ul">
15+
<template v-if="show" #default>
16+
<li class="test" v-for="value in list" :key="value">{{ value }}</li>
17+
</template>
18+
</TransitionGroup>
19+
</div>
20+
</div>
21+
</template>

packages/runtime-vapor/src/components/TransitionGroup.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ import {
1717
warn,
1818
} from '@vue/runtime-dom'
1919
import { extend, isArray } from '@vue/shared'
20-
import { type Block, type TransitionBlock, insert } from '../block'
20+
import {
21+
type Block,
22+
type BlockFn,
23+
type TransitionBlock,
24+
insert,
25+
} from '../block'
2126
import { renderEffect } from '../renderEffect'
2227
import {
2328
type ResolvedTransitionBlock,
@@ -140,29 +145,30 @@ const VaporTransitionGroupImpl = defineVaporComponent({
140145

141146
const frag = new DynamicFragment('transition-group')
142147
let currentTag: string | undefined
148+
let currentSlot: BlockFn | undefined
143149
let isMounted = false
150+
144151
renderEffect(() => {
145152
const tag = props.tag
146-
// tag is not changed, do nothing
147-
if (isMounted && tag === currentTag) return
153+
const slot = slots.default
154+
// if the tag and slot are the same as previous render, no need to update.
155+
if (isMounted && tag === currentTag && slot === currentSlot) return
148156

157+
const container = tag ? createElement(tag) : undefined
149158
let block: Block = slottedBlock
150-
frag.update(
151-
() => {
152-
block = (slots.default && slots.default()) || []
153-
applyGroupTransitionHooks(block, propsProxy, state, instance)
154-
if (tag) {
155-
const container = createElement(tag)
156-
insert(block, container)
157-
return container
158-
}
159-
return block
160-
},
161-
// Avoid `undefined` falling back to the render function as the key.
162-
tag ?? null,
163-
)
159+
frag.update(() => {
160+
block = (slot && slot()) || []
161+
applyGroupTransitionHooks(block, propsProxy, state, instance)
162+
if (container) {
163+
insert(block, container)
164+
return container
165+
}
166+
return block
167+
})
164168
slottedBlock = block
169+
165170
currentTag = tag
171+
currentSlot = slot
166172
isMounted = true
167173
})
168174
return frag

0 commit comments

Comments
 (0)