Commit 4e421196 by Ryan McKinley Committed by GitHub

GraphNG: time range should match the panel timeRange (#29596)

* match time range

* less logging

* almost working

* more logging

* more logging

* fix snapshot

* Use layout effect for the time range ref update

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
parent c869ae57
import React, { useCallback, useMemo, useRef } from 'react';
import React, { useCallback, useLayoutEffect, useMemo, useRef } from 'react';
import {
compareDataFrameStructures,
DataFrame,
......@@ -8,6 +8,7 @@ import {
formattedValueToString,
getFieldColorModeForField,
getFieldDisplayName,
TimeRange,
} from '@grafana/data';
import { alignDataFrames } from './utils';
import { UPlotChart } from '../uPlot/Plot';
......@@ -58,11 +59,17 @@ export const GraphNG: React.FC<GraphNGProps> = ({
const compareFrames = useCallback((a?: DataFrame | null, b?: DataFrame | null) => {
if (a && b) {
return compareDataFrameStructures(a, b, ['min', 'max']);
return compareDataFrameStructures(a, b);
}
return false;
}, []);
// reference change will not triger re-render
const currentTimeRange = useRef<TimeRange>(timeRange);
useLayoutEffect(() => {
currentTimeRange.current = timeRange;
}, [timeRange]);
const configRev = useRevision(alignedFrame, compareFrames);
const configBuilder = useMemo(() => {
......@@ -78,6 +85,10 @@ export const GraphNG: React.FC<GraphNGProps> = ({
builder.addScale({
scaleKey: 'x',
isTime: true,
range: () => {
const r = currentTimeRange.current!;
return [r.from.valueOf(), r.to.valueOf()];
},
});
builder.addAxis({
scaleKey: 'x',
......
......@@ -4,8 +4,7 @@ import { buildPlotContext, PlotContext } from './context';
import { pluginLog } from './utils';
import { usePlotConfig } from './hooks';
import { AlignedFrameWithGapTest, PlotProps } from './types';
import { DataFrame, FieldType } from '@grafana/data';
import isNumber from 'lodash/isNumber';
import { DataFrame } from '@grafana/data';
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
import usePrevious from 'react-use/lib/usePrevious';
......@@ -62,7 +61,7 @@ export const UPlotChart: React.FC<PlotProps> = props => {
}
// 4. Otherwise, assume only data has changed and update uPlot data
updateData(props.data.frame, props.config, plotInstance.current, prepareData(props.data).data);
updateData(props.data.frame, props.config, plotInstance.current, prepareData(props.data));
}, [props, isConfigReady]);
// When component unmounts, clean the existing uPlot instance
......@@ -95,38 +94,15 @@ function initializePlot(data: AlignedDataWithGapTest, config: Options, el: HTMLD
return new uPlot(config, data, el);
}
function updateData(frame: DataFrame, config: UPlotConfigBuilder, plotInstance?: uPlot, data?: AlignedData | null) {
function updateData(
frame: DataFrame,
config: UPlotConfigBuilder,
plotInstance?: uPlot,
data?: AlignedDataWithGapTest | null
) {
if (!plotInstance || !data) {
return;
}
pluginLog('uPlot core', false, 'updating plot data(throttled log!)', data);
updateScales(frame, config, plotInstance);
plotInstance.setData(data);
}
function updateScales(frame: DataFrame, config: UPlotConfigBuilder, plotInstance: uPlot) {
let yRange: [number, number] | undefined = undefined;
for (let i = 0; i < frame.fields.length; i++) {
if (frame.fields[i].type !== FieldType.number) {
continue;
}
if (isNumber(frame.fields[i].config.min) && isNumber(frame.fields[i].config.max)) {
yRange = [frame.fields[i].config.min!, frame.fields[i].config.max!];
break;
}
}
const scalesConfig = config.getConfig().scales;
if (scalesConfig && yRange) {
for (const scale in scalesConfig) {
if (!scalesConfig.hasOwnProperty(scale)) {
continue;
}
if (scale !== 'x') {
plotInstance.setScale(scale, { min: yRange[0], max: yRange[1] });
}
}
}
}
......@@ -22,10 +22,12 @@ describe('UPlotConfigBuilder', () => {
"axes": Array [],
"scales": Object {
"scale-x": Object {
"range": [Function],
"time": true,
},
"scale-y": Object {
"range": [Function],
"time": false,
},
},
"series": Array [
......
......@@ -6,6 +6,7 @@ export interface ScaleProps {
isTime?: boolean;
min?: number | null;
max?: number | null;
range?: () => number[]; // min/max
}
export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
......@@ -14,22 +15,19 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
this.props.max = optMinMax('max', this.props.max, props.max);
}
// uPlot range function
range = (u: uPlot, dataMin: number, dataMax: number) => {
const { min, max } = this.props;
const [smin, smax] = uPlot.rangeNum(min ?? dataMin, max ?? dataMax, 0.1 as any, true);
return [min ?? smin, max ?? smax];
};
getConfig() {
const { isTime, scaleKey } = this.props;
if (isTime) {
return {
[scaleKey]: {
time: true, // TODO? this should be based on the query range, not the data
},
};
}
const { isTime, scaleKey, range } = this.props;
return {
[scaleKey]: {
range: (u: uPlot, dataMin: number, dataMax: number) => {
const { min, max } = this.props;
const [smin, smax] = uPlot.rangeNum(min ?? dataMin, max ?? dataMax, 0.1 as any, true);
return [min ?? smin, max ?? smax];
},
time: isTime,
range: range ?? this.range,
},
};
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment