Skip to content

Commit

Permalink
fix(runtime): ensure event listener are not registered twice
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-bromann committed Nov 20, 2024
1 parent 6331d9a commit f05a2e7
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 4 deletions.
8 changes: 6 additions & 2 deletions src/runtime/bootstrap-custom-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
10 changes: 10 additions & 0 deletions test/wdio/event-listener-capture/event-re-register.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
:host {
display: block;
padding: 5px;
background: bisque;
cursor: pointer;
max-width: 300px;
}
:host(:focus) {
outline: 2px solid blue;
}
49 changes: 49 additions & 0 deletions test/wdio/event-listener-capture/event-re-register.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// @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'));
});
});
34 changes: 34 additions & 0 deletions test/wdio/event-listener-capture/event-re-register.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
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 <Host tabindex="1">
<ul id="reattach">
<li>Focus this component;</li>
<li>Press key;</li>
<li>See console output</li>
<li>Press 'Reconnect' button</li>
<li>Repeat steps 1-3</li>
</ul>
<p>Event fired times: {this.eventFiredTimes}</p>
</Host>;
}
}
5 changes: 3 additions & 2 deletions test/wdio/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit f05a2e7

Please sign in to comment.