]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(teleport): Teleport into SVG elements (#2648)
authorYasser Lahbibi <yasser.lahbibi@apenhet.com>
Mon, 30 Nov 2020 22:30:41 +0000 (23:30 +0100)
committerGitHub <noreply@github.com>
Mon, 30 Nov 2020 22:30:41 +0000 (17:30 -0500)
fix #2652

packages/runtime-core/__tests__/components/Teleport.spec.ts
packages/runtime-core/src/components/Teleport.ts

index eb8ee41635149f745a84c4962e734b19ce8fe707..15afa6db2e72258fb397339007858d4160543a14 100644 (file)
@@ -7,10 +7,11 @@ import {
   Text,
   ref,
   nextTick,
-  markRaw
+  markRaw,
+  defineComponent
 } from '@vue/runtime-test'
 import { createVNode, Fragment } from '../../src/vnode'
-import { compile } from 'vue'
+import { compile, render as domRender } from 'vue'
 
 describe('renderer: teleport', () => {
   test('should work', () => {
@@ -33,6 +34,37 @@ describe('renderer: teleport', () => {
     )
   })
 
+  test('should work with SVG', async () => {
+    const root = document.createElement('div')
+    const svg = ref()
+    const circle = ref()
+
+    const Comp = defineComponent({
+      setup() {
+        return {
+          svg,
+          circle
+        }
+      },
+      template: `
+      <svg ref="svg"></svg>
+      <teleport :to="svg" v-if="svg">
+      <circle ref="circle"></circle>
+      </teleport>`
+    })
+
+    domRender(h(Comp), root)
+
+    await nextTick()
+
+    expect(root.innerHTML).toMatchInlineSnapshot(
+      `"<svg><circle></circle></svg><!--teleport start--><!--teleport end-->"`
+    )
+
+    expect(svg.value.namespaceURI).toBe('http://www.w3.org/2000/svg')
+    expect(circle.value.namespaceURI).toBe('http://www.w3.org/2000/svg')
+  })
+
   test('should update target', async () => {
     const targetA = nodeOps.createElement('div')
     const targetB = nodeOps.createElement('div')
index 48f6e04a63402acd272cc198bb0184e1628263b3..fa61c636a880fb3c22b421d489659bb6e595a938 100644 (file)
@@ -24,6 +24,9 @@ export const isTeleport = (type: any): boolean => type.__isTeleport
 export const isTeleportDisabled = (props: VNode['props']): boolean =>
   props && (props.disabled || props.disabled === '')
 
+const isTargetSVG = (target: RendererElement): boolean =>
+  typeof SVGElement !== 'undefined' && target instanceof SVGElement
+
 const resolveTarget = <T = RendererElement>(
   props: TeleportProps | null,
   select: RendererOptions['querySelector']
@@ -80,6 +83,7 @@ export const TeleportImpl = {
 
     const disabled = isTeleportDisabled(n2.props)
     const { shapeFlag, children } = n2
+
     if (n1 == null) {
       // insert anchors in the main view
       const placeholder = (n2.el = __DEV__
@@ -90,11 +94,12 @@ export const TeleportImpl = {
         : createText(''))
       insert(placeholder, container, anchor)
       insert(mainAnchor, container, anchor)
-
       const target = (n2.target = resolveTarget(n2.props, querySelector))
       const targetAnchor = (n2.targetAnchor = createText(''))
       if (target) {
         insert(targetAnchor, target)
+        // #2652 we could be teleporting from a non-SVG tree into an SVG tree
+        isSVG = isSVG || isTargetSVG(target)
       } else if (__DEV__ && !disabled) {
         warn('Invalid Teleport target on mount:', target, `(${typeof target})`)
       }
@@ -129,6 +134,7 @@ export const TeleportImpl = {
       const wasDisabled = isTeleportDisabled(n1.props)
       const currentContainer = wasDisabled ? container : target
       const currentAnchor = wasDisabled ? mainAnchor : targetAnchor
+      isSVG = isSVG || isTargetSVG(target)
 
       if (n2.dynamicChildren) {
         // fast path when the teleport happens to be a block root