Skip to content

Commit

Permalink
Merge pull request #222 from xjmdoo/feature/single-observer
Browse files Browse the repository at this point in the history
Use single ResizeObserver for better performance
  • Loading branch information
elwayman02 authored Mar 3, 2021
2 parents 8b4069e + 6b8600a commit 50066f2
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 55 deletions.
57 changes: 34 additions & 23 deletions addon/modifiers/did-resize.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,46 @@ export default class DidResizeModifier extends Modifier {
options = {};

// Private API
observer = null;
static observer = null;
static handlers = null;

observe() {
if (this.observer) {
this.observer.observe(this.element, this.options);
constructor() {
super(...arguments);

if (!('ResizeObserver' in window)) {
return;
}

if (!DidResizeModifier.observer) {
DidResizeModifier.handlers = new WeakMap();
DidResizeModifier.observer = new ResizeObserver((entries, observer) => {
for (let entry of entries) {
const handler = DidResizeModifier.handlers.get(entry.target);
if (handler) handler(entry, observer);
}
});
}
}

unobserve() {
if (this.observer) {
this.observer.unobserve();
addHandler() {
DidResizeModifier.handlers.set(this.element, this.handler);
}

removeHandler() {
DidResizeModifier.handlers.delete(this.element);
}

observe() {
if (DidResizeModifier.observer) {
this.addHandler();
DidResizeModifier.observer.observe(this.element, this.options);
}
}

disconnect() {
if (this.observer) {
this.observer.disconnect();
unobserve() {
if (DidResizeModifier.observer) {
DidResizeModifier.observer.unobserve(this.element);
this.removeHandler();
}
}

Expand All @@ -41,19 +64,7 @@ export default class DidResizeModifier extends Modifier {
this.observe();
}

didInstall() {
if (!('ResizeObserver' in window)) {
return;
}

this.observer = new ResizeObserver((entries, observer) => {
this.handler(entries[0], observer);
});

this.observe();
}

willRemove() {
this.disconnect();
this.unobserve();
}
}
111 changes: 79 additions & 32 deletions tests/integration/modifiers/did-resize-test.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { find, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';

let resizeCallback;
let observeStub;
let unobserveStub;
let disconnectStub;
let MockResizeObserver;
import DidResizeModifier from 'ember-resize-modifier/modifiers/did-resize';

module('Integration | Modifier | did-resize', function (hooks) {
setupRenderingTest(hooks);

let resizeObserver;
let resizeCallback = null;
let observeStub = sinon.stub();
let unobserveStub = sinon.stub();
let disconnectStub = sinon.stub();
let resizeObserver = window.ResizeObserver;
let mockResizeObserver = class MockResizeObserver {
constructor(callback) {
resizeCallback = callback;
}

observe = observeStub;
unobserve = unobserveStub;
disconnect = disconnectStub;
};

hooks.beforeEach(function () {
resizeCallback = null;
observeStub = sinon.stub();
unobserveStub = sinon.stub();
disconnectStub = sinon.stub();

MockResizeObserver = class MockResizeObserver {
constructor(callback) {
resizeCallback = callback;
}

observe = observeStub;
unobserve = unobserveStub;
disconnect = disconnectStub;
};

resizeObserver = window.ResizeObserver;
window.ResizeObserver = MockResizeObserver;

observeStub.reset();
unobserveStub.reset();
disconnectStub.reset();
this.resizeStub = sinon.stub();
window.ResizeObserver = mockResizeObserver;

// reset static properties to make sure every test case runs independently
DidResizeModifier.observer = null;
DidResizeModifier.handlers = null;
resizeCallback = null;
});

hooks.afterEach(function () {
Expand All @@ -58,15 +57,16 @@ module('Integration | Modifier | did-resize', function (hooks) {
});

test('modifier triggers handler when ResizeObserver fires callback', async function (assert) {
await render(hbs`<div {{did-resize this.resizeStub}}></div>`);

let fakeEntry = { target: {} };
await render(
hbs`<div id="test-element" {{did-resize this.resizeStub}}></div>`
);
let entry = { target: find('#test-element') };
let fakeObserver = { observe: {} };

resizeCallback([fakeEntry], fakeObserver);
resizeCallback([entry], fakeObserver);

assert.ok(
this.resizeStub.calledOnceWith(fakeEntry, fakeObserver),
this.resizeStub.calledOnceWith(entry, fakeObserver),
'handler fired with correct parameters'
);
});
Expand All @@ -86,7 +86,6 @@ module('Integration | Modifier | did-resize', function (hooks) {
delete window.ResizeObserver;

await render(hbs`<div {{did-resize this.resizeStub}}></div>`);

assert.notOk(resizeCallback, 'no callback received');
assert.notOk(observeStub.calledOnce, 'observe was not called');
});
Expand All @@ -102,4 +101,52 @@ module('Integration | Modifier | did-resize', function (hooks) {
assert.ok(unobserveStub.calledOnce, 'unobserve called');
assert.ok(observeStub.calledTwice, 'observe was called again');
});

test('handlers are setup and called correctly when multiple modifiers are present', async function (assert) {
this.setProperties({
resizeStub2: sinon.stub(),
resizeStub3: sinon.stub(),
});

await render(
hbs`<div id="test-element1" {{did-resize this.resizeStub}}></div>
<div id="test-element2" {{did-resize this.resizeStub2}}></div>
<div id="test-element3" {{did-resize this.resizeStub3}}></div>`
);

let entries = ['#test-element1', '#test-element2', '#test-element3'].map(
(elementId) => {
return { target: find(elementId) };
}
);
let fakeObserver = { observe: {} };

// trigger resize on all elements
resizeCallback(entries, fakeObserver);

assert.ok(this.resizeStub.calledOnce, 'First handler was called only once');
assert.ok(
this.resizeStub2.calledOnce,
'Second handler was called only once'
);
assert.ok(
this.resizeStub3.calledOnce,
'Third handler was called only once'
);

// trigger resize only on the first element
resizeCallback([entries[0]], fakeObserver);
assert.ok(
this.resizeStub.calledTwice,
'First handler was called a second time'
);
assert.notOk(
this.resizeStub2.calledTwice,
'Second handler was not called a second time'
);
assert.notOk(
this.resizeStub3.calledTwice,
'Third handler was not called a second time'
);
});
});

0 comments on commit 50066f2

Please sign in to comment.