Skip to content

Commit

Permalink
fix(custom-elements): fix event emitting for async custom elements (v…
Browse files Browse the repository at this point in the history
  • Loading branch information
LinusBorg authored and chrislone committed Feb 4, 2023
1 parent f7de757 commit 4c2b349
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 20 deletions.
11 changes: 7 additions & 4 deletions packages/runtime-core/src/apiAsyncComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
8 changes: 0 additions & 8 deletions packages/runtime-core/src/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
24 changes: 23 additions & 1 deletion packages/runtime-dom/__tests__/customElement.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
defineAsyncComponent,
defineComponent,
defineCustomElement,
h,
inject,
Expand Down Expand Up @@ -227,7 +228,7 @@ describe('defineCustomElement', () => {
})

describe('emits', () => {
const E = defineCustomElement({
const CompDef = defineComponent({
setup(_, { emit }) {
emit('created')
return () =>
Expand All @@ -241,6 +242,7 @@ describe('defineCustomElement', () => {
})
}
})
const E = defineCustomElement(CompDef)
customElements.define('my-el-emits', E)

test('emit on connect', () => {
Expand Down Expand Up @@ -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', () => {
Expand Down
9 changes: 2 additions & 7 deletions packages/runtime-dom/src/apiCustomElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down

0 comments on commit 4c2b349

Please sign in to comment.