diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx index 7be0600292f..1ebcc1a50da 100644 --- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx +++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx @@ -3,7 +3,7 @@ import $ from 'jquery'; import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; import { Themeable, GrafanaThemeType } from '../../types/theme'; import { Threshold, BasicGaugeColor } from '../../types/panel'; -import { DisplayValue } from '../../utils/valueProcessor'; +import { DisplayValue } from '../../utils'; export interface Props extends Themeable { width: number; diff --git a/packages/grafana-ui/src/utils/valueProcessor.test.ts b/packages/grafana-ui/src/utils/displayValue.test.ts similarity index 86% rename from packages/grafana-ui/src/utils/valueProcessor.test.ts rename to packages/grafana-ui/src/utils/displayValue.test.ts index c32ca9389d2..61b55d36d3d 100644 --- a/packages/grafana-ui/src/utils/valueProcessor.test.ts +++ b/packages/grafana-ui/src/utils/displayValue.test.ts @@ -1,7 +1,7 @@ -import { getValueProcessor, getColorFromThreshold, ValueProcessor, DisplayValue } from './valueProcessor'; +import { getDisplayProcessor, getColorFromThreshold, DisplayProcessor, DisplayValue } from './displayValue'; import { MappingType, ValueMapping } from '../types/panel'; -function assertSame(input: any, processorss: ValueProcessor[], match: DisplayValue) { +function assertSame(input: any, processorss: DisplayProcessor[], match: DisplayValue) { processorss.forEach(processor => { const value = processor(input); expect(value.text).toEqual(match.text); @@ -15,13 +15,13 @@ describe('Process simple display values', () => { // Don't test float values here since the decimal formatting changes const processors = [ // Without options, this shortcuts to a much easier implementation - getValueProcessor(), + getDisplayProcessor(), // Add a simple option that is not used (uses a different base class) - getValueProcessor({ color: '#FFF' }), + getDisplayProcessor({ color: '#FFF' }), // Add a simple option that is not used (uses a different base class) - getValueProcessor({ unit: 'locale' }), + getDisplayProcessor({ unit: 'locale' }), ]; it('support null', () => { @@ -75,7 +75,7 @@ describe('Process simple display values', () => { describe('Processor with more configs', () => { it('support prefix & suffix', () => { - const processor = getValueProcessor({ + const processor = getDisplayProcessor({ prefix: 'AA_', suffix: '_ZZ', }); @@ -113,7 +113,7 @@ describe('Format value', () => { it('should return if value isNaN', () => { const valueMappings: ValueMapping[] = []; const value = 'N/A'; - const instance = getValueProcessor({ mappings: valueMappings }); + const instance = getDisplayProcessor({ mappings: valueMappings }); const result = instance(value); @@ -124,7 +124,7 @@ describe('Format value', () => { const valueMappings: ValueMapping[] = []; const value = '6'; - const instance = getValueProcessor({ mappings: valueMappings, decimals: 1 }); + const instance = getDisplayProcessor({ mappings: valueMappings, decimals: 1 }); const result = instance(value); @@ -137,7 +137,7 @@ describe('Format value', () => { { id: 1, operator: '', text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' }, ]; const value = '10'; - const instance = getValueProcessor({ mappings: valueMappings, decimals: 1 }); + const instance = getDisplayProcessor({ mappings: valueMappings, decimals: 1 }); const result = instance(value); @@ -150,7 +150,7 @@ describe('Format value', () => { { id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' }, ]; const value = '11'; - const instance = getValueProcessor({ mappings: valueMappings, decimals: 1 }); + const instance = getDisplayProcessor({ mappings: valueMappings, decimals: 1 }); expect(instance(value).text).toEqual('1-20'); }); diff --git a/packages/grafana-ui/src/utils/valueProcessor.ts b/packages/grafana-ui/src/utils/displayValue.ts similarity index 76% rename from packages/grafana-ui/src/utils/valueProcessor.ts rename to packages/grafana-ui/src/utils/displayValue.ts index 0fc92c00dac..f8af6c604f3 100644 --- a/packages/grafana-ui/src/utils/valueProcessor.ts +++ b/packages/grafana-ui/src/utils/displayValue.ts @@ -4,18 +4,19 @@ import { getValueFormat, DecimalCount } from './valueFormats/valueFormats'; import { getMappedValue } from './valueMappings'; import { GrafanaTheme, GrafanaThemeType } from '../types/theme'; import { getColorFromHexRgbOrName } from './namedColorsPalette'; +import moment from 'moment'; export interface DisplayValue { - text: string; // How the value should be displayed - numeric: number; // Use isNaN to check if it actually is a number - color?: string; // suggested color + text: string; // Show in the UI + numeric: number; // Use isNaN to check if it is a real number + color?: string; // color based on configs or Threshold } export interface DisplayValueOptions { unit?: string; decimals?: DecimalCount; scaledDecimals?: DecimalCount; - isUtc?: boolean; + dateFormat?: string; // If set try to convert numbers to date color?: string; mappings?: ValueMapping[]; @@ -23,13 +24,17 @@ export interface DisplayValueOptions { prefix?: string; suffix?: string; + // Alternative to empty string noValue?: string; + + // Context + isUtc?: boolean; theme?: GrafanaTheme; // Will pick 'dark' if not defined } -export type ValueProcessor = (value: any) => DisplayValue; +export type DisplayProcessor = (value: any) => DisplayValue; -export function getValueProcessor(options?: DisplayValueOptions): ValueProcessor { +export function getDisplayProcessor(options?: DisplayValueOptions): DisplayProcessor { if (options && !_.isEmpty(options)) { const formatFunc = getValueFormat(options.unit || 'none'); return (value: any) => { @@ -52,6 +57,14 @@ export function getValueProcessor(options?: DisplayValueOptions): ValueProcessor } } + if (options.dateFormat) { + const date = toMoment(value, numeric, options.dateFormat); + if (date.isValid()) { + text = date.format(options.dateFormat); + shouldFormat = false; + } + } + if (!isNaN(numeric)) { if (shouldFormat && !_.isBoolean(value)) { text = formatFunc(numeric, options.decimals, options.scaledDecimals, options.isUtc); @@ -76,6 +89,20 @@ export function getValueProcessor(options?: DisplayValueOptions): ValueProcessor return toStringProcessor; } +function toMoment(value: any, numeric: number, format: string): moment.Moment { + if (!isNaN(numeric)) { + const v = moment(numeric); + if (v.isValid()) { + return v; + } + } + const v = moment(value, format); + if (v.isValid) { + return v; + } + return moment(value); // moment will try to parse the format +} + /** Will return any value as a number or NaN */ function toNumber(value: any): number { if (typeof value === 'number') { diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index a2acc828752..5f1099f18c7 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -3,5 +3,6 @@ export * from './valueFormats/valueFormats'; export * from './colors'; export * from './namedColorsPalette'; export * from './string'; +export * from './displayValue'; export * from './deprecationWarning'; export { getMappedValue } from './valueMappings'; diff --git a/public/app/plugins/panel/gauge/DisplayValueEditor.tsx b/public/app/plugins/panel/gauge/DisplayValueEditor.tsx index 51c956a9529..94660de85af 100644 --- a/public/app/plugins/panel/gauge/DisplayValueEditor.tsx +++ b/public/app/plugins/panel/gauge/DisplayValueEditor.tsx @@ -5,7 +5,7 @@ import React, { PureComponent } from 'react'; import { FormField, FormLabel, PanelOptionsGroup, UnitPicker } from '@grafana/ui'; // Types -import { DisplayValueOptions } from '@grafana/ui/src/utils/valueProcessor'; +import { DisplayValueOptions } from '@grafana/ui'; const labelWidth = 6; diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index b3af5c7ef75..5e2a32f4f35 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -9,8 +9,7 @@ import { Gauge } from '@grafana/ui'; // Types import { GaugeOptions } from './types'; -import { PanelProps, NullValueMode, BasicGaugeColor } from '@grafana/ui/src/types'; -import { DisplayValue, getValueProcessor } from '@grafana/ui/src/utils/valueProcessor'; +import { PanelProps, NullValueMode, BasicGaugeColor, DisplayValue, getDisplayProcessor } from '@grafana/ui'; interface Props extends PanelProps {} interface State { @@ -42,7 +41,7 @@ export class GaugePanel extends Component { const prefix = replaceVariables(display.prefix); const suffix = replaceVariables(display.suffix); - return getValueProcessor({ + return getDisplayProcessor({ color: BasicGaugeColor.Red, // The default color ...display, prefix, diff --git a/public/app/plugins/panel/gauge/GaugePanelEditor.tsx b/public/app/plugins/panel/gauge/GaugePanelEditor.tsx index db64c00f241..e68743ddd88 100644 --- a/public/app/plugins/panel/gauge/GaugePanelEditor.tsx +++ b/public/app/plugins/panel/gauge/GaugePanelEditor.tsx @@ -12,7 +12,7 @@ import { SingleStatValueEditor } from 'app/plugins/panel/gauge/SingleStatValueEd import { GaugeOptionsBox } from './GaugeOptionsBox'; import { GaugeOptions } from './types'; import { DisplayValueEditor } from './DisplayValueEditor'; -import { DisplayValueOptions } from '@grafana/ui/src/utils/valueProcessor'; +import { DisplayValueOptions } from '@grafana/ui'; export class GaugePanelEditor extends PureComponent> { onDisplayOptionsChanged = (displayOptions: DisplayValueOptions) => diff --git a/public/app/plugins/panel/gauge/types.ts b/public/app/plugins/panel/gauge/types.ts index c90b254cdc4..effcf195e27 100644 --- a/public/app/plugins/panel/gauge/types.ts +++ b/public/app/plugins/panel/gauge/types.ts @@ -1,5 +1,4 @@ -import { Threshold, ValueMapping } from '@grafana/ui'; -import { DisplayValueOptions } from '@grafana/ui/src/utils/valueProcessor'; +import { Threshold, ValueMapping, DisplayValueOptions } from '@grafana/ui'; export interface GaugeOptions { maxValue: number;