Skip to content

Commit 928014a

Browse files
committed
fix(transition): get root el of DEV_FRAGMENT_ROOT
fix #6745
1 parent 96ba71d commit 928014a

File tree

3 files changed

+69
-2
lines changed

3 files changed

+69
-2
lines changed

packages/runtime-core/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@ export {
302302
normalizeStyle
303303
} from '@vue/shared'
304304

305+
export { filterSingleRoot } from './componentRenderUtils'
306+
305307
// For test-utils
306308
export { transformVNodeArgs } from './vnode'
307309

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import {
2323
toRaw,
2424
compatUtils,
2525
DeprecationTypes,
26-
ComponentOptions
26+
ComponentOptions,
27+
VNodeArrayChildren,
28+
filterSingleRoot
2729
} from '@vue/runtime-core'
28-
import { extend } from '@vue/shared'
30+
import { extend, PatchFlags, ShapeFlags } from '@vue/shared'
2931

3032
const positionMap = new WeakMap<VNode, DOMRect>()
3133
const newPositionMap = new WeakMap<VNode, DOMRect>()
@@ -111,6 +113,28 @@ const TransitionGroupImpl: ComponentOptions = {
111113
}
112114

113115
prevChildren = children
116+
117+
// In dev mode, comments are preserved, and it's possible for a template
118+
// to have comments alongside the root element which makes it a fragment.
119+
// In that case we re-assign `el` so DOM operations access the actual
120+
// root element instead of the fragment root. (#6745)
121+
if (__DEV__ && prevChildren) {
122+
prevChildren.forEach(child => {
123+
if (
124+
child.shapeFlag & ShapeFlags.COMPONENT &&
125+
child.component &&
126+
child.component.subTree.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT
127+
) {
128+
const elementRoot = filterSingleRoot(
129+
child.component.subTree.children as VNodeArrayChildren
130+
)
131+
if (elementRoot) {
132+
child.el = elementRoot.el
133+
}
134+
}
135+
})
136+
}
137+
114138
children = slots.default ? getTransitionRawChildren(slots.default()) : []
115139

116140
for (let i = 0; i < children.length; i++) {

packages/vue/__tests__/TransitionGroup.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,4 +508,45 @@ describe('e2e: TransitionGroup', () => {
508508

509509
expect(`<TransitionGroup> children must be keyed`).toHaveBeenWarned()
510510
})
511+
512+
test(
513+
'works when child component has single root + comments',
514+
async () => {
515+
const onErrorSpy = jest.fn()
516+
await page().exposeFunction('onErrorSpy', onErrorSpy)
517+
518+
await page().evaluate(() => {
519+
const { onErrorSpy } = window as any
520+
const { createApp, ref, onErrorCaptured } = (window as any).Vue
521+
522+
const app = createApp({
523+
template: `
524+
<div id="container">
525+
<transition-group>
526+
<a-component v-if="show"></a-component>
527+
</transition-group>
528+
</div>
529+
<button id="toggleBtn" @click="click">button</button>
530+
`,
531+
setup: () => {
532+
onErrorCaptured(() => onErrorSpy())
533+
const show = ref(true)
534+
const click = () => (show.value = false)
535+
return { show, click }
536+
}
537+
})
538+
app.component('a-component', { template: `<!----><div></div>` })
539+
app.mount('#app')
540+
})
541+
542+
expect(await html('#container')).toBe('<!----><div></div>')
543+
544+
await htmlWhenTransitionStart()
545+
await transitionFinish()
546+
547+
expect(onErrorSpy).not.toBeCalled()
548+
expect(await html('#container')).toBe('')
549+
},
550+
E2E_TIMEOUT
551+
)
511552
})

0 commit comments

Comments
 (0)