From e181bff6dc39d5cef92000c10291243c7d6e4d08 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 3 Aug 2024 13:46:58 +0800 Subject: [PATCH] feat(custom-element): support emit with options In a custom element created via `defineCustomElement`, if the first event argument is an object, it will be used as the options object for the emitted CustomEvent. The entire argument list is still exposed via the CustomEvent's `detail` property. ```js emit('event', { bubbles: true }) ``` close #7605 --- .../__tests__/customElement.spec.ts | 17 +++++++++++++++++ packages/runtime-dom/src/apiCustomElement.ts | 18 ++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index ab4d6f47939..1ed7cc25311 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -408,6 +408,9 @@ describe('defineCustomElement', () => { onMousedown: () => { emit('myEvent', 1) // validate hyphenation }, + onWheel: () => { + emit('my-wheel', { bubbles: true }, 1) + }, }) }, }) @@ -467,6 +470,7 @@ describe('defineCustomElement', () => { detail: [1], }) }) + // #7293 test('emit in an async component wrapper with properties bound', async () => { const E = defineCustomElement( @@ -488,6 +492,19 @@ describe('defineCustomElement', () => { detail: [1], }) }) + + test('emit with options', async () => { + container.innerHTML = `` + const e = container.childNodes[0] as VueElement + const spy = vi.fn() + e.addEventListener('my-wheel', spy) + e.shadowRoot!.childNodes[0].dispatchEvent(new CustomEvent('wheel')) + expect(spy).toHaveBeenCalledTimes(1) + expect(spy.mock.calls[0][0]).toMatchObject({ + bubbles: true, + detail: [{ bubbles: true }, 1], + }) + }) }) describe('slots', () => { diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 5c421b38ed5..47672370791 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -27,7 +27,14 @@ import { nextTick, warn, } from '@vue/runtime-core' -import { camelize, extend, hyphenate, isArray, toNumber } from '@vue/shared' +import { + camelize, + extend, + hyphenate, + isArray, + isPlainObject, + toNumber, +} from '@vue/shared' import { hydrate, render } from '.' export type VueElementConstructor

= { @@ -405,9 +412,12 @@ export class VueElement extends BaseClass { const dispatch = (event: string, args: any[]) => { this.dispatchEvent( - new CustomEvent(event, { - detail: args, - }), + new CustomEvent( + event, + isPlainObject(args[0]) + ? extend({ detail: args }, args[0]) + : { detail: args }, + ), ) }