BarChart: Use new data error view component to show actions in panel edit (#42474)

This commit is contained in:
Torkel Ödegaard 2021-12-08 11:45:56 +01:00 committed by GitHub
parent a0333c1545
commit 8828d5e8d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 35 additions and 37 deletions

View File

@ -12,6 +12,7 @@ export interface PanelDataErrorViewProps {
data: PanelData;
needsTimeField?: boolean;
needsNumberField?: boolean;
needsStringField?: boolean;
// suggestions?: VisualizationSuggestion[]; <<< for sure optional
}

View File

@ -57,7 +57,7 @@ export function PanelDataErrorView(props: PanelDataErrorViewProps) {
}
function getMessageFor(
{ data, message, needsNumberField, needsTimeField }: PanelDataErrorViewProps,
{ data, message, needsNumberField, needsTimeField, needsStringField }: PanelDataErrorViewProps,
dataSummary: PanelDataSummary
): string {
if (message) {
@ -68,6 +68,10 @@ function getMessageFor(
return 'No data';
}
if (needsStringField && !dataSummary.hasStringField) {
return 'Data is missing a string field';
}
if (needsNumberField && !dataSummary.hasNumberField) {
return 'Data is missing a number field';
}

View File

@ -5,16 +5,17 @@ import { measureText, TooltipPlugin, UPLOT_AXIS_FONT_SIZE, useTheme2 } from '@gr
import { BarChartOptions } from './types';
import { BarChart } from './BarChart';
import { prepareGraphableFrames } from './utils';
import { PanelDataErrorView } from '@grafana/runtime';
interface Props extends PanelProps<BarChartOptions> {}
/**
* @alpha
*/
export const BarChartPanel: React.FunctionComponent<Props> = ({ data, options, width, height, timeZone }) => {
export const BarChartPanel: React.FunctionComponent<Props> = ({ data, options, width, height, timeZone, id }) => {
const theme = useTheme2();
const { frames, warn } = useMemo(() => prepareGraphableFrames(data?.series, theme, options), [data, theme, options]);
const frames = useMemo(() => prepareGraphableFrames(data?.series, theme, options), [data, theme, options]);
const orientation = useMemo(() => {
if (!options.orientation || options.orientation === VizOrientation.Auto) {
return width < height ? VizOrientation.Horizontal : VizOrientation.Vertical;
@ -48,12 +49,8 @@ export const BarChartPanel: React.FunctionComponent<Props> = ({ data, options, w
return options.tooltip;
}, [options.tooltip, options.stacking]);
if (!frames || warn) {
return (
<div className="panel-empty">
<p>{warn ?? 'No data found in response'}</p>
</div>
);
if (!frames) {
return <PanelDataErrorView panelId={id} data={data} needsStringField={true} needsNumberField={true} />;
}
return (

View File

@ -147,7 +147,7 @@ describe('BarChart utils', () => {
describe('prepareGraphableFrames', () => {
it('will warn when there is no data in the response', () => {
const result = prepareGraphableFrames([], createTheme(), { stacking: StackingMode.None } as any);
expect(result.warn).toEqual('No data in response');
expect(result).toBeNull();
});
it('will warn when there is no string field in the response', () => {
@ -158,8 +158,7 @@ describe('BarChart utils', () => {
],
});
const result = prepareGraphableFrames([df], createTheme(), { stacking: StackingMode.None } as any);
expect(result.warn).toEqual('Bar charts requires a string field');
expect(result.frames).toBeUndefined();
expect(result).toBeNull();
});
it('will warn when there are no numeric fields in the response', () => {
@ -170,8 +169,7 @@ describe('BarChart utils', () => {
],
});
const result = prepareGraphableFrames([df], createTheme(), { stacking: StackingMode.None } as any);
expect(result.warn).toEqual('No numeric fields found');
expect(result.frames).toBeUndefined();
expect(result).toBeNull();
});
it('will convert NaN and Infinty to nulls', () => {
@ -181,9 +179,9 @@ describe('BarChart utils', () => {
{ name: 'value', values: [-10, NaN, 10, -Infinity, +Infinity] },
],
});
const result = prepareGraphableFrames([df], createTheme(), { stacking: StackingMode.None } as any);
const frames = prepareGraphableFrames([df], createTheme(), { stacking: StackingMode.None } as any)!;
const field = result.frames![0].fields[1];
const field = frames[0].fields[1];
expect(field!.values.toArray()).toMatchInlineSnapshot(`
Array [
-10,
@ -205,21 +203,23 @@ describe('BarChart utils', () => {
],
});
const resultAsc = prepareGraphableFrames([frame], createTheme(), {
const framesAsc = prepareGraphableFrames([frame], createTheme(), {
legend: { sortBy: 'Min', sortDesc: false },
} as any);
expect(resultAsc.frames![0].fields[0].type).toBe(FieldType.string);
expect(resultAsc.frames![0].fields[1].name).toBe('a');
expect(resultAsc.frames![0].fields[2].name).toBe('c');
expect(resultAsc.frames![0].fields[3].name).toBe('b');
} as any)!;
const resultDesc = prepareGraphableFrames([frame], createTheme(), {
expect(framesAsc[0].fields[0].type).toBe(FieldType.string);
expect(framesAsc[0].fields[1].name).toBe('a');
expect(framesAsc[0].fields[2].name).toBe('c');
expect(framesAsc[0].fields[3].name).toBe('b');
const framesDesc = prepareGraphableFrames([frame], createTheme(), {
legend: { sortBy: 'Min', sortDesc: true },
} as any);
expect(resultDesc.frames![0].fields[0].type).toBe(FieldType.string);
expect(resultDesc.frames![0].fields[1].name).toBe('b');
expect(resultDesc.frames![0].fields[2].name).toBe('c');
expect(resultDesc.frames![0].fields[3].name).toBe('a');
} as any)!;
expect(framesDesc[0].fields[0].type).toBe(FieldType.string);
expect(framesDesc[0].fields[1].name).toBe('b');
expect(framesDesc[0].fields[2].name).toBe('c');
expect(framesDesc[0].fields[3].name).toBe('a');
});
});
});

View File

@ -298,24 +298,20 @@ export function prepareGraphableFrames(
series: DataFrame[],
theme: GrafanaTheme2,
options: BarChartOptions
): { frames?: DataFrame[]; warn?: string } {
): DataFrame[] | null {
if (!series?.length) {
return { warn: 'No data in response' };
return null;
}
const frames: DataFrame[] = [];
const firstFrame = series[0];
if (!firstFrame.fields.some((f) => f.type === FieldType.string)) {
return {
warn: 'Bar charts requires a string field',
};
return null;
}
if (!firstFrame.fields.some((f) => f.type === FieldType.number)) {
return {
warn: 'No numeric fields found',
};
return null;
}
const legendOrdered = isLegendOrdered(options.legend);
@ -384,7 +380,7 @@ export function prepareGraphableFrames(
});
}
return { frames };
return frames;
}
export const isLegendOrdered = (options: VizLegendOptions) => Boolean(options?.sortBy && options.sortDesc !== null);