Skip to content

Commit 4b479db

Browse files
authoredNov 15, 2024··
fix(transition): reflow before leave-active class after leave-from (#12288)
re-fix #2593
1 parent a20a4cb commit 4b479db

File tree

4 files changed

+96
-8
lines changed

4 files changed

+96
-8
lines changed
 

‎packages/runtime-dom/src/components/Transition.ts

+21-7
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,13 @@ export function resolveTransitionProps(
181181
onAppearCancelled = onEnterCancelled,
182182
} = baseProps
183183

184-
const finishEnter = (el: Element, isAppear: boolean, done?: () => void) => {
184+
const finishEnter = (
185+
el: Element & { _enterCancelled?: boolean },
186+
isAppear: boolean,
187+
done?: () => void,
188+
isCancelled?: boolean,
189+
) => {
190+
el._enterCancelled = isCancelled
185191
removeTransitionClass(el, isAppear ? appearToClass : enterToClass)
186192
removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass)
187193
done && done()
@@ -240,7 +246,10 @@ export function resolveTransitionProps(
240246
},
241247
onEnter: makeEnterHook(false),
242248
onAppear: makeEnterHook(true),
243-
onLeave(el: Element & { _isLeaving?: boolean }, done) {
249+
onLeave(
250+
el: Element & { _isLeaving?: boolean; _enterCancelled?: boolean },
251+
done,
252+
) {
244253
el._isLeaving = true
245254
const resolve = () => finishLeave(el, done)
246255
addTransitionClass(el, leaveFromClass)
@@ -249,9 +258,14 @@ export function resolveTransitionProps(
249258
}
250259
// add *-leave-active class before reflow so in the case of a cancelled enter transition
251260
// the css will not get the final state (#10677)
252-
addTransitionClass(el, leaveActiveClass)
253-
// force reflow so *-leave-from classes immediately take effect (#2593)
254-
forceReflow()
261+
if (!el._enterCancelled) {
262+
// force reflow so *-leave-from classes immediately take effect (#2593)
263+
forceReflow()
264+
addTransitionClass(el, leaveActiveClass)
265+
} else {
266+
addTransitionClass(el, leaveActiveClass)
267+
forceReflow()
268+
}
255269
nextFrame(() => {
256270
if (!el._isLeaving) {
257271
// cancelled
@@ -269,11 +283,11 @@ export function resolveTransitionProps(
269283
callHook(onLeave, [el, resolve])
270284
},
271285
onEnterCancelled(el) {
272-
finishEnter(el, false)
286+
finishEnter(el, false, undefined, true)
273287
callHook(onEnterCancelled, [el])
274288
},
275289
onAppearCancelled(el) {
276-
finishEnter(el, true)
290+
finishEnter(el, true, undefined, true)
277291
callHook(onAppearCancelled, [el])
278292
},
279293
onLeaveCancelled(el) {

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

+50-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'node:path'
33
import { Transition, createApp, h, nextTick, ref } from 'vue'
44

55
describe('e2e: Transition', () => {
6-
const { page, html, classList, isVisible, timeout, nextFrame, click } =
6+
const { page, html, classList, style, isVisible, timeout, nextFrame, click } =
77
setupPuppeteer()
88
const baseUrl = `file://${path.resolve(__dirname, './transition.html')}`
99

@@ -2986,6 +2986,55 @@ describe('e2e: Transition', () => {
29862986
)
29872987
})
29882988

2989+
test('reflow after *-leave-from before *-leave-active', async () => {
2990+
await page().evaluate(() => {
2991+
const { createApp, ref } = (window as any).Vue
2992+
createApp({
2993+
template: `
2994+
<div id="container">
2995+
<transition name="test-reflow">
2996+
<div v-if="toggle" class="test-reflow">content</div>
2997+
</transition>
2998+
</div>
2999+
<button id="toggleBtn" @click="click">button</button>
3000+
`,
3001+
setup: () => {
3002+
const toggle = ref(false)
3003+
const click = () => (toggle.value = !toggle.value)
3004+
return {
3005+
toggle,
3006+
click,
3007+
}
3008+
},
3009+
}).mount('#app')
3010+
})
3011+
3012+
// if transition starts while there's v-leave-active added along with v-leave-from, its bad, it has to start when it doesnt have the v-leave-from
3013+
3014+
// enter
3015+
await classWhenTransitionStart()
3016+
await transitionFinish()
3017+
3018+
// leave
3019+
expect(await classWhenTransitionStart()).toStrictEqual([
3020+
'test-reflow',
3021+
'test-reflow-leave-from',
3022+
'test-reflow-leave-active',
3023+
])
3024+
3025+
expect(await style('.test-reflow', 'opacity')).toStrictEqual('0.9')
3026+
3027+
await nextFrame()
3028+
expect(await classList('.test-reflow')).toStrictEqual([
3029+
'test-reflow',
3030+
'test-reflow-leave-active',
3031+
'test-reflow-leave-to',
3032+
])
3033+
3034+
await transitionFinish()
3035+
expect(await html('#container')).toBe('<!--v-if-->')
3036+
})
3037+
29893038
test('warn when used on multiple elements', async () => {
29903039
createApp({
29913040
render() {

‎packages/vue/__tests__/e2e/e2eUtils.ts

+15
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ interface PuppeteerUtils {
3939
value(selector: string): Promise<string>
4040
html(selector: string): Promise<string>
4141
classList(selector: string): Promise<string[]>
42+
style(selector: string, property: keyof CSSStyleDeclaration): Promise<any>
4243
children(selector: string): Promise<any[]>
4344
isVisible(selector: string): Promise<boolean>
4445
isChecked(selector: string): Promise<boolean>
@@ -120,6 +121,19 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
120121
return page.$eval(selector, (node: any) => [...node.children])
121122
}
122123

124+
async function style(
125+
selector: string,
126+
property: keyof CSSStyleDeclaration,
127+
): Promise<any> {
128+
return await page.$eval(
129+
selector,
130+
(node, property) => {
131+
return window.getComputedStyle(node)[property]
132+
},
133+
property,
134+
)
135+
}
136+
123137
async function isVisible(selector: string): Promise<boolean> {
124138
const display = await page.$eval(selector, node => {
125139
return window.getComputedStyle(node).display
@@ -195,6 +209,7 @@ export function setupPuppeteer(args?: string[]): PuppeteerUtils {
195209
value,
196210
html,
197211
classList,
212+
style,
198213
children,
199214
isVisible,
200215
isChecked,

‎packages/vue/__tests__/e2e/transition.html

+10
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,21 @@
1616
.test-appear,
1717
.test-enter,
1818
.test-leave-active,
19+
.test-reflow-enter,
20+
.test-reflow-leave-to,
1921
.hello,
2022
.bye.active,
2123
.changed-enter {
2224
opacity: 0;
2325
}
26+
.test-reflow-leave-active,
27+
.test-reflow-enter-active {
28+
-webkit-transition: opacity 50ms ease;
29+
transition: opacity 50ms ease;
30+
}
31+
.test-reflow-leave-from {
32+
opacity: 0.9;
33+
}
2434
.test-anim-enter-active {
2535
animation: test-enter 50ms;
2636
-webkit-animation: test-enter 50ms;

0 commit comments

Comments
 (0)
Please sign in to comment.