Skip to content

Commit

Permalink
fix: use ResizeMixin to observe chart resize (#3463)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored Feb 17, 2022
1 parent fc67eeb commit bf60fed
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 23 deletions.
3 changes: 2 additions & 1 deletion packages/charts/src/vaadin-chart.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
import { Axis, Chart as HighchartsChart, ExtremesObject, Options, Point, Series } from 'highcharts';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

export type ChartCategories = Array<string> | { [key: number]: string };
Expand Down Expand Up @@ -418,7 +419,7 @@ export type ChartEventMap = HTMLElementEventMap & ChartCustomEventMap;
* @fires {CustomEvent} xaxes-extremes-set - Fired when when the minimum and maximum is set for the X axis.
* @fires {CustomEvent} yaxes-extremes-set - Fired when when the minimum and maximum is set for the Y axis.
*/
declare class Chart extends ThemableMixin(ElementMixin(HTMLElement)) {
declare class Chart extends ResizeMixin(ThemableMixin(ElementMixin(HTMLElement))) {
readonly options: Options;

/**
Expand Down
37 changes: 21 additions & 16 deletions packages/charts/src/vaadin-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { beforeNextRender } from '@polymer/polymer/lib/utils/render-status.js';
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
import Highcharts from 'highcharts/es-modules/masters/highstock.src.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { ChartSeries } from './vaadin-chart-series.js';

Expand Down Expand Up @@ -234,10 +235,11 @@ export function deepMerge(target, source) {
* @fires {CustomEvent} yaxes-extremes-set - Fired when when the minimum and maximum is set for the Y axis.
*
* @extends HTMLElement
* @mixes ResizeMixin
* @mixes ThemableMixin
* @mixes ElementMixin
*/
class Chart extends ElementMixin(ThemableMixin(PolymerElement)) {
class Chart extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement))) {
static get template() {
return html`
<style>
Expand Down Expand Up @@ -512,8 +514,6 @@ class Chart extends ElementMixin(ThemableMixin(PolymerElement)) {
beta: 15,
depth: 50
};

this.__mutationCallback = this.__mutationCallback.bind(this);
}

/** @protected */
Expand All @@ -532,9 +532,6 @@ class Chart extends ElementMixin(ThemableMixin(PolymerElement)) {
this._jsonConfigurationBuffer = null;
this.__initChart(options);
this.__addChildObserver();
const config = { attributes: true, characterData: true };
this.__mutationObserver = new MutationObserver(this.__mutationCallback);
this.__mutationObserver.observe(this, config);
});
}

Expand Down Expand Up @@ -918,24 +915,33 @@ class Chart extends ElementMixin(ThemableMixin(PolymerElement)) {
};
}

/** @private */
__reflow() {
/**
* Implements resize callback from `ResizeMixin`
* to reflow when the chart element is resized.
* @protected
* @override
*/
_onResize(contentRect) {
if (!this.configuration) {
return;
}
this.configuration.reflow();
}

/** @private */
__mutationCallback() {
const { height: componentHeight } = this.getBoundingClientRect();
const { chartHeight } = this.configuration;
const { height, width } = contentRect;
const { chartHeight, chartWidth } = this.configuration;

if (componentHeight !== chartHeight) {
if (height !== chartHeight || width !== chartWidth) {
this.__reflow();
}
}

/** @private */
__reflow() {
if (!this.configuration) {
return;
}
this.configuration.reflow();
}

/** @private */
__addChildObserver() {
this._childObserver = new FlattenedNodesObserver(this.$.slot, (info) => {
Expand Down Expand Up @@ -1055,7 +1061,6 @@ class Chart extends ElementMixin(ThemableMixin(PolymerElement)) {
/** @protected */
disconnectedCallback() {
super.disconnectedCallback();
this.__mutationObserver && this.__mutationObserver.disconnect();
this._childObserver && this._childObserver.disconnect();
}

Expand Down
66 changes: 61 additions & 5 deletions packages/charts/test/chart-element.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,27 @@ import { aTimeout, fixtureSync, nextFrame, nextRender, oneEvent } from '@vaadin/
import sinon from 'sinon';
import '../vaadin-chart.js';

/**
* Resolves once the function is invoked on the given object.
*/
function onceInvoked(object, functionName) {
return new Promise((resolve) => {
sinon.replace(object, functionName, (...args) => {
sinon.restore();
object[functionName](...args);
resolve();
});
});
}

/**
* Resolves once the ResizeObserver in AvatarGroup has processed a resize.
*/
async function onceResized(el) {
// Wait for the _onResize function to be invoked by the ResizeObserver
await onceInvoked(el, '_onResize');
}

describe('vaadin-chart', () => {
describe('custom element definition', () => {
let chart, tagName;
Expand Down Expand Up @@ -255,6 +276,29 @@ describe('vaadin-chart', () => {
});
});

describe('width', () => {
let chart;

beforeEach(async () => {
chart = fixtureSync('<vaadin-chart style="width: 400px"></vaadin-chart>');
await oneEvent(chart, 'chart-load');
});

it('should propagate height to the chart container', () => {
const rect = chart.$.chart.getBoundingClientRect();
expect(rect.width).to.be.equal(400);
expect(chart.configuration.chartWidth).to.be.equal(400);
});

it('should update container height on chart resize', async () => {
chart.style.width = '300px';
await oneEvent(chart, 'chart-redraw');
const rect = chart.$.chart.getBoundingClientRect();
expect(rect.width).to.be.equal(300);
expect(chart.configuration.chartWidth).to.be.equal(300);
});
});

describe('height', () => {
let chart;

Expand Down Expand Up @@ -283,7 +327,7 @@ describe('vaadin-chart', () => {

beforeEach(async () => {
layout = fixtureSync(`
<div style="display: flex; width: 1000px;">
<div style="display: flex; width: 1000px; height: 300px;">
<vaadin-chart>
<vaadin-chart-series values="[1,7,3,1,5,6]"></vaadin-chart-series>
</vaadin-chart>
Expand All @@ -292,23 +336,35 @@ describe('vaadin-chart', () => {
</vaadin-chart>
</div>
`);
charts = layout.querySelectorAll('vaadin-chart');
charts = Array.from(layout.querySelectorAll('vaadin-chart'));
await oneEvent(charts[0], 'chart-load');
});

it('should update chart size on window resize', async () => {
it('should update chart width when container width changes', async () => {
expect(layout.getBoundingClientRect().width).to.be.equal(1000);
expect(charts[0].configuration.chartWidth).to.be.equal(500);
expect(charts[1].configuration.chartWidth).to.be.equal(500);

layout.style.width = '500px';
window.dispatchEvent(new Event('resize'));
await onceResized(charts[0]);

await aTimeout(100);
expect(layout.getBoundingClientRect().width).to.be.equal(500);
expect(charts[0].configuration.chartWidth).to.be.equal(250);
expect(charts[1].configuration.chartWidth).to.be.equal(250);
});

it('should update chart height when container height changes', async () => {
expect(layout.getBoundingClientRect().height).to.be.equal(300);
expect(charts[0].configuration.chartHeight).to.be.equal(300);
expect(charts[1].configuration.chartHeight).to.be.equal(300);

layout.style.height = '200px';
await onceResized(charts[0]);

expect(layout.getBoundingClientRect().height).to.be.equal(200);
expect(charts[0].configuration.chartHeight).to.be.equal(200);
expect(charts[1].configuration.chartHeight).to.be.equal(200);
});
});

describe('reattach', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/charts/theme/vaadin-chart-base-theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ const chartBaseTheme = css`
}
/* https://github.com/highcharts/highcharts/issues/16282 */
/* without this __mutationCallback always calls __reflow */
/* without this the resize callback always calls __reflow */
ul[aria-hidden='false'] {
margin: 0px;
}
Expand Down

0 comments on commit bf60fed

Please sign in to comment.