Skip to content

Commit

Permalink
feat(esl-event-listener): support for ESLDomElementTarget in `ESLRe…
Browse files Browse the repository at this point in the history
…sizeObserverTarget` (#1573)
  • Loading branch information
fshovchko authored Apr 12, 2023
1 parent 6ae10e6 commit f177381
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 37 deletions.
21 changes: 13 additions & 8 deletions src/modules/esl-event-listener/core/targets/resize.adapter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {SyntheticEventTarget} from '../../../esl-utils/dom/events/target';
import {ESLElementResizeEvent} from './resize.adapter.event';

import {resolveDomTarget} from '../../../esl-utils/abstract/dom-target';
import type {ESLDomElementTarget} from '../../../esl-utils/abstract/dom-target';

/** Adapter class for {@link ResizeObserver} that implements {@link EventTarget} */
export class ESLResizeObserverTarget extends SyntheticEventTarget {
/** {@link ESLResizeObserverTarget} instances holder */
Expand All @@ -10,6 +13,9 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget {
changes.forEach(this.handleChange, this)
);

/** Observed {@link Element} of the {@link ESLResizeObserverTarget} instance */
public readonly target: Element;

/** Internal method to handle {@link ResizeObserver} entry change */
protected static handleChange(
this: typeof ESLResizeObserverTarget,
Expand All @@ -20,23 +26,22 @@ export class ESLResizeObserverTarget extends SyntheticEventTarget {
adapter.dispatchEvent(ESLElementResizeEvent.fromEntry(entry));
}

/** Creates {@link ESLResizeObserverTarget} instance for the {@link Element} */
public static create(target: Element): ESLResizeObserverTarget {
/** Creates {@link ESLResizeObserverTarget} instance for the {@link ESLDomElementTarget} */
public static create(target: ESLDomElementTarget): ESLResizeObserverTarget {
return new ESLResizeObserverTarget(target);
}

/**
* Creates {@link ESLResizeObserverTarget} for the {@link Element}.
* Creates {@link ESLResizeObserverTarget} for the {@link ESLDomElementTarget}.
* Note the {@link ESLResizeObserverTarget} instances are singletons relatively to the {@link Element}
*/
protected constructor(
/** Observed {@link Element} of the {@link ESLResizeObserverTarget} instance */
public readonly target: Element
) {
protected constructor(target: ESLDomElementTarget) {
target = resolveDomTarget(target);
const instance = ESLResizeObserverTarget.mapping.get(target);
if (instance) return instance;
super();
ESLResizeObserverTarget.mapping.set(target, this);
this.target = target;
ESLResizeObserverTarget.mapping.set(this.target, this);
}

/** Subscribes to the observed target {@link Element} changes */
Expand Down
117 changes: 88 additions & 29 deletions src/modules/esl-event-listener/test/targets/resize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,79 @@ describe('ESLEventUtils: ResizeObserver EventTarget adapter', () => {
});

describe('ESLResizeObserverTarget livecycle', () => {
const mock = getLastResizeObserverMock();
const el = document.createElement('div');
const target = ESLEventUtils.resize(el);

const cb1 = jest.fn();
const cb2 = jest.fn();

beforeEach(() => {
mock.observe.mockReset();
mock.unobserve.mockReset();
describe('Element as a taget', () => {
const mock = getLastResizeObserverMock();
const el = document.createElement('div');
const target = ESLEventUtils.resize(el);

const cb1 = jest.fn();
const cb2 = jest.fn();

beforeEach(() => {
mock.observe.mockReset();
mock.unobserve.mockReset();
});

test('Subscription produces ResizeObserver interaction', () => {
target.addEventListener(cb1);
expect(mock.observe).lastCalledWith(el);
expect(mock.unobserve).not.toBeCalled();
});
test('Second subscription does not produces ResizeObserver interaction', () => {
target.addEventListener(cb2);
expect(mock.observe).not.toBeCalled();
expect(mock.unobserve).not.toBeCalled();
});
test('Single unsubscription does not produces internal unsubscription', () => {
target.removeEventListener(cb2);
expect(mock.observe).not.toBeCalled();
expect(mock.unobserve).not.toBeCalled();
});
test('Removal of all of subscriptions produces internal unsubscription', () => {
target.removeEventListener(cb1);
expect(mock.observe).not.toBeCalled();
expect(mock.unobserve).toBeCalled();
});
});

test('Subscription produces ResizeObserver interaction', () => {
target.addEventListener(cb1);
expect(mock.observe).lastCalledWith(el);
expect(mock.unobserve).not.toBeCalled();
});
test('Second subscription does not produces ResizeObserver interaction', () => {
target.addEventListener(cb2);
expect(mock.observe).not.toBeCalled();
expect(mock.unobserve).not.toBeCalled();
});
test('Single unsubscription does not produces internal unsubscription', () => {
target.removeEventListener(cb2);
expect(mock.observe).not.toBeCalled();
expect(mock.unobserve).not.toBeCalled();
});
test('Remove of all of subscriptions produce internal unsubscription', () => {
target.removeEventListener(cb1);
expect(mock.observe).not.toBeCalled();
expect(mock.unobserve).toBeCalled();
describe('ESLDomElementRelated as a taget', () => {
const mock = getLastResizeObserverMock();
const el = document.createElement('div');
const mixin = {$host: el};
const target = ESLEventUtils.resize(el);
const targetMixin = ESLEventUtils.resize(mixin);

const cb1 = jest.fn();
const cb2 = jest.fn();

beforeEach(() => {
mock.observe.mockReset();
mock.unobserve.mockReset();
});

test('Should be only one instance for ESLResizeObserverTarget and it`s host', () => {
expect(targetMixin).toBe(target);
});
test('ESLDomElementRelated subscription produces ResizeObserver interaction', () => {
targetMixin.addEventListener(cb1);
expect(mock.observe).lastCalledWith(el);
expect(mock.unobserve).not.toBeCalled();
});
test('Repeated subscription of ESLDomElementRelated host does not produces ResizeObserver interaction', () => {
target.addEventListener(cb2);
expect(mock.observe).not.toBeCalled();
expect(mock.unobserve).not.toBeCalled();
});
test('Single unsubscription does not produces internal unsubscription', () => {
targetMixin.removeEventListener(cb1);
expect(mock.observe).not.toBeCalled();
expect(mock.unobserve).not.toBeCalled();
});
test('Removal of all of subscriptions produces internal unsubscription', () => {
target.removeEventListener(cb2);
expect(mock.observe).not.toBeCalled();
expect(mock.unobserve).toBeCalled();
});
});
});

Expand All @@ -56,6 +98,10 @@ describe('ESLEventUtils: ResizeObserver EventTarget adapter', () => {

const el1 = document.createElement('div');
const el2 = document.createElement('div');
const el3 = document.createElement('div');
const mixin = {
$host: el3
};

const cb1_1 = jest.fn();
ESLEventUtils.resize(el1).addEventListener(cb1_1);
Expand All @@ -69,6 +115,9 @@ describe('ESLEventUtils: ResizeObserver EventTarget adapter', () => {
ESLEventUtils.resize(el2).addEventListener(cb2_1);
expect(cb2_1).not.toBeCalled();

const cb3_1 = jest.fn();
ESLEventUtils.resize(mixin).addEventListener(cb3_1);

const fakeEntry = (el: Element) => ({
target: el,
contentRect: el.getBoundingClientRect(),
Expand All @@ -81,6 +130,7 @@ describe('ESLEventUtils: ResizeObserver EventTarget adapter', () => {
cb1_1.mockReset();
cb1_2.mockReset();
cb2_1.mockReset();
cb3_1.mockReset();
});

test('Dispatched change received by all subscribers', () => {
Expand All @@ -90,6 +140,7 @@ describe('ESLEventUtils: ResizeObserver EventTarget adapter', () => {
expect(cb1_1).toBeCalled();
expect(cb1_2).toBeCalled();
expect(cb2_1).not.toBeCalled();
expect(cb3_1).not.toBeCalled();
});

test('Dispatched change received by correct target', () => {
Expand All @@ -99,6 +150,7 @@ describe('ESLEventUtils: ResizeObserver EventTarget adapter', () => {
expect(cb2_1).toBeCalled();
expect(cb1_1).not.toBeCalled();
expect(cb1_2).not.toBeCalled();
expect(cb3_1).not.toBeCalled();
});

test('Dispatched change produces an Event instance', () => {
Expand Down Expand Up @@ -126,5 +178,12 @@ describe('ESLEventUtils: ResizeObserver EventTarget adapter', () => {
mock.callback.call(mock, [entry]);
expect(cb2_1).lastCalledWith(expect.objectContaining({target: el2}));
});

test('Dispatched change produces correct event target for ESLDomElementRelated', () => {
const entry: ResizeObserverEntry = fakeEntry(el3);

mock.callback.call(mock, [entry]);
expect(cb3_1).lastCalledWith(expect.objectContaining({target: el3}));
});
});
});

0 comments on commit f177381

Please # to comment.