GraphNG: make sure dataset and config are in sync when initializing and re-initializing uPlot (#32106)

* Make sure dataset and uPlot config are in sync when rendering uPlot

* Tests

* Fix TS
This commit is contained in:
Dominik Prokop
2021-03-18 17:26:57 +01:00
committed by GitHub
parent b2e82a4f37
commit a6cb9fb295
2 changed files with 82 additions and 9 deletions

View File

@@ -7,6 +7,7 @@ import uPlot from 'uplot';
import createMockRaf from 'mock-raf';
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
import { preparePlotData } from './utils';
import { SeriesProps } from './config/UPlotSeriesBuilder';
const mockRaf = createMockRaf();
const setDataMock = jest.fn();
@@ -52,7 +53,9 @@ const mockData = () => {
raw: { from: '1602673200000', to: '1602680400000' },
};
return { data, timeRange, config: new UPlotConfigBuilder() };
const config = new UPlotConfigBuilder();
config.addSeries({} as SeriesProps);
return { data, timeRange, config };
};
describe('UPlotChart', () => {
@@ -159,14 +162,11 @@ describe('UPlotChart', () => {
expect(uPlot).toBeCalledTimes(1);
const nextConfig = new UPlotConfigBuilder();
nextConfig.addSeries({} as SeriesProps);
rerender(
<UPlotChart
data={preparePlotData(data)}
config={new UPlotConfigBuilder()}
timeRange={timeRange}
width={100}
height={100}
/>
<UPlotChart data={preparePlotData(data)} config={nextConfig} timeRange={timeRange} width={100} height={100} />
);
expect(destroyMock).toBeCalledTimes(1);
@@ -190,11 +190,13 @@ describe('UPlotChart', () => {
act(() => {
mockRaf.step({ count: 1 });
});
const nextConfig = new UPlotConfigBuilder();
nextConfig.addSeries({} as SeriesProps);
rerender(
<UPlotChart
data={preparePlotData(data)} // frame
config={new UPlotConfigBuilder()}
config={nextConfig}
timeRange={timeRange}
width={200}
height={200}
@@ -205,5 +207,68 @@ describe('UPlotChart', () => {
expect(uPlot).toBeCalledTimes(1);
expect(setSizeMock).toBeCalledTimes(1);
});
it('does not initialize plot when config and data are not in sync', () => {
const { data, timeRange, config } = mockData();
// 1 series in data, 2 series in config
config.addSeries({} as SeriesProps);
render(
<UPlotChart
data={preparePlotData(data)} // frame
config={config}
timeRange={timeRange}
width={100}
height={100}
/>
);
// we wait 1 frame for plugins initialisation logic to finish
act(() => {
mockRaf.step({ count: 1 });
});
expect(destroyMock).toBeCalledTimes(0);
expect(uPlot).toBeCalledTimes(0);
});
it('does not reinitialize plot when config and data are not in sync', () => {
const { data, timeRange, config } = mockData();
// 1 series in data, 1 series in config
const { rerender } = render(
<UPlotChart
data={preparePlotData(data)} // frame
config={config}
timeRange={timeRange}
width={100}
height={100}
/>
);
// we wait 1 frame for plugins initialisation logic to finish
act(() => {
mockRaf.step({ count: 1 });
});
const nextConfig = new UPlotConfigBuilder();
nextConfig.addSeries({} as SeriesProps);
nextConfig.addSeries({} as SeriesProps);
// 1 series in data, 2 series in config
rerender(
<UPlotChart
data={preparePlotData(data)} // frame
config={nextConfig}
timeRange={timeRange}
width={200}
height={200}
/>
);
expect(destroyMock).toBeCalledTimes(0);
expect(uPlot).toBeCalledTimes(1);
});
});
});

View File

@@ -31,6 +31,14 @@ export const UPlotChart: React.FC<PlotProps> = (props) => {
return;
}
// 0. Exit if the data set length is different than number of series expected to render
// This may happen when GraphNG has not synced config yet with the aligned frame. Alignment happens before the render
// in the getDerivedStateFromProps, while the config creation happens in componentDidUpdate, causing one more render
// of the UPlotChart if the config needs to be updated.
if (currentConfig.current.series.length !== props.data.length) {
return;
}
// 1. When config is ready and there is no uPlot instance, create new uPlot and return
if (isConfigReady && !plotInstance.current) {
plotInstance.current = initializePlot(props.data, currentConfig.current, canvasRef.current);