Skip to content

Commit

Permalink
fix: event listener not being invoked for native web components (#4012)
Browse files Browse the repository at this point in the history
* fix: event listener not being invoked for native web components

* chore: simplify test

* chore: test tweaks

* chore: also avoid wrapping listeners for non-Node EventTargets

* chore: account for light dom nodes

* chore: account for window

---------

Co-authored-by: Eugene Kashida <ekashida@gmail.com>
  • Loading branch information
jye-sf and ekashida committed Mar 4, 2024
1 parent 36e53a0 commit 943fe6e
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { createElement } from 'lwc';
import Test from 'x/test';

describe('event.composedPath() of event dispatched from closed shadow root', () => {
it('should have shadowed elements when invoked inside the shadow root', () => {
const elm = createElement('x-test', { is: Test });
document.body.appendChild(elm);

return Promise.resolve().then(() => {
elm.clickShadowedButton();

expect(elm.getShadowedComposedPath()).toEqual([
jasmine.any(HTMLElement), // button
jasmine.any(Object), // #shadow-root(closed)
elm.child,
elm.shadowRoot,
elm,
document.body,
document.documentElement,
document,
window,
]);
});
});

it('should not have shadowed elements when invoked outside the shadow root', () => {
const elm = createElement('x-test', { is: Test });
document.body.appendChild(elm);

return Promise.resolve().then(() => {
elm.clickShadowedButton();

expect(elm.getComposedPath()).toEqual([
elm.child,
elm.shadowRoot,
elm,
document.body,
document.documentElement,
document,
window,
]);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
customElements.define(
'x-mixed-shadow-mode-composed-path',
class extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({ mode: 'closed' });
this._button = document.createElement('button');
this._button.addEventListener('click', (event) => {
this.composedPath = event.composedPath();
});
this._shadowRoot.appendChild(this._button);
}

click() {
this._button.click();
}
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<x-mixed-shadow-mode-composed-path lwc:external lwc:ref="child" onclick={handleClick}></x-mixed-shadow-mode-composed-path>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { LightningElement, api } from 'lwc';
import './component.js';

export default class Parent extends LightningElement {
composedPath;

@api
get child() {
return this.refs.child;
}

@api
getShadowedComposedPath() {
return this.child.composedPath;
}

@api
getComposedPath() {
return this.composedPath;
}

@api
clickShadowedButton() {
this.child.click();
}

handleClick(event) {
this.composedPath = event.composedPath();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
eventTargetPrototype,
removeEventListener as nativeRemoveEventListener,
} from '../../env/event-target';
import { Node } from '../../env/node';
import { isInstanceOfNativeShadowRoot } from '../../env/shadow-root';
import {
addCustomElementEventListener,
removeCustomElementEventListener,
Expand All @@ -28,6 +30,13 @@ function patchedAddEventListener(
// @ts-expect-error type-mismatch
return addCustomElementEventListener.apply(this, arguments);
}

if (this instanceof Node && isInstanceOfNativeShadowRoot(this.getRootNode())) {
// Typescript does not like it when you treat the `arguments` object as an array
// @ts-expect-error type-mismatch
return nativeAddEventListener.apply(this, arguments);
}

if (arguments.length < 2) {
// Slow path, unlikely to be called frequently. We expect modern browsers to throw:
// https://googlechrome.github.io/samples/event-listeners-mandatory-arguments/
Expand Down

0 comments on commit 943fe6e

Please sign in to comment.