diff --git a/packages/grafana-runtime/src/components/PanelDataErrorView.tsx b/packages/grafana-runtime/src/components/PanelDataErrorView.tsx index aac2f9ee304..25e577d6712 100644 --- a/packages/grafana-runtime/src/components/PanelDataErrorView.tsx +++ b/packages/grafana-runtime/src/components/PanelDataErrorView.tsx @@ -12,6 +12,7 @@ export interface PanelDataErrorViewProps { data: PanelData; needsTimeField?: boolean; needsNumberField?: boolean; + needsStringField?: boolean; // suggestions?: VisualizationSuggestion[]; <<< for sure optional } diff --git a/public/app/features/panel/components/PanelDataErrorView.tsx b/public/app/features/panel/components/PanelDataErrorView.tsx index d838257fcf0..4520cc1ea09 100644 --- a/public/app/features/panel/components/PanelDataErrorView.tsx +++ b/public/app/features/panel/components/PanelDataErrorView.tsx @@ -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'; } diff --git a/public/app/plugins/panel/barchart/BarChartPanel.tsx b/public/app/plugins/panel/barchart/BarChartPanel.tsx index b83a75d155f..4e63d277b7e 100755 --- a/public/app/plugins/panel/barchart/BarChartPanel.tsx +++ b/public/app/plugins/panel/barchart/BarChartPanel.tsx @@ -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 {} /** * @alpha */ -export const BarChartPanel: React.FunctionComponent = ({ data, options, width, height, timeZone }) => { +export const BarChartPanel: React.FunctionComponent = ({ 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 = ({ data, options, w return options.tooltip; }, [options.tooltip, options.stacking]); - if (!frames || warn) { - return ( -
-

{warn ?? 'No data found in response'}

-
- ); + if (!frames) { + return ; } return ( diff --git a/public/app/plugins/panel/barchart/utils.test.ts b/public/app/plugins/panel/barchart/utils.test.ts index 94f35220d91..7e83c878d6e 100644 --- a/public/app/plugins/panel/barchart/utils.test.ts +++ b/public/app/plugins/panel/barchart/utils.test.ts @@ -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'); }); }); }); diff --git a/public/app/plugins/panel/barchart/utils.ts b/public/app/plugins/panel/barchart/utils.ts index f9d70febe08..a3ffd2448a9 100644 --- a/public/app/plugins/panel/barchart/utils.ts +++ b/public/app/plugins/panel/barchart/utils.ts @@ -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);