From 3670f0f5255c323c4c5ae9b8e3bdfcbb8b50128d Mon Sep 17 00:00:00 2001 From: Thorsten Luenborg Date: Tue, 8 Feb 2022 18:08:49 +0100 Subject: [PATCH 1/2] fix(runtime-core): custom-element: ensure event names are hyphenated --- packages/runtime-dom/__tests__/customElement.spec.ts | 10 ++++++++-- packages/runtime-dom/src/apiCustomElement.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index dd087ad279d..6f8fd5012d1 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -232,7 +232,10 @@ describe('defineCustomElement', () => { emit('created') return () => h('div', { - onClick: () => emit('my-click', 1) + onClick: () => { + emit('my-click', 1) + emit('myClick', 1) // validate hypenization + } }) } }) @@ -252,10 +255,13 @@ describe('defineCustomElement', () => { const spy = jest.fn() e.addEventListener('my-click', spy) e.shadowRoot!.childNodes[0].dispatchEvent(new CustomEvent('click')) - expect(spy).toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(2) expect(spy.mock.calls[0][0]).toMatchObject({ detail: [1] }) + expect(spy.mock.calls[1][0]).toMatchObject({ + detail: [1] + }) }) }) diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 5108e3bdfbe..4a542c9ee27 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -354,7 +354,7 @@ export class VueElement extends BaseClass { // intercept emit instance.emit = (event: string, ...args: any[]) => { this.dispatchEvent( - new CustomEvent(event, { + new CustomEvent(hyphenate(event), { detail: args }) ) From b7b934d687c438cb0c6ae93ac1bc80b11955dab2 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 11 Nov 2022 11:56:56 +0800 Subject: [PATCH 2/2] fix: also listen for original case --- .../__tests__/customElement.spec.ts | 24 +++++++++++++++---- packages/runtime-dom/src/apiCustomElement.ts | 15 +++++++++--- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index 6f8fd5012d1..7472cac2913 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -234,7 +234,9 @@ describe('defineCustomElement', () => { h('div', { onClick: () => { emit('my-click', 1) - emit('myClick', 1) // validate hypenization + }, + onMousedown: () => { + emit('myEvent', 1) // validate hypenization } }) } @@ -255,13 +257,25 @@ describe('defineCustomElement', () => { const spy = jest.fn() e.addEventListener('my-click', spy) e.shadowRoot!.childNodes[0].dispatchEvent(new CustomEvent('click')) - expect(spy).toHaveBeenCalledTimes(2) + expect(spy).toHaveBeenCalledTimes(1) expect(spy.mock.calls[0][0]).toMatchObject({ detail: [1] }) - expect(spy.mock.calls[1][0]).toMatchObject({ - detail: [1] - }) + }) + + // #5373 + test('case transform for camelCase event', () => { + container.innerHTML = `` + const e = container.childNodes[0] as VueElement + const spy1 = jest.fn() + e.addEventListener('myEvent', spy1) + const spy2 = jest.fn() + // emitting myEvent, but listening for my-event. This happens when + // using the custom element in a Vue template + e.addEventListener('my-event', spy2) + e.shadowRoot!.childNodes[0].dispatchEvent(new CustomEvent('mousedown')) + expect(spy1).toHaveBeenCalledTimes(1) + expect(spy2).toHaveBeenCalledTimes(1) }) }) diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 4a542c9ee27..5f0f3e8921f 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -351,15 +351,24 @@ export class VueElement extends BaseClass { } } - // intercept emit - instance.emit = (event: string, ...args: any[]) => { + const dispatch = (event: string, args: any[]) => { this.dispatchEvent( - new CustomEvent(hyphenate(event), { + new CustomEvent(event, { detail: args }) ) } + // intercept emit + instance.emit = (event: string, ...args: any[]) => { + // dispatch both the raw and hyphenated versions of an event + // to match Vue behavior + dispatch(event, args) + if (hyphenate(event) !== event) { + dispatch(hyphenate(event), args) + } + } + // locate nearest Vue custom element parent for provide/inject let parent: Node | null = this while (