BarChart: fix stale bar values and x axis labels (#39188)

This commit is contained in:
Leon Sorokin 2021-09-14 12:16:43 -05:00 committed by GitHub
parent 063160aae2
commit df6b6e7a98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 22 additions and 5 deletions

View File

@ -210,7 +210,7 @@ export class GraphNG extends React.Component<GraphNGProps, GraphNGState> {
render() { render() {
const { width, height, children, timeRange, renderLegend } = this.props; const { width, height, children, timeRange, renderLegend } = this.props;
const { config, alignedFrame } = this.state; const { config, alignedFrame, alignedData } = this.state;
if (!config) { if (!config) {
return null; return null;
@ -220,8 +220,8 @@ export class GraphNG extends React.Component<GraphNGProps, GraphNGState> {
<VizLayout width={width} height={height} legend={renderLegend(config)}> <VizLayout width={width} height={height} legend={renderLegend(config)}>
{(vizWidth: number, vizHeight: number) => ( {(vizWidth: number, vizHeight: number) => (
<UPlotChart <UPlotChart
config={this.state.config!} config={config}
data={this.state.alignedData} data={alignedData}
width={vizWidth} width={vizWidth}
height={vizHeight} height={vizHeight}
timeRange={timeRange} timeRange={timeRange}

View File

@ -101,6 +101,14 @@ export class UPlotChart extends React.Component<PlotProps, UPlotChartState> {
this.reinitPlot(); this.reinitPlot();
} else if (!sameData(prevProps, this.props)) { } else if (!sameData(prevProps, this.props)) {
plot?.setData(this.props.data); plot?.setData(this.props.data);
// this is a uPlot cache-busting hack for bar charts in case x axis labels changed
// since the x scale's "range" doesnt change, the axis size doesnt get recomputed, which is where the tick labels are regenerated & cached
// the more expensive, more proper/thorough way to do this is to force all axes to recalc: plot?.redraw(false, true);
if (plot && typeof this.props.data[0][0] === 'string') {
//@ts-ignore
plot.axes[0]._values = this.props.data[0];
}
} else if (!sameTimeRange(prevProps, this.props)) { } else if (!sameTimeRange(prevProps, this.props)) {
plot?.setScale('x', { plot?.setScale('x', {
min: this.props.timeRange.from.valueOf(), min: this.props.timeRange.from.valueOf(),

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useRef } from 'react';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { DataFrame, TimeRange } from '@grafana/data'; import { DataFrame, TimeRange } from '@grafana/data';
import { GraphNG, GraphNGProps, PlotLegend, UPlotConfigBuilder, usePanelContext, useTheme2 } from '@grafana/ui'; import { GraphNG, GraphNGProps, PlotLegend, UPlotConfigBuilder, usePanelContext, useTheme2 } from '@grafana/ui';
@ -27,6 +27,9 @@ export const BarChart: React.FC<BarChartProps> = (props) => {
const theme = useTheme2(); const theme = useTheme2();
const { eventBus } = usePanelContext(); const { eventBus } = usePanelContext();
const frame0Ref = useRef<DataFrame>();
frame0Ref.current = props.frames[0];
const renderLegend = (config: UPlotConfigBuilder) => { const renderLegend = (config: UPlotConfigBuilder) => {
if (!config || props.legend.displayMode === LegendDisplayMode.Hidden) { if (!config || props.legend.displayMode === LegendDisplayMode.Hidden) {
return null; return null;
@ -35,6 +38,8 @@ export const BarChart: React.FC<BarChartProps> = (props) => {
return <PlotLegend data={props.frames} config={config} maxHeight="35%" maxWidth="60%" {...props.legend} />; return <PlotLegend data={props.frames} config={config} maxHeight="35%" maxWidth="60%" {...props.legend} />;
}; };
const rawValue = (seriesIdx: number, valueIdx: number) => frame0Ref.current!.fields[seriesIdx].values.get(valueIdx);
const prepConfig = (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => { const prepConfig = (alignedFrame: DataFrame, allFrames: DataFrame[], getTimeRange: () => TimeRange) => {
const { timeZone, orientation, barWidth, showValue, groupWidth, stacking, legend, tooltip, text } = props; const { timeZone, orientation, barWidth, showValue, groupWidth, stacking, legend, tooltip, text } = props;
@ -52,6 +57,7 @@ export const BarChart: React.FC<BarChartProps> = (props) => {
legend, legend,
tooltip, tooltip,
text, text,
rawValue,
allFrames: props.frames, allFrames: props.frames,
}); });
}; };

View File

@ -19,6 +19,7 @@ export interface BarChartOptions extends OptionsWithLegend, OptionsWithTooltip,
showValue: BarValueVisibility; showValue: BarValueVisibility;
barWidth: number; barWidth: number;
groupWidth: number; groupWidth: number;
rawValue: (seriesIdx: number, valueIdx: number) => number;
} }
/** /**

View File

@ -94,6 +94,7 @@ describe('BarChart utils', () => {
text: { text: {
valueSize: 10, valueSize: 10,
}, },
rawValue: (seriesIdx: number, valueIdx: number) => frame.fields[seriesIdx].values.get(valueIdx),
}; };
it.each([VizOrientation.Auto, VizOrientation.Horizontal, VizOrientation.Vertical])('orientation', (v) => { it.each([VizOrientation.Auto, VizOrientation.Horizontal, VizOrientation.Vertical])('orientation', (v) => {

View File

@ -45,6 +45,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<BarChartOptions> = ({
barWidth, barWidth,
stacking, stacking,
text, text,
rawValue,
}) => { }) => {
const builder = new UPlotConfigBuilder(); const builder = new UPlotConfigBuilder();
const defaultValueFormatter = (seriesIdx: number, value: any) => const defaultValueFormatter = (seriesIdx: number, value: any) =>
@ -67,7 +68,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<BarChartOptions> = ({
groupWidth, groupWidth,
barWidth, barWidth,
stacking, stacking,
rawValue: (seriesIdx: number, valueIdx: number) => frame.fields[seriesIdx].values.get(valueIdx), rawValue,
formatValue, formatValue,
text, text,
showValue, showValue,