diff --git a/packages-private/vapor-e2e-test/__tests__/transition.spec.ts b/packages-private/vapor-e2e-test/__tests__/transition.spec.ts
index 6afc39a36..19770e7b9 100644
--- a/packages-private/vapor-e2e-test/__tests__/transition.spec.ts
+++ b/packages-private/vapor-e2e-test/__tests__/transition.spec.ts
@@ -5,8 +5,18 @@ import {
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
import connect from 'connect'
import sirv from 'sirv'
-const { page, classList, text, nextFrame, timeout, isVisible, count, html } =
- setupPuppeteer()
+const {
+ page,
+ classList,
+ text,
+ nextFrame,
+ timeout,
+ isVisible,
+ count,
+ html,
+ transitionStart,
+ waitForElement,
+} = setupPuppeteer()
const duration = process.env.CI ? 200 : 50
const buffer = process.env.CI ? 50 : 20
@@ -32,43 +42,6 @@ describe('vapor transition', () => {
await page().waitForSelector('#app')
})
- const transitionStart = (btnSelector: string, containerSelector: string) =>
- page().evaluate(
- ([btnSel, containerSel]) => {
- ;(document.querySelector(btnSel) as HTMLElement)!.click()
- return Promise.resolve().then(() => {
- const container = document.querySelector(containerSel)!
- return {
- classNames: container.className.split(/\s+/g),
- innerHTML: container.innerHTML,
- }
- })
- },
- [btnSelector, containerSelector],
- )
-
- const waitForElement = (
- selector: string,
- text: string,
- classNames: string[], // if empty, check for no classes
- timeout = 2000,
- ) =>
- page().waitForFunction(
- (sel, expectedText, expectedClasses) => {
- const el = document.querySelector(sel)
- const hasClasses =
- expectedClasses.length === 0
- ? el?.classList.length === 0
- : expectedClasses.every(c => el?.classList.contains(c))
- const hasText = el?.textContent?.includes(expectedText)
- return !!el && hasClasses && hasText
- },
- { timeout },
- selector,
- text,
- classNames,
- )
-
test(
'should work with v-show',
async () => {
diff --git a/packages-private/vapor-e2e-test/__tests__/vdomInterop.spec.ts b/packages-private/vapor-e2e-test/__tests__/vdomInterop.spec.ts
index 360f48085..a6eb410fb 100644
--- a/packages-private/vapor-e2e-test/__tests__/vdomInterop.spec.ts
+++ b/packages-private/vapor-e2e-test/__tests__/vdomInterop.spec.ts
@@ -5,10 +5,23 @@ import {
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
import connect from 'connect'
import sirv from 'sirv'
+const {
+ page,
+ click,
+ text,
+ enterValue,
+ html,
+ transitionStart,
+ waitForElement,
+ nextFrame,
+ timeout,
+} = setupPuppeteer()
+
+const duration = process.env.CI ? 200 : 50
+const buffer = process.env.CI ? 50 : 20
+const transitionFinish = (time = duration) => timeout(time + buffer)
describe('vdom / vapor interop', () => {
- const { page, click, text, enterValue } = setupPuppeteer()
-
let server: any
const port = '8193'
beforeAll(() => {
@@ -22,12 +35,15 @@ describe('vdom / vapor interop', () => {
server.close()
})
+ beforeEach(async () => {
+ const baseUrl = `http://localhost:${port}/interop/`
+ await page().goto(baseUrl)
+ await page().waitForSelector('#app')
+ })
+
test(
'should work',
async () => {
- const baseUrl = `http://localhost:${port}/interop/`
- await page().goto(baseUrl)
-
expect(await text('.vapor > h2')).toContain('Vapor component in VDOM')
expect(await text('.vapor-prop')).toContain('hello')
@@ -81,4 +97,121 @@ describe('vdom / vapor interop', () => {
},
E2E_TIMEOUT,
)
+
+ describe('vdom transition', () => {
+ test(
+ 'render vapor component',
+ async () => {
+ const btnSelector = '.trans-vapor > button'
+ const containerSelector = '.trans-vapor > div'
+
+ expect(await html(containerSelector)).toBe(
+ `
vapor compA
`,
+ )
+
+ // comp leave
+ expect(
+ (await transitionStart(btnSelector, containerSelector)).innerHTML,
+ ).toBe(
+ `vapor compA
`,
+ )
+
+ await nextFrame()
+ expect(await html(containerSelector)).toBe(
+ `vapor compA
`,
+ )
+
+ await transitionFinish()
+ expect(await html(containerSelector)).toBe(``)
+
+ // comp enter
+ expect(
+ (await transitionStart(btnSelector, containerSelector)).innerHTML,
+ ).toBe(
+ `vapor compA
`,
+ )
+
+ await nextFrame()
+ expect(await html(containerSelector)).toBe(
+ `vapor compA
`,
+ )
+
+ await transitionFinish()
+ expect(await html(containerSelector)).toBe(
+ `vapor compA
`,
+ )
+ },
+ E2E_TIMEOUT,
+ )
+
+ test(
+ 'switch between vdom/vapor component (out-in mode)',
+ async () => {
+ const btnSelector = '.trans-vdom-vapor-out-in > button'
+ const containerSelector = '.trans-vdom-vapor-out-in > div'
+ const childSelector = `${containerSelector} > div`
+
+ expect(await html(containerSelector)).toBe(`vdom comp
`)
+
+ // switch to vapor comp
+ // vdom comp leave
+ expect(
+ (await transitionStart(btnSelector, containerSelector)).innerHTML,
+ ).toBe(
+ `vdom comp
`,
+ )
+
+ await nextFrame()
+ expect(await html(containerSelector)).toBe(
+ `vdom comp
`,
+ )
+
+ // vapor comp enter
+ await waitForElement(childSelector, 'vapor compA', [
+ 'fade-enter-from',
+ 'fade-enter-active',
+ ])
+
+ await nextFrame()
+ expect(await html(containerSelector)).toBe(
+ `vapor compA
`,
+ )
+
+ await transitionFinish()
+ expect(await html(containerSelector)).toBe(
+ `vapor compA
`,
+ )
+
+ // switch to vdom comp
+ // vapor comp leave
+ expect(
+ (await transitionStart(btnSelector, containerSelector)).innerHTML,
+ ).toBe(
+ `vapor compA
`,
+ )
+
+ await nextFrame()
+ expect(await html(containerSelector)).toBe(
+ `vapor compA
`,
+ )
+
+ // vdom comp enter
+ await waitForElement(childSelector, 'vdom comp', [
+ 'fade-enter-from',
+ 'fade-enter-active',
+ ])
+
+ await nextFrame()
+ expect(await html(containerSelector)).toBe(
+ `vdom comp
`,
+ )
+
+ await transitionFinish()
+ expect(await html(containerSelector)).toBe(
+ `vdom comp
`,
+ )
+ },
+ E2E_TIMEOUT,
+ )
+ })
})
diff --git a/packages-private/vapor-e2e-test/interop/App.vue b/packages-private/vapor-e2e-test/interop/App.vue
index 772a6989d..f29df3c80 100644
--- a/packages-private/vapor-e2e-test/interop/App.vue
+++ b/packages-private/vapor-e2e-test/interop/App.vue
@@ -1,9 +1,18 @@
@@ -19,4 +28,28 @@ const passSlot = ref(true)
A test slot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages-private/vapor-e2e-test/interop/main.ts b/packages-private/vapor-e2e-test/interop/main.ts
index d5d6d7dcf..41155dc5c 100644
--- a/packages-private/vapor-e2e-test/interop/main.ts
+++ b/packages-private/vapor-e2e-test/interop/main.ts
@@ -1,4 +1,5 @@
import { createApp, vaporInteropPlugin } from 'vue'
import App from './App.vue'
+import '../transition/style.css'
createApp(App).use(vaporInteropPlugin).mount('#app')
diff --git a/packages-private/vapor-e2e-test/transition/components/VaporCompA.vue b/packages-private/vapor-e2e-test/transition/components/VaporCompA.vue
index 40b09eaf4..f6902d8cf 100644
--- a/packages-private/vapor-e2e-test/transition/components/VaporCompA.vue
+++ b/packages-private/vapor-e2e-test/transition/components/VaporCompA.vue
@@ -1,4 +1,4 @@
-
diff --git a/packages-private/vapor-e2e-test/transition/components/VaporCompB.vue b/packages-private/vapor-e2e-test/transition/components/VaporCompB.vue
index c22f3b6bb..db90f993f 100644
--- a/packages-private/vapor-e2e-test/transition/components/VaporCompB.vue
+++ b/packages-private/vapor-e2e-test/transition/components/VaporCompB.vue
@@ -1,4 +1,4 @@
-
diff --git a/packages-private/vapor-e2e-test/transition/components/VdomComp.vue b/packages-private/vapor-e2e-test/transition/components/VdomComp.vue
index 009ca229a..cb6ec7cca 100644
--- a/packages-private/vapor-e2e-test/transition/components/VdomComp.vue
+++ b/packages-private/vapor-e2e-test/transition/components/VdomComp.vue
@@ -1,4 +1,4 @@
-
diff --git a/packages/runtime-vapor/src/components/Transition.ts b/packages/runtime-vapor/src/components/Transition.ts
index 8ff58bf73..e3094ed14 100644
--- a/packages/runtime-vapor/src/components/Transition.ts
+++ b/packages/runtime-vapor/src/components/Transition.ts
@@ -20,7 +20,7 @@ import {
type VaporTransitionHooks,
isFragment,
} from '../block'
-import { isVaporComponent } from '../component'
+import { type VaporComponentInstance, isVaporComponent } from '../component'
const decorate = (t: typeof VaporTransition) => {
t.displayName = 'VaporTransition'
@@ -244,3 +244,13 @@ export function findTransitionBlock(block: Block): TransitionBlock | undefined {
return child
}
+
+export function setTransitionToInstance(
+ block: VaporComponentInstance,
+ hooks: VaporTransitionHooks,
+): void {
+ const child = findTransitionBlock(block.block)
+ if (!child) return
+
+ setTransitionHooks(child, hooks)
+}
diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts
index 44ec5105e..4724b302c 100644
--- a/packages/runtime-vapor/src/vdomInterop.ts
+++ b/packages/runtime-vapor/src/vdomInterop.ts
@@ -15,7 +15,7 @@ import {
ensureRenderer,
onScopeDispose,
renderSlot,
- setTransitionHooks,
+ setTransitionHooks as setVNodeTransitionHooks,
shallowRef,
simpleSetCurrentInstance,
} from '@vue/runtime-dom'
@@ -28,13 +28,20 @@ import {
mountComponent,
unmountComponent,
} from './component'
-import { type Block, VaporFragment, insert, remove } from './block'
+import {
+ type Block,
+ VaporFragment,
+ type VaporTransitionHooks,
+ insert,
+ remove,
+} from './block'
import { EMPTY_OBJ, extend, isFunction } from '@vue/shared'
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
import type { RawSlots, VaporSlot } from './componentSlots'
import { renderEffect } from './renderEffect'
import { createTextNode } from './dom/node'
import { optimizePropertyLookup } from './dom/prop'
+import { setTransitionToInstance } from './components/Transition'
// mounting vapor components and slots in vdom
const vaporInteropImpl: Omit<
@@ -62,6 +69,12 @@ const vaporInteropImpl: Omit<
))
instance.rawPropsRef = propsRef
instance.rawSlotsRef = slotsRef
+ if (vnode.transition) {
+ setTransitionToInstance(
+ instance,
+ vnode.transition as VaporTransitionHooks,
+ )
+ }
mountComponent(instance, container, selfAnchor)
simpleSetCurrentInstance(prev)
return instance
@@ -174,7 +187,7 @@ function createVDOMComponent(
let isMounted = false
const parentInstance = currentInstance as VaporComponentInstance
const unmount = (parentNode?: ParentNode, transition?: TransitionHooks) => {
- if (transition) setTransitionHooks(vnode, transition)
+ if (transition) setVNodeTransitionHooks(vnode, transition)
internals.umt(vnode.component!, null, !!parentNode)
}
@@ -182,7 +195,7 @@ function createVDOMComponent(
const prev = currentInstance
simpleSetCurrentInstance(parentInstance)
if (!isMounted) {
- if (transition) setTransitionHooks(vnode, transition)
+ if (transition) setVNodeTransitionHooks(vnode, transition)
internals.mt(
vnode,
parentNode,
diff --git a/packages/vue/__tests__/e2e/e2eUtils.ts b/packages/vue/__tests__/e2e/e2eUtils.ts
index 2ffebeb59..ac05a47e7 100644
--- a/packages/vue/__tests__/e2e/e2eUtils.ts
+++ b/packages/vue/__tests__/e2e/e2eUtils.ts
@@ -50,6 +50,16 @@ interface PuppeteerUtils {
clearValue(selector: string): Promise
timeout(time: number): Promise
nextFrame(): Promise
+ transitionStart(
+ btnSelector: string,
+ containerSelector: string,
+ ): Promise<{ classNames: string[]; innerHTML: string }>
+ waitForElement(
+ selector: string,
+ text: string,
+ classNames: string[],
+ timeout?: number,
+ ): Promise
}
export function setupPuppeteer(args?: string[]): PuppeteerUtils {
@@ -200,6 +210,43 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
})
}
+ const transitionStart = (btnSelector: string, containerSelector: string) =>
+ page.evaluate(
+ ([btnSel, containerSel]) => {
+ ;(document.querySelector(btnSel) as HTMLElement)!.click()
+ return Promise.resolve().then(() => {
+ const container = document.querySelector(containerSel)!
+ return {
+ classNames: container.className.split(/\s+/g),
+ innerHTML: container.innerHTML,
+ }
+ })
+ },
+ [btnSelector, containerSelector],
+ )
+
+ const waitForElement = (
+ selector: string,
+ text: string,
+ classNames: string[], // if empty, check for no classes
+ timeout = 2000,
+ ) =>
+ page.waitForFunction(
+ (sel, expectedText, expectedClasses) => {
+ const el = document.querySelector(sel)
+ const hasClasses =
+ expectedClasses.length === 0
+ ? el?.classList.length === 0
+ : expectedClasses.every(c => el?.classList.contains(c))
+ const hasText = el?.textContent?.includes(expectedText)
+ return !!el && hasClasses && hasText
+ },
+ { timeout },
+ selector,
+ text,
+ classNames,
+ )
+
return {
page: () => page,
click,
@@ -219,5 +266,7 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
clearValue,
timeout,
nextFrame,
+ transitionStart,
+ waitForElement,
}
}