]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(custom-elements): fix event emitting for async custom elements (#5601)
authorThorsten Lünborg <t.luenborg@googlemail.com>
Fri, 11 Nov 2022 04:33:17 +0000 (05:33 +0100)
committerGitHub <noreply@github.com>
Fri, 11 Nov 2022 04:33:17 +0000 (23:33 -0500)
fix #5599

packages/runtime-core/src/apiAsyncComponent.ts
packages/runtime-core/src/hmr.ts
packages/runtime-dom/__tests__/customElement.spec.ts
packages/runtime-dom/src/apiCustomElement.ts

index 41ccd94a7445faa644aa1ced414eeba1fc727007..09a857bc08b5a06e8d5c4c3e30f9873f18563c48 100644 (file)
@@ -211,13 +211,16 @@ export function defineAsyncComponent<
 
 function createInnerComp(
   comp: ConcreteComponent,
-  {
-    vnode: { ref, props, children, shapeFlag },
-    parent
-  }: ComponentInternalInstance
+  parent: ComponentInternalInstance
 ) {
+  const { ref, props, children, ce } = parent.vnode
   const vnode = createVNode(comp, props, children)
   // ensure inner component inherits the async wrapper's ref owner
   vnode.ref = ref
+  // pass the custom element callback on to the inner comp
+  // and remove it from the async wrapper
+  vnode.ce = ce
+  delete parent.vnode.ce
+
   return vnode
 }
index 3c3f5208bcc617ebbf24e19ae1e5f08416d140a9..c5039f62b6f3d8e70c52423f9a3f38c784c26d02 100644 (file)
@@ -136,14 +136,6 @@ function reload(id: string, newComp: HMRComponent) {
       // components to be unmounted and re-mounted. Queue the update so that we
       // don't end up forcing the same parent to re-render multiple times.
       queueJob(instance.parent.update)
-      // instance is the inner component of an async custom element
-      // invoke to reset styles
-      if (
-        (instance.parent.type as ComponentOptions).__asyncLoader &&
-        instance.parent.ceReload
-      ) {
-        instance.parent.ceReload((newComp as any).styles)
-      }
     } else if (instance.appContext.reload) {
       // root instance mounted via createApp() has a reload method
       instance.appContext.reload()
index eccbe4d425f09513fade5d3dc011e909f11db249..68049efe7a3276078390ce0885ce5df72cbab2ae 100644 (file)
@@ -1,5 +1,6 @@
 import {
   defineAsyncComponent,
+  defineComponent,
   defineCustomElement,
   h,
   inject,
@@ -227,7 +228,7 @@ describe('defineCustomElement', () => {
   })
 
   describe('emits', () => {
-    const E = defineCustomElement({
+    const CompDef = defineComponent({
       setup(_, { emit }) {
         emit('created')
         return () =>
@@ -241,6 +242,7 @@ describe('defineCustomElement', () => {
           })
       }
     })
+    const E = defineCustomElement(CompDef)
     customElements.define('my-el-emits', E)
 
     test('emit on connect', () => {
@@ -277,6 +279,26 @@ describe('defineCustomElement', () => {
       expect(spy1).toHaveBeenCalledTimes(1)
       expect(spy2).toHaveBeenCalledTimes(1)
     })
+
+    test('emit from within async component wrapper', async () => {
+      const E = defineCustomElement(
+        defineAsyncComponent(
+          () => new Promise<typeof CompDef>(res => res(CompDef as any))
+        )
+      )
+      customElements.define('my-async-el-emits', E)
+      container.innerHTML = `<my-async-el-emits></my-async-el-emits>`
+      const e = container.childNodes[0] as VueElement
+      const spy = jest.fn()
+      e.addEventListener('my-click', spy)
+      // this feels brittle but seems necessary to reach the node in the DOM.
+      await customElements.whenDefined('my-async-el-emits')
+      e.shadowRoot!.childNodes[0].dispatchEvent(new CustomEvent('click'))
+      expect(spy).toHaveBeenCalled()
+      expect(spy.mock.calls[0][0]).toMatchObject({
+        detail: [1]
+      })
+    })
   })
 
   describe('slots', () => {
index 92bd2f3ad3dc55ac7ec3cc94289a15bf1aef3a5a..fc68bb43f26d7b9ce1a80afe6c05d252a00897e9 100644 (file)
@@ -341,13 +341,8 @@ export class VueElement extends BaseClass {
               this._styles.length = 0
             }
             this._applyStyles(newStyles)
-            // if this is an async component, ceReload is called from the inner
-            // component so no need to reload the async wrapper
-            if (!(this._def as ComponentOptions).__asyncLoader) {
-              // reload
-              this._instance = null
-              this._update()
-            }
+            this._instance = null
+            this._update()
           }
         }