From 38734366d1502d8b07735c6c1663737b3ed361c9 Mon Sep 17 00:00:00 2001 From: Oscar Kilhed Date: Fri, 11 Jun 2021 13:49:26 +0200 Subject: [PATCH] TimeSeries: Fix fill below for dataframes with name (#35453) Use original dataframes for matching config. --- packages/grafana-data/src/field/fieldState.ts | 2 +- .../src/components/GraphNG/GraphNG.tsx | 6 ++-- .../src/components/GraphNG/utils.test.ts | 1 + .../src/components/TimeSeries/TimeSeries.tsx | 4 +-- .../src/components/TimeSeries/utils.ts | 33 ++++++++++++++----- .../uPlot/config/UPlotConfigBuilder.ts | 1 + .../app/plugins/panel/barchart/BarChart.tsx | 3 +- .../app/plugins/panel/barchart/utils.test.ts | 3 ++ .../panel/state-timeline/TimelineChart.tsx | 3 +- 9 files changed, 39 insertions(+), 17 deletions(-) diff --git a/packages/grafana-data/src/field/fieldState.ts b/packages/grafana-data/src/field/fieldState.ts index 0e88d5be15e..1bbfde314d9 100644 --- a/packages/grafana-data/src/field/fieldState.ts +++ b/packages/grafana-data/src/field/fieldState.ts @@ -51,7 +51,7 @@ export function getFieldDisplayName(field: Field, frame?: DataFrame, allFrames?: } /** - * Get an appropriate display name. If the 'displayName' field config is set, use that + * Get an appropriate display name. If the 'displayName' field config is set, use that. */ function calculateFieldDisplayName(field: Field, frame?: DataFrame, allFrames?: DataFrame[]): string { const hasConfigTitle = field.config?.displayName && field.config?.displayName.length; diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx index 5ec89b7d7b6..763954e70fc 100755 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx @@ -38,7 +38,7 @@ export interface GraphNGProps extends Themeable2 { fields?: XYFieldMatchers; // default will assume timeseries data onLegendClick?: (event: GraphNGLegendEvent) => void; children?: (builder: UPlotConfigBuilder, alignedFrame: DataFrame) => React.ReactNode; - prepConfig: (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => UPlotConfigBuilder; + prepConfig: (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => UPlotConfigBuilder; propsToDiff?: string[]; preparePlotFrame?: (frames: DataFrame[], dimFields: XYFieldMatchers) => DataFrame; renderLegend: (config: UPlotConfigBuilder) => React.ReactElement | null; @@ -105,7 +105,7 @@ export class GraphNG extends React.Component { pluginLog('GraphNG', false, 'data prepared', state.alignedData); if (withConfig) { - state.config = props.prepConfig(alignedFrame, this.getTimeRange); + state.config = props.prepConfig(alignedFrame, this.props.frames, this.getTimeRange); pluginLog('GraphNG', false, 'config prepared', state.config); } } @@ -182,7 +182,7 @@ export class GraphNG extends React.Component { propsChanged; if (shouldReconfig) { - newState.config = this.props.prepConfig(newState.alignedFrame, this.getTimeRange); + newState.config = this.props.prepConfig(newState.alignedFrame, this.props.frames, this.getTimeRange); pluginLog('GraphNG', false, 'config recreated', newState.config); } } diff --git a/packages/grafana-ui/src/components/GraphNG/utils.test.ts b/packages/grafana-ui/src/components/GraphNG/utils.test.ts index 3d4751fbccb..1529dc05c29 100644 --- a/packages/grafana-ui/src/components/GraphNG/utils.test.ts +++ b/packages/grafana-ui/src/components/GraphNG/utils.test.ts @@ -198,6 +198,7 @@ describe('GraphNG utils', () => { getTimeRange: getDefaultTimeRange, eventBus: new EventBusSrv(), sync: DashboardCursorSync.Tooltip, + allFrames: [frame!], }).getConfig(); expect(result).toMatchSnapshot(); }); diff --git a/packages/grafana-ui/src/components/TimeSeries/TimeSeries.tsx b/packages/grafana-ui/src/components/TimeSeries/TimeSeries.tsx index 8b509d3bb77..ead1711993f 100644 --- a/packages/grafana-ui/src/components/TimeSeries/TimeSeries.tsx +++ b/packages/grafana-ui/src/components/TimeSeries/TimeSeries.tsx @@ -16,10 +16,10 @@ export class UnthemedTimeSeries extends React.Component { static contextType = PanelContextRoot; panelContext: PanelContext = {} as PanelContext; - prepConfig = (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => { + prepConfig = (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => { const { eventBus, sync } = this.context; const { theme, timeZone } = this.props; - return preparePlotConfigBuilder({ frame: alignedFrame, theme, timeZone, getTimeRange, eventBus, sync }); + return preparePlotConfigBuilder({ frame: alignedFrame, theme, timeZone, getTimeRange, eventBus, sync, allFrames }); }; renderLegend = (config: UPlotConfigBuilder) => { diff --git a/packages/grafana-ui/src/components/TimeSeries/utils.ts b/packages/grafana-ui/src/components/TimeSeries/utils.ts index 2b3dc0657ab..e7331791b5f 100644 --- a/packages/grafana-ui/src/components/TimeSeries/utils.ts +++ b/packages/grafana-ui/src/components/TimeSeries/utils.ts @@ -9,8 +9,8 @@ import { FieldType, formattedValueToString, getFieldColorModeForField, - getFieldDisplayName, getFieldSeriesColor, + getFieldDisplayName, } from '@grafana/data'; import { UPlotConfigBuilder, UPlotConfigPrepFn } from '../uPlot/config/UPlotConfigBuilder'; @@ -41,6 +41,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor getTimeRange, eventBus, sync, + allFrames, }) => { const builder = new UPlotConfigBuilder(timeZone); @@ -97,7 +98,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor const stackingGroups: Map = new Map(); - let indexByName: Map | undefined = undefined; + let indexByName: Map | undefined; for (let i = 1; i < frame.fields.length; i++) { const field = frame.fields[i]; @@ -150,11 +151,15 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor let { fillOpacity } = customConfig; - if (customConfig.fillBelowTo) { + if (customConfig.fillBelowTo && field.state?.origin) { if (!indexByName) { - indexByName = getNamesToFieldIndex(frame); + indexByName = getNamesToFieldIndex(frame, allFrames); } - const t = indexByName.get(getFieldDisplayName(field, frame)); + + const originFrame = allFrames[field.state.origin.frameIndex]; + const originField = originFrame.fields[field.state.origin.fieldIndex]; + + const t = indexByName.get(getFieldDisplayName(originField, originFrame, allFrames)); const b = indexByName.get(customConfig.fillBelowTo); if (isNumber(b) && isNumber(t)) { builder.addBand({ @@ -259,10 +264,20 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor return builder; }; -export function getNamesToFieldIndex(frame: DataFrame): Map { - const names = new Map(); +export function getNamesToFieldIndex(frame: DataFrame, allFrames: DataFrame[]): Map { + const originNames = new Map(); for (let i = 0; i < frame.fields.length; i++) { - names.set(getFieldDisplayName(frame.fields[i], frame), i); + const origin = frame.fields[i].state?.origin; + if (origin) { + originNames.set( + getFieldDisplayName( + allFrames[origin.frameIndex].fields[origin.fieldIndex], + allFrames[origin.frameIndex], + allFrames + ), + i + ); + } } - return names; + return originNames; } diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts index 6672233641e..6f5e34d5146 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts @@ -227,6 +227,7 @@ type UPlotConfigPrepOpts = {}> = { timeZone: TimeZone; getTimeRange: () => TimeRange; eventBus: EventBus; + allFrames: DataFrame[]; } & T; /** @alpha */ diff --git a/public/app/plugins/panel/barchart/BarChart.tsx b/public/app/plugins/panel/barchart/BarChart.tsx index 8e65a5f67df..737a60881f8 100644 --- a/public/app/plugins/panel/barchart/BarChart.tsx +++ b/public/app/plugins/panel/barchart/BarChart.tsx @@ -34,7 +34,7 @@ export const BarChart: React.FC = (props) => { return ; }; - const prepConfig = (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => { + const prepConfig = (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => { const { timeZone, orientation, barWidth, showValue, groupWidth, stacking, legend, tooltip, text } = props; return preparePlotConfigBuilder({ @@ -51,6 +51,7 @@ export const BarChart: React.FC = (props) => { legend, tooltip, text, + allFrames: props.frames, }); }; diff --git a/public/app/plugins/panel/barchart/utils.test.ts b/public/app/plugins/panel/barchart/utils.test.ts index 7dd17ec616e..f45f5e941c7 100644 --- a/public/app/plugins/panel/barchart/utils.test.ts +++ b/public/app/plugins/panel/barchart/utils.test.ts @@ -105,6 +105,7 @@ describe('BarChart utils', () => { timeZone: DefaultTimeZone, getTimeRange: getDefaultTimeRange, eventBus: new EventBusSrv(), + allFrames: [frame], }).getConfig(); expect(result).toMatchSnapshot(); }); @@ -119,6 +120,7 @@ describe('BarChart utils', () => { timeZone: DefaultTimeZone, getTimeRange: getDefaultTimeRange, eventBus: new EventBusSrv(), + allFrames: [frame], }).getConfig() ).toMatchSnapshot(); }); @@ -133,6 +135,7 @@ describe('BarChart utils', () => { timeZone: DefaultTimeZone, getTimeRange: getDefaultTimeRange, eventBus: new EventBusSrv(), + allFrames: [frame], }).getConfig() ).toMatchSnapshot(); }); diff --git a/public/app/plugins/panel/state-timeline/TimelineChart.tsx b/public/app/plugins/panel/state-timeline/TimelineChart.tsx index ae1ef810cc4..e0ac00abf45 100755 --- a/public/app/plugins/panel/state-timeline/TimelineChart.tsx +++ b/public/app/plugins/panel/state-timeline/TimelineChart.tsx @@ -35,7 +35,7 @@ export class TimelineChart extends React.Component { static contextType = PanelContextRoot; panelContext: PanelContext = {} as PanelContext; - prepConfig = (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => { + prepConfig = (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => { this.panelContext = this.context as PanelContext; const { eventBus } = this.panelContext; @@ -43,6 +43,7 @@ export class TimelineChart extends React.Component { frame: alignedFrame, getTimeRange, eventBus, + allFrames: this.props.frames, ...this.props, // When there is only one row, use the full space