Skip to content

Commit 000c04f

Browse files
author
yangchangtao
committed
feat(transition): support transition to teleport component child
1 parent 9a36f2a commit 000c04f

File tree

3 files changed

+89
-5
lines changed

3 files changed

+89
-5
lines changed

packages/runtime-core/src/componentRenderUtils.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
warnDeprecation,
2828
} from './compat/compatConfig'
2929
import { shallowReadonly } from '@vue/reactivity'
30-
import { setTransitionHooks } from './components/BaseTransition'
30+
import { getInnerChild, setTransitionHooks } from './components/BaseTransition'
3131

3232
/**
3333
* dev only flag to track whether $attrs was used during render.
@@ -248,13 +248,14 @@ export function renderComponentRoot(
248248
}
249249
// inherit transition data
250250
if (vnode.transition) {
251-
if (__DEV__ && !isElementRoot(root)) {
251+
const child = getInnerChild(root) ?? root
252+
if (__DEV__ && !isElementRoot(child)) {
252253
warn(
253254
`Component inside <Transition> renders non-element root node ` +
254255
`that cannot be animated.`,
255256
)
256257
}
257-
setTransitionHooks(root, vnode.transition)
258+
setTransitionHooks(child, vnode.transition)
258259
}
259260

260261
if (__DEV__ && setRoot) {

packages/runtime-core/src/components/BaseTransition.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ function emptyPlaceholder(vnode: VNode): VNode | undefined {
484484
}
485485
}
486486

487-
function getInnerChild(vnode: VNode): VNode | undefined {
487+
export function getInnerChild(vnode: VNode): VNode | undefined {
488488
if (!isKeepAlive(vnode)) {
489489
if (isTeleport(vnode.type) && vnode.children) {
490490
return findNonCommentChild(vnode.children as VNode[])

packages/vue/__tests__/e2e/Transition.spec.ts

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { E2E_TIMEOUT, setupPuppeteer } from './e2eUtils'
22
import path from 'node:path'
3-
import { Transition, createApp, h, nextTick, ref } from 'vue'
3+
import { Teleport, Transition, createApp, h, nextTick, ref } from 'vue'
44

55
describe('e2e: Transition', () => {
66
const { page, html, classList, isVisible, timeout, nextFrame, click } =
@@ -2310,6 +2310,89 @@ describe('e2e: Transition', () => {
23102310
)
23112311
},
23122312
E2E_TIMEOUT,
2313+
),
2314+
test(
2315+
'apply transition to teleport component child',
2316+
async () => {
2317+
await page().evaluate(() => {
2318+
const { createApp, ref, h } = (window as any).Vue
2319+
createApp({
2320+
template: `
2321+
<div id="target"></div>
2322+
<div id="container">
2323+
<transition>
2324+
<Comp v-if="toggle" >content</Comp>
2325+
</transition>
2326+
</div>
2327+
<button id="toggleBtn" @click="click">button</button>
2328+
`,
2329+
components: {
2330+
Comp: {
2331+
setup() {
2332+
return () => h(Teleport, { to: '#target' }, [h('div', { class: 'test' }, 'content')])
2333+
},
2334+
},
2335+
},
2336+
setup: () => {
2337+
const toggle = ref(false)
2338+
const click = () => (toggle.value = !toggle.value)
2339+
return { toggle, click }
2340+
},
2341+
}).mount('#app')
2342+
})
2343+
2344+
expect(await html('#target')).toBe('')
2345+
expect(await html('#container')).toBe(
2346+
'<!--v-if-->',
2347+
)
2348+
2349+
const classWhenTransitionStart = () =>
2350+
page().evaluate(() => {
2351+
;(document.querySelector('#toggleBtn') as any)!.click()
2352+
return Promise.resolve().then(() => {
2353+
// find the class of teleported node
2354+
return document
2355+
.querySelector('#target div')!
2356+
.className.split(/\s+/g)
2357+
})
2358+
})
2359+
2360+
// enter
2361+
expect(await classWhenTransitionStart()).toStrictEqual([
2362+
'test',
2363+
'v-enter-from',
2364+
'v-enter-active',
2365+
])
2366+
await nextFrame()
2367+
expect(await classList('.test')).toStrictEqual([
2368+
'test',
2369+
'v-enter-active',
2370+
'v-enter-to',
2371+
])
2372+
await transitionFinish()
2373+
expect(await html('#target')).toBe(
2374+
'<div class="test">content</div>',
2375+
)
2376+
2377+
// leave
2378+
expect(await classWhenTransitionStart()).toStrictEqual([
2379+
'test',
2380+
'v-leave-from',
2381+
'v-leave-active',
2382+
])
2383+
await nextFrame()
2384+
expect(await classList('.test')).toStrictEqual([
2385+
'test',
2386+
'v-leave-active',
2387+
'v-leave-to',
2388+
])
2389+
await transitionFinish()
2390+
expect(await html('#target')).toBe('')
2391+
expect(await html('#container')).toBe(
2392+
'<!--v-if-->',
2393+
)
2394+
},
2395+
E2E_TIMEOUT,
23132396
)
23142397
})
23152398

0 commit comments

Comments
 (0)