Skip to content

Commit c11f6ee

Browse files
authored
fix(transition): avoid move transition for hidden v-show group children (#14895)
close #14894
1 parent 0dcd225 commit c11f6ee

2 files changed

Lines changed: 99 additions & 1 deletion

File tree

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
resolveTransitionProps,
1010
vtcKey,
1111
} from './Transition'
12+
import { type VShowElement, vShowHidden } from '../directives/vShow'
1213
import {
1314
type ComponentOptions,
1415
DeprecationTypes,
@@ -139,7 +140,12 @@ const TransitionGroupImpl: ComponentOptions = /*@__PURE__*/ decorate({
139140
if (children) {
140141
for (let i = 0; i < children.length; i++) {
141142
const child = children[i]
142-
if (child.el && child.el instanceof Element) {
143+
if (
144+
child.el &&
145+
child.el instanceof Element &&
146+
// Hidden v-show nodes have no previous layout box to animate from.
147+
!(child.el as VShowElement)[vShowHidden]
148+
) {
143149
prevChildren.push(child)
144150
setTransitionHooks(
145151
child,

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

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,98 @@ describe('e2e: TransitionGroup', () => {
297297
E2E_TIMEOUT,
298298
)
299299

300+
test(
301+
'v-show enter does not also run move transition',
302+
async () => {
303+
await page().evaluate(duration => {
304+
const { createApp, ref, onMounted } = (window as any).Vue
305+
createApp({
306+
template: `
307+
<div id="container">
308+
<transition-group name="show-group" tag="div" id="showGroup">
309+
<div
310+
v-for="item in items"
311+
:key="item"
312+
:id="\`show-\${item}\`"
313+
v-show="visibleItems.includes(item)"
314+
class="show-item"
315+
>
316+
{{ item }}
317+
</div>
318+
</transition-group>
319+
</div>
320+
<button id="toggleBtn" @click="click">button</button>
321+
`,
322+
setup: () => {
323+
const items = ref(['a', 'b', 'c'])
324+
const visibleItems = ref(['a', 'c'])
325+
const click = () => (visibleItems.value = ['a', 'b', 'c'])
326+
327+
onMounted(() => {
328+
const styleNode = document.createElement('style')
329+
styleNode.textContent = `
330+
#showGroup {
331+
display: flex;
332+
gap: 5px;
333+
}
334+
#showGroup > .show-item {
335+
width: 100px;
336+
height: 20px;
337+
}
338+
.show-group-enter-active,
339+
.show-group-move {
340+
transition: transform ${duration}ms ease;
341+
}
342+
.show-group-enter-from {
343+
transform: translateX(1000px);
344+
}
345+
`
346+
document.head.appendChild(styleNode)
347+
})
348+
349+
return { click, items, visibleItems }
350+
},
351+
}).mount('#app')
352+
}, duration)
353+
354+
const duringTransition = await page().evaluate(() => {
355+
;(document.querySelector('#toggleBtn') as any)!.click()
356+
return Promise.resolve().then(() =>
357+
Array.from(document.querySelectorAll('.show-item')).map(node => ({
358+
text: node.textContent!.trim(),
359+
classes: Array.from(node.classList),
360+
inlineTransform: (node as HTMLElement).style.transform,
361+
})),
362+
)
363+
})
364+
365+
expect(duringTransition).toStrictEqual([
366+
{
367+
text: 'a',
368+
classes: ['show-item'],
369+
inlineTransform: '',
370+
},
371+
{
372+
text: 'b',
373+
classes: [
374+
'show-item',
375+
'show-group-enter-from',
376+
'show-group-enter-active',
377+
],
378+
inlineTransform: '',
379+
},
380+
{
381+
text: 'c',
382+
classes: ['show-item', 'show-group-move'],
383+
inlineTransform: '',
384+
},
385+
])
386+
387+
await transitionFinish()
388+
},
389+
E2E_TIMEOUT,
390+
)
391+
300392
test(
301393
'move while entering',
302394
async () => {

0 commit comments

Comments
 (0)