diff --git a/src/runtime/bootstrap-custom-element.ts b/src/runtime/bootstrap-custom-element.ts index 0f6964e5be2..4db002b21a2 100644 --- a/src/runtime/bootstrap-custom-element.ts +++ b/src/runtime/bootstrap-custom-element.ts @@ -67,13 +67,17 @@ export const proxyCustomElement = (Cstr: any, compactMeta: d.ComponentRuntimeMet const originalConnectedCallback = Cstr.prototype.connectedCallback; const originalDisconnectedCallback = Cstr.prototype.disconnectedCallback; + let hasHostListenerAttached = false; Object.assign(Cstr.prototype, { __registerHost() { registerHost(this, cmpMeta); }, connectedCallback() { - const hostRef = getHostRef(this); - addHostEventListeners(this, hostRef, cmpMeta.$listeners$, false); + if (!hasHostListenerAttached) { + const hostRef = getHostRef(this); + addHostEventListeners(this, hostRef, cmpMeta.$listeners$, false); + hasHostListenerAttached = true; + } connectedCallback(this); if (BUILD.connectedCallback && originalConnectedCallback) { diff --git a/test/wdio/event-listener-capture/event-re-register.css b/test/wdio/event-listener-capture/event-re-register.css new file mode 100644 index 00000000000..04dfd663dd3 --- /dev/null +++ b/test/wdio/event-listener-capture/event-re-register.css @@ -0,0 +1,10 @@ +:host { + display: block; + padding: 5px; + background: bisque; + cursor: pointer; + max-width: 300px; +} +:host(:focus) { + outline: 2px solid blue; +} diff --git a/test/wdio/event-listener-capture/event-re-register.test.tsx b/test/wdio/event-listener-capture/event-re-register.test.tsx new file mode 100644 index 00000000000..56ca474c4c7 --- /dev/null +++ b/test/wdio/event-listener-capture/event-re-register.test.tsx @@ -0,0 +1,47 @@ +// @ts-expect-error will be resolved by WDIO +import { defineCustomElement } from '/test-components/event-re-register.js'; + +defineCustomElement(); + +describe('event-listener-capture using lazy load components', function () { + const eventListenerCaptureCmp = () => $('event-re-register'); + + afterEach(() => { + const elem = document.querySelector('event-re-register') as HTMLElement; + if (elem) { + elem.remove(); + } + }); + + it('should only attach keydown event listener once', async () => { + const elem = document.createElement('event-re-register') as HTMLElement; + document.body.appendChild(elem); + + const reattach = eventListenerCaptureCmp(); + await expect(reattach).toBePresent(); + + // focus on element + await reattach.click(); + await browser.action('key').down('a').pause(100).up('a').perform(); + await browser.action('key').down('a').pause(100).up('a').perform(); + await browser.action('key').down('a').pause(100).up('a').perform(); + + // check if event fired 3 times + await expect(reattach).toHaveText(expect.stringContaining('Event fired times: 3')); + + // remove node from DOM + elem.remove(); + + // reattach node to DOM + document.body.appendChild(elem); + + // retrigger event + await reattach.click(); + await browser.action('key').down('a').pause(100).up('a').perform(); + await browser.action('key').down('a').pause(100).up('a').perform(); + await browser.action('key').down('a').pause(100).up('a').perform(); + + // check if event fired 6 times + await expect(reattach).toHaveText(expect.stringContaining('Event fired times: 6')); + }); +}); diff --git a/test/wdio/event-listener-capture/event-re-register.tsx b/test/wdio/event-listener-capture/event-re-register.tsx new file mode 100644 index 00000000000..20429e1ba45 --- /dev/null +++ b/test/wdio/event-listener-capture/event-re-register.tsx @@ -0,0 +1,36 @@ +import { Component, ComponentInterface, h, Host, Listen, State } from '@stencil/core'; + +@Component({ + tag: 'event-re-register', + styleUrl: 'event-re-register.css', + shadow: true, +}) +export class EventReRegister implements ComponentInterface { + @State() eventFiredTimes: number = 0; + @Listen('keydown') + handleKeydown(event: KeyboardEvent) { + this.eventFiredTimes++; + console.log(event); + } + + connectedCallback() { + console.log('connected'); + } + disconnectedCallback() { + console.log('disconnected'); + } + render() { + return ( + + +

Event fired times: {this.eventFiredTimes}

+
+ ); + } +} diff --git a/test/wdio/setup.ts b/test/wdio/setup.ts index fe4500d79b8..7f936432828 100644 --- a/test/wdio/setup.ts +++ b/test/wdio/setup.ts @@ -15,10 +15,11 @@ const testRequiresManualSetup = window.__wdioSpec__.includes('custom-elements-output') || window.__wdioSpec__.includes('global-script') || window.__wdioSpec__.endsWith('custom-tag-name.test.tsx') || - window.__wdioSpec__.endsWith('page-list.test.ts'); + window.__wdioSpec__.endsWith('page-list.test.ts') || + window.__wdioSpec__.endsWith('event-re-register.test.tsx'); /** - * setup all components defined in tests except for those where we want ot manually setup + * setup all components defined in tests except for those where we want to manually setup * the components in the test */ if (!testRequiresManualSetup) {