Skip to content

Commit

Permalink
fix(engine): regular dom nodes can use object type event listeners (#943
Browse files Browse the repository at this point in the history
)

* fix: event listeners of type object are handled

* fix: address PR feedback
  • Loading branch information
ravijayaramappa authored Jan 11, 2019
1 parent 19953e1 commit adf3504
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 3 deletions.
54 changes: 54 additions & 0 deletions packages/@lwc/engine/src/faux-shadow/__tests__/events.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,4 +572,58 @@ describe('events', () => {
});
});
});
describe('event patching preserves standard listener specs', () => {
it('Issue#941: an object type EventListener works on standard html nodes outside custom element', () => {
let target;
const eventListener = {
handleEvent: function(evt) {
expect(this).toBe(eventListener);
target = evt.target;
}
};

class MyComponent extends LightningElement {
}
const elm = createElement('x-foo', { is: MyComponent });
const span = document.createElement('span');
span.appendChild(elm);
span.addEventListener('click', eventListener);
document.body.appendChild(span);
elm.click();
expect(target).toBe(elm);
});
it('Issue#941: an object type EventListener works on standard html nodes in the template', () => {
expect.assertions(2);
const tpl = compileTemplate(`
<template>
<button>click me</button>
</template>
`);

class MyComponent extends LightningElement {
renderedCallback() {
const button = this.template.querySelector('button');
const eventListener = {
handleEvent: function(evt) {
expect(this).toBe(eventListener);
expect(evt.target).toBe(button);
}
};
button.addEventListener('click', eventListener);
}

triggerInternalClick() {
this.template.querySelector('button').click();
}

render() {
return tpl;
}
}
MyComponent.publicMethods = ['triggerInternalClick'];
const elm = createElement('x-foo', { is: MyComponent });
document.body.appendChild(elm);
elm.triggerInternalClick();
});
});
});
2 changes: 1 addition & 1 deletion packages/@lwc/engine/src/faux-shadow/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ function isValidEventForCustomElement(event: Event): boolean {

export function addCustomElementEventListener(elm: HTMLElement, type: string, listener: EventListener, options?: boolean | AddEventListenerOptions) {
if (process.env.NODE_ENV !== 'production') {
assert.invariant(isFunction(listener), `Invalid second argument for this.template.addEventListener() in ${toString(elm)} for event "${type}". Expected an EventListener but received ${listener}.`);
assert.invariant(isFunction(listener), `Invalid second argument for this.addEventListener() in ${toString(elm)} for event "${type}". Expected an EventListener but received ${listener}.`);
// TODO: issue #420
// this is triggered when the component author attempts to add a listener programmatically into a lighting element node
if (!isUndefined(options)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ BaseLightningElement.prototype = {
if (process.env.NODE_ENV !== 'production') {
assert.isTrue(vm && "cmpRoot" in vm, `${vm} is not a vm.`);
assert.invariant(!isRendering, `${vmBeingRendered}.render() method has side effects on the state of ${vm} by adding an event listener for "${type}".`);
assert.invariant(isFunction(listener), `Invalid second argument for this.template.addEventListener() in ${vm} for event "${type}". Expected an EventListener but received ${listener}.`);
assert.invariant(isFunction(listener), `Invalid second argument for this.addEventListener() in ${vm} for event "${type}". Expected an EventListener but received ${listener}.`);
}
const wrappedListener = getWrappedComponentsListener(vm, listener);
vm.elm.addEventListener(type, wrappedListener, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ function getEventListenerWrapper(fnOrObj): EventListener | null {
try {
wrapperFn = fnOrObj.$$lwcEventWrapper$$;
if (!wrapperFn) {
const isHandlerFunction = typeof fnOrObj === 'function';
wrapperFn = fnOrObj.$$lwcEventWrapper$$ = function(this: EventTarget, e: Event) {
// we don't want to patch every event, only when the original target is coming
// from inside a synthetic shadow
if (doesEventNeedsPatch(e)) {
patchEvent(e);
}
return fnOrObj.call(this, e);
return isHandlerFunction ? fnOrObj.call(this, e) : fnOrObj.handleEvent && fnOrObj.handleEvent(e);
};
}
} catch (e) { /** ignore */ }
Expand Down

0 comments on commit adf3504

Please sign in to comment.