Skip to content

Commit

Permalink
refactor: add option to observe parent element (#3829) (#3835)
Browse files Browse the repository at this point in the history
Co-authored-by: Serhii Kulykov <iamkulykov@gmail.com>
  • Loading branch information
vaadin-bot and web-padawan committed May 12, 2022
1 parent c79a2ae commit 0360e7d
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 2 deletions.
6 changes: 6 additions & 0 deletions packages/component-base/src/resize-mixin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ export declare class ResizeMixinClass {
* Override the method to implement your own behavior.
*/
protected _onResize(contentRect: DOMRect): void;

/**
* When true, the parent element resize will be also observed.
* Override this getter and return `true` to enable this.
*/
protected readonly _observeParent: boolean;
}
46 changes: 45 additions & 1 deletion packages/component-base/src/resize-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
const observer = new ResizeObserver((entries) => {
setTimeout(() => {
entries.forEach((entry) => {
entry.target._onResize(entry.contentRect);
// Notify child resizables, if any
if (entry.target.resizables) {
entry.target.resizables.forEach((resizable) => {
resizable._onResize(entry.contentRect);
});
} else {
entry.target._onResize(entry.contentRect);
}
});
});
});
Expand All @@ -25,12 +32,49 @@ export const ResizeMixin = dedupingMixin(
connectedCallback() {
super.connectedCallback();
observer.observe(this);

if (this._observeParent) {
const parent = this.parentNode instanceof ShadowRoot ? this.parentNode.host : this.parentNode;

if (!parent.resizables) {
parent.resizables = new Set();
observer.observe(parent);
}

parent.resizables.add(this);
this.__parent = parent;
}
}

/** @protected */
disconnectedCallback() {
super.disconnectedCallback();
observer.unobserve(this);

const parent = this.__parent;
if (this._observeParent && parent) {
const resizables = parent.resizables;

if (resizables) {
resizables.delete(this);

if (resizables.size === 0) {
observer.unobserve(parent);
}
}

this.__parent = null;
}
}

/**
* When true, the parent element resize will be also observed.
* Override this getter and return `true` to enable this.
*
* @protected
*/
get _observeParent() {
return false;
}

/**
Expand Down
70 changes: 69 additions & 1 deletion packages/component-base/test/resize-mixin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
import { ResizeMixin } from '../src/resize-mixin.js';

describe('resize-mixin', () => {
let element;
let element, observeParent;

before(() => {
customElements.define(
Expand All @@ -22,6 +22,10 @@ describe('resize-mixin', () => {
`;
}

get _observeParent() {
return observeParent;
}

_onResize() {
this.__resolveResize?.();
}
Expand Down Expand Up @@ -69,4 +73,68 @@ describe('resize-mixin', () => {
expect(console.warn.args[0][0]).to.include('WARNING: Since Vaadin 23, notifyResize() is deprecated.');
});
});

describe('observe parent', () => {
let parent;

beforeEach(() => {
parent = fixtureSync('<div style="display: flex"></div>');
observeParent = true;
});

describe('light DOM', () => {
beforeEach(async () => {
parent.appendChild(element);
// Wait for the initial resize.
await element.nextResize();
});

it('should notify parent element resize', async () => {
parent.style.width = '100px';
await element.nextResize();
});

describe('multiple children', () => {
let sibling, spy1, spy2;

beforeEach(() => {
sibling = element.cloneNode(true);
parent.appendChild(sibling);

spy1 = sinon.spy(element, '_onResize');
spy2 = sinon.spy(sibling, '_onResize');
});

it('should notify resize once per element', async () => {
parent.style.width = '100px';
await aTimeout(20);
expect(spy1.calledOnce).to.be.true;
expect(spy2.calledOnce).to.be.true;
});

it('should not notify element when detached', async () => {
parent.removeChild(element);

parent.style.width = '100px';
await aTimeout(20);
expect(spy1.called).to.be.false;
expect(spy2.calledOnce).to.be.true;
});
});
});

describe('shadow DOM', () => {
beforeEach(async () => {
parent.attachShadow({ mode: 'open' });
parent.shadowRoot.appendChild(element);
// Wait for the initial resize.
await element.nextResize();
});

it('should notify shadow host resize', async () => {
parent.style.width = '100px';
await element.nextResize();
});
});
});
});

0 comments on commit 0360e7d

Please sign in to comment.