diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx index d07cb0efa4c..8cfea11fcd2 100755 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx @@ -1,4 +1,4 @@ -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 = ({ 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); + useLayoutEffect(() => { + currentTimeRange.current = timeRange; + }, [timeRange]); + const configRev = useRevision(alignedFrame, compareFrames); const configBuilder = useMemo(() => { @@ -78,6 +85,10 @@ export const GraphNG: React.FC = ({ builder.addScale({ scaleKey: 'x', isTime: true, + range: () => { + const r = currentTimeRange.current!; + return [r.from.valueOf(), r.to.valueOf()]; + }, }); builder.addAxis({ scaleKey: 'x', diff --git a/packages/grafana-ui/src/components/uPlot/Plot.tsx b/packages/grafana-ui/src/components/uPlot/Plot.tsx index f461d8ffff9..cf924042db7 100755 --- a/packages/grafana-ui/src/components/uPlot/Plot.tsx +++ b/packages/grafana-ui/src/components/uPlot/Plot.tsx @@ -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 = 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] }); - } - } - } -} diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts index 397e650ee8b..d55ddd38d78 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts @@ -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 [ diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts index 16f2f2caef4..13709f605c3 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts @@ -6,6 +6,7 @@ export interface ScaleProps { isTime?: boolean; min?: number | null; max?: number | null; + range?: () => number[]; // min/max } export class UPlotScaleBuilder extends PlotConfigBuilder { @@ -14,22 +15,19 @@ export class UPlotScaleBuilder extends PlotConfigBuilder { 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, }, }; }