diff --git a/docs/sources/features/panels/singlestat.md b/docs/sources/features/panels/singlestat.md index f6f06e87150..34a9bc1eb86 100644 --- a/docs/sources/features/panels/singlestat.md +++ b/docs/sources/features/panels/singlestat.md @@ -85,6 +85,8 @@ Gauges gives a clear picture of how high a value is in it's context. It's a grea Value/Range to text mapping allows you to translate the value of the summary stat into explicit text. The text will respect all styling, thresholds and customization defined for the value. This can be useful to translate the number of the main Singlestat value into a context-specific human-readable word or message. +If you want to replace the default "No data" text being displayed when no data is available, add a `value to text mapping` from `null` to your preferred custom text value. +
## Troubleshooting diff --git a/packages/grafana-data/src/field/fieldDisplay.test.ts b/packages/grafana-data/src/field/fieldDisplay.test.ts index 5d2920ba79a..c0c45cd6226 100644 --- a/packages/grafana-data/src/field/fieldDisplay.test.ts +++ b/packages/grafana-data/src/field/fieldDisplay.test.ts @@ -1,8 +1,10 @@ +import merge from 'lodash/merge'; import { getFieldProperties, getFieldDisplayValues, GetFieldDisplayValuesOptions } from './fieldDisplay'; import { toDataFrame } from '../dataframe/processDataFrame'; import { ReducerID } from '../transformations/fieldReducer'; import { Threshold } from '../types/threshold'; import { GrafanaTheme } from '../types/theme'; +import { MappingType } from '../types'; describe('FieldDisplay', () => { it('Construct simple field properties', () => { @@ -32,33 +34,8 @@ describe('FieldDisplay', () => { expect(field.unit).toEqual('ms'); }); - // Simple test dataset - - const options: GetFieldDisplayValuesOptions = { - data: [ - toDataFrame({ - name: 'Series Name', - fields: [ - { name: 'Field 1', values: ['a', 'b', 'c'] }, - { name: 'Field 2', values: [1, 3, 5] }, - { name: 'Field 3', values: [2, 4, 6] }, - ], - }), - ], - replaceVariables: (value: string) => { - return value; // Return it unchanged - }, - fieldOptions: { - calcs: [], - override: {}, - defaults: {}, - }, - theme: {} as GrafanaTheme, - }; - it('show first numeric values', () => { - const display = getFieldDisplayValues({ - ...options, + const options = createDisplayOptions({ fieldOptions: { calcs: [ReducerID.first], override: {}, @@ -67,28 +44,24 @@ describe('FieldDisplay', () => { }, }, }); + const display = getFieldDisplayValues(options); expect(display.map(v => v.display.text)).toEqual(['1', '2']); - // expect(display.map(v => v.display.title)).toEqual([ - // 'a * Field 1 * Series Name', // 0 - // 'b * Field 2 * Series Name', // 1 - // ]); }); it('show last numeric values', () => { - const display = getFieldDisplayValues({ - ...options, + const options = createDisplayOptions({ fieldOptions: { calcs: [ReducerID.last], override: {}, defaults: {}, }, }); + const display = getFieldDisplayValues(options); expect(display.map(v => v.display.numeric)).toEqual([5, 6]); }); it('show all numeric values', () => { - const display = getFieldDisplayValues({ - ...options, + const options = createDisplayOptions({ fieldOptions: { values: true, // limit: 1000, @@ -97,12 +70,12 @@ describe('FieldDisplay', () => { defaults: {}, }, }); + const display = getFieldDisplayValues(options); expect(display.map(v => v.display.numeric)).toEqual([1, 3, 5, 2, 4, 6]); }); it('show 2 numeric values (limit)', () => { - const display = getFieldDisplayValues({ - ...options, + const options = createDisplayOptions({ fieldOptions: { values: true, // limit: 2, @@ -111,6 +84,7 @@ describe('FieldDisplay', () => { defaults: {}, }, }); + const display = getFieldDisplayValues(options); expect(display.map(v => v.display.numeric)).toEqual([1, 3]); // First 2 are from the first field }); @@ -132,28 +106,108 @@ describe('FieldDisplay', () => { }); it('Should return field thresholds when there is no data', () => { - const options: GetFieldDisplayValuesOptions = { - data: [ - { - name: 'No data', - fields: [], - length: 0, - }, - ], - replaceVariables: (value: string) => { - return value; - }, + const options = createEmptyDisplayOptions({ fieldOptions: { - calcs: [], - override: {}, defaults: { thresholds: [{ color: '#F2495C', value: 50 }], }, }, - theme: {} as GrafanaTheme, - }; + }); const display = getFieldDisplayValues(options); expect(display[0].field.thresholds!.length).toEqual(1); + expect(display[0].display.numeric).toEqual(0); + }); + + it('Should return field with default text when no mapping or data available', () => { + const options = createEmptyDisplayOptions(); + const display = getFieldDisplayValues(options); + expect(display[0].display.text).toEqual('No data'); + expect(display[0].display.numeric).toEqual(0); + }); + + it('Should return field mapped value when there is no data', () => { + const mapEmptyToText = '0'; + const options = createEmptyDisplayOptions({ + fieldOptions: { + override: { + mappings: [ + { + id: 1, + operator: '', + text: mapEmptyToText, + type: MappingType.ValueToText, + value: 'null', + }, + ], + }, + }, + }); + + const display = getFieldDisplayValues(options); + expect(display[0].display.text).toEqual(mapEmptyToText); + expect(display[0].display.numeric).toEqual(0); + }); + + it('Should always return display numeric 0 when there is no data', () => { + const mapEmptyToText = '0'; + const options = createEmptyDisplayOptions({ + fieldOptions: { + override: { + mappings: [ + { + id: 1, + operator: '', + text: mapEmptyToText, + type: MappingType.ValueToText, + value: 'null', + }, + ], + }, + }, + }); + + const display = getFieldDisplayValues(options); + expect(display[0].display.numeric).toEqual(0); }); }); + +function createEmptyDisplayOptions(extend = {}): GetFieldDisplayValuesOptions { + const options = createDisplayOptions(extend); + + return Object.assign(options, { + data: [ + { + name: 'No data', + fields: [], + length: 0, + }, + ], + }); +} + +function createDisplayOptions(extend = {}): GetFieldDisplayValuesOptions { + const options: GetFieldDisplayValuesOptions = { + data: [ + toDataFrame({ + name: 'Series Name', + fields: [ + { name: 'Field 1', values: ['a', 'b', 'c'] }, + { name: 'Field 2', values: [1, 3, 5] }, + { name: 'Field 3', values: [2, 4, 6] }, + ], + }), + ], + replaceVariables: (value: string) => { + return value; + }, + fieldOptions: { + calcs: [], + override: {}, + defaults: {}, + }, + theme: {} as GrafanaTheme, + }; + + return merge