TimeSeries: Fix fill below for dataframes with name (#35453)

Use original dataframes for matching config.
This commit is contained in:
Oscar Kilhed
2021-06-11 13:49:26 +02:00
committed by GitHub
parent aa8e09d383
commit 38734366d1
9 changed files with 39 additions and 17 deletions

View File

@@ -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;

View File

@@ -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<GraphNGProps, GraphNGState> {
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<GraphNGProps, GraphNGState> {
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);
}
}

View File

@@ -198,6 +198,7 @@ describe('GraphNG utils', () => {
getTimeRange: getDefaultTimeRange,
eventBus: new EventBusSrv(),
sync: DashboardCursorSync.Tooltip,
allFrames: [frame!],
}).getConfig();
expect(result).toMatchSnapshot();
});

View File

@@ -16,10 +16,10 @@ export class UnthemedTimeSeries extends React.Component<TimeSeriesProps> {
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) => {

View File

@@ -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<string, number[]> = new Map();
let indexByName: Map<string, number> | undefined = undefined;
let indexByName: Map<string, number> | 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<string, number> {
const names = new Map<string, number>();
export function getNamesToFieldIndex(frame: DataFrame, allFrames: DataFrame[]): Map<string, number> {
const originNames = new Map<string, number>();
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;
}

View File

@@ -227,6 +227,7 @@ type UPlotConfigPrepOpts<T extends Record<string, any> = {}> = {
timeZone: TimeZone;
getTimeRange: () => TimeRange;
eventBus: EventBus;
allFrames: DataFrame[];
} & T;
/** @alpha */

View File

@@ -34,7 +34,7 @@ export const BarChart: React.FC<BarChartProps> = (props) => {
return <PlotLegend data={props.frames} config={config} maxHeight="35%" maxWidth="60%" {...props.legend} />;
};
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<BarChartProps> = (props) => {
legend,
tooltip,
text,
allFrames: props.frames,
});
};

View File

@@ -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();
});

View File

@@ -35,7 +35,7 @@ export class TimelineChart extends React.Component<TimelineProps> {
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<TimelineProps> {
frame: alignedFrame,
getTimeRange,
eventBus,
allFrames: this.props.frames,
...this.props,
// When there is only one row, use the full space