Skip to content

Commit

Permalink
feat(mock-doc): add simple MockEvent#composedPath() impl (#3204)
Browse files Browse the repository at this point in the history
this commit adds a simplistic implementation of composedPath.
specifically, it inspects a MockEvent's target, and attempts to
climb the dom to provide the path the event propagates. this
implementation does not take into account closed shadow roots, nor
slots. the reason for which is that unlike a browser, which has far more
knowledge of the dom and event phases, stencil has none of this at the
mock-doc level, making a deep implementation difficult. a fuller
featured version may be considered for a future version of stencil

STENCIL-240: Enhance MockEvent to support composedPath()
  • Loading branch information
rwaskiewicz committed Jan 14, 2022
1 parent 10efeb6 commit 7b47d96
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 1 deletion.
21 changes: 21 additions & 0 deletions src/mock-doc/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ export class MockEvent {
stopImmediatePropagation() {
this.cancelBubble = true;
}

composedPath(): MockElement[] {
const composedPath: MockElement[] = [];

let currentElement = this.target;

while (currentElement) {
composedPath.push(currentElement);

if (!currentElement.parentElement && currentElement.nodeName === NODE_NAMES.DOCUMENT_NODE) {
// the current element doesn't have a parent, but we've detected it's our root document node. push the window
// object associated with the document onto the path
composedPath.push((currentElement as MockDocument).defaultView);
break;
}

currentElement = currentElement.parentElement;
}

return composedPath;
}
}

export class MockCustomEvent extends MockEvent {
Expand Down
59 changes: 58 additions & 1 deletion src/mock-doc/test/event.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { MockWindow } from '../window';
import { EventTarget } from '../event';
import { EventTarget, MockEvent } from '../event';
import { MockElement } from '../node';
import { MockDocument } from '../document';

describe('event', () => {
let win: MockWindow;
Expand Down Expand Up @@ -218,4 +220,59 @@ describe('event', () => {
expect(ev.buttons).toBe(99);
expect(ev.relatedTarget).toBe(null);
});

describe('composedPath', () => {
let doc: MockDocument;

beforeEach(() => {
doc = win.document as unknown as MockDocument;
});

it('returns an empty path with no target', () => {
const event = new MockEvent('onclick', { bubbles: true });
expect(event.composedPath()).toHaveLength(0);
});

it('returns the correct path for a simple div element', () => {
const event = new MockEvent('onclick', { bubbles: true });

const divElm = new MockElement(doc, 'div');
divElm.textContent = 'simple div text';
doc.body.appendChild(divElm);

divElm.dispatchEvent(event);

const composedPath = event.composedPath();

expect(composedPath).toHaveLength(5);
expect(composedPath[0]).toBe(divElm);
expect(composedPath[1]).toBe(doc.body);
expect(composedPath[2]).toBe(doc.documentElement);
expect(composedPath[3]).toBe(doc);
expect(composedPath[4]).toBe(win);
});

it('returns the correct path for a nested element', () => {
const event = new MockEvent('onclick', { bubbles: true });

const divElm = new MockElement(doc, 'div');
doc.body.appendChild(divElm);

const pElm = new MockElement(doc, 'p');
pElm.textContent = 'simple p text';
divElm.appendChild(pElm);

pElm.dispatchEvent(event);

const composedPath = event.composedPath();

expect(composedPath).toHaveLength(6);
expect(composedPath[0]).toBe(pElm);
expect(composedPath[1]).toBe(divElm);
expect(composedPath[2]).toBe(doc.body);
expect(composedPath[3]).toBe(doc.documentElement);
expect(composedPath[4]).toBe(doc);
expect(composedPath[5]).toBe(win);
});
});
});

0 comments on commit 7b47d96

Please sign in to comment.