diff --git a/packages/grafana-data/src/field/displayProcessor.test.ts b/packages/grafana-data/src/field/displayProcessor.test.ts index 77415af38c1..281b755cc3d 100644 --- a/packages/grafana-data/src/field/displayProcessor.test.ts +++ b/packages/grafana-data/src/field/displayProcessor.test.ts @@ -305,7 +305,7 @@ describe('Format value', () => { const value = 1200; const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' }); const disp = instance(value); - expect(disp.text).toEqual('1.2'); + expect(disp.text).toEqual('1.20'); expect(disp.suffix).toEqual(' K'); }); @@ -329,7 +329,7 @@ describe('Format value', () => { const value = 1500000; const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' }); const disp = instance(value); - expect(disp.text).toEqual('1.5'); + expect(disp.text).toEqual('1.50'); expect(disp.suffix).toEqual(' Mil'); }); @@ -376,6 +376,53 @@ describe('Format value', () => { expect(disp.text).toEqual(value); }); }); + + describe('number formatting for y axis ticks (dynamic decimals with trailing 0s trimming)', () => { + // all these tests have non-null adjacentDecimals != null, which we only do durink axis tick formatting + + it('should trim trailing zeros after decimal from fractional seconds when formatted as millis with adjacentDecimals=2', () => { + const processor = getDisplayProcessorFromConfig({ unit: 's' }, FieldType.number); + expect(processor(0.06, 2).text).toEqual('60'); + }); + + it('should trim trailing zeros after decimal from number', () => { + const processor = getDisplayProcessorFromConfig({}, FieldType.number); + expect(processor(1.2, 2).text).toEqual('1.2'); + + // dynamic! + expect(processor(13.50008, 3).text).toEqual('13.5'); + }); + + it('should not attempt to trim zeros from currency*', () => { + const processor = getDisplayProcessorFromConfig({ unit: 'currencyUSD' }, FieldType.number); + expect(processor(1.2, 2).text).toEqual('1.20'); + }); + + it('should not attempt to trim zeros from bool', () => { + const processor = getDisplayProcessorFromConfig({ unit: 'bool' }, FieldType.number); + expect(processor(1, 2).text).toEqual('True'); + }); + + it('should not attempt to trim zeros from time', () => { + const processor = getDisplayProcessorFromConfig({}, FieldType.time); + expect(processor(1666402869517, 2).text).toEqual('2022-10-21 20:41:09'); + }); + + it('should not attempt to trim zeros from dateTimeAsUS', () => { + const processor = getDisplayProcessorFromConfig({ unit: 'dateTimeAsUS' }, FieldType.number); + expect(processor(1666402869517, 2).text).toEqual('10/21/2022 8:41:09 pm'); + }); + + it('should not attempt to trim zeros from locale', () => { + const processor = getDisplayProcessorFromConfig({ unit: 'locale' }, FieldType.number); + expect(processor(3500000, 2).text).toEqual('3,500,000'); + }); + + it('should not attempt to trim zeros when explicit decimals: 5', () => { + const processor = getDisplayProcessorFromConfig({ decimals: 5 }, FieldType.number); + expect(processor(35, 2).text).toEqual('35.00000'); + }); + }); }); describe('Date display options', () => { diff --git a/packages/grafana-data/src/field/displayProcessor.ts b/packages/grafana-data/src/field/displayProcessor.ts index 055ad7baa89..0761d29897d 100644 --- a/packages/grafana-data/src/field/displayProcessor.ts +++ b/packages/grafana-data/src/field/displayProcessor.ts @@ -10,7 +10,7 @@ import { Field, FieldType } from '../types/dataFrame'; import { DecimalCount, DisplayProcessor, DisplayValue } from '../types/displayValue'; import { anyToNumber } from '../utils/anyToNumber'; import { getValueMappingResult } from '../utils/valueMappings'; -import { getValueFormat, isBooleanUnit } from '../valueFormats/valueFormats'; +import { FormattedValue, getValueFormat, isBooleanUnit } from '../valueFormats/valueFormats'; import { getScaleCalculator } from './scale'; @@ -72,16 +72,17 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP unit = 'string'; } + const hasCurrencyUnit = unit?.startsWith('currency'); const hasBoolUnit = unit === 'bool'; const isNumType = field.type === FieldType.number; const isLocaleFormat = unit === 'locale'; - const shouldTrimTrailingDecimalZeros = - !hasDateUnit && !hasBoolUnit && !isLocaleFormat && isNumType && config.decimals == null; + const canTrimTrailingDecimalZeros = + !hasDateUnit && !hasCurrencyUnit && !hasBoolUnit && !isLocaleFormat && isNumType && config.decimals == null; const formatFunc = getValueFormat(unit || 'none'); const scaleFunc = getScaleCalculator(field, options.theme); - return (value: any, decimals?: DecimalCount) => { + return (value: any, adjacentDecimals?: DecimalCount) => { const { mappings } = config; const isStringUnit = unit === 'string'; @@ -117,14 +118,18 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP if (!Number.isNaN(numeric)) { if (text == null && !isBoolean(value)) { - const v = formatFunc(numeric, decimals ?? config.decimals, null, options.timeZone, showMs); + let v: FormattedValue; - // if no explicit decimals config, we strip trailing zeros e.g. 60.00 -> 60 - // this is needed because we may have determined the minimum required `decimals` for y tick increments based on - // e.g. 'seconds' field unit (0.15s, 0.20s, 0.25s), but then formatFunc decided to return milli or nanos (150, 200, 250) - // so we end up with excess precision: 150.00, 200.00, 250.00 - if (shouldTrimTrailingDecimalZeros) { + if (canTrimTrailingDecimalZeros && adjacentDecimals != null) { + v = formatFunc(numeric, adjacentDecimals, null, options.timeZone, showMs); + + // if no explicit decimals config, we strip trailing zeros e.g. 60.00 -> 60 + // this is needed because we may have determined the minimum determined `adjacentDecimals` for y tick increments based on + // e.g. 'seconds' field unit (0.15s, 0.20s, 0.25s), but then formatFunc decided to return milli or nanos (150, 200, 250) + // so we end up with excess precision: 150.00, 200.00, 250.00 v.text = +v.text + ''; + } else { + v = formatFunc(numeric, config.decimals, null, options.timeZone, showMs); } text = v.text; diff --git a/packages/grafana-ui/src/components/TimeSeries/utils.ts b/packages/grafana-ui/src/components/TimeSeries/utils.ts index 16414194202..10b86c3322c 100644 --- a/packages/grafana-ui/src/components/TimeSeries/utils.ts +++ b/packages/grafana-ui/src/components/TimeSeries/utils.ts @@ -279,7 +279,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ label: customConfig.axisLabel, size: customConfig.axisWidth, placement: customConfig.axisPlacement ?? AxisPlacement.Auto, - formatValue: (v, decimals) => formattedValueToString(fmt(v, config.decimals ?? decimals)), + formatValue: (v, decimals) => formattedValueToString(fmt(v, decimals)), theme, grid: { show: customConfig.axisGridShow }, decimals: field.config.decimals, diff --git a/public/app/plugins/panel/barchart/utils.ts b/public/app/plugins/panel/barchart/utils.ts index af4f1a08997..9987a1b28a8 100644 --- a/public/app/plugins/panel/barchart/utils.ts +++ b/public/app/plugins/panel/barchart/utils.ts @@ -253,7 +253,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn = ({ label: customConfig.axisLabel, size: customConfig.axisWidth, placement, - formatValue: (v, decimals) => formattedValueToString(field.display!(v, field.config.decimals ?? decimals)), + formatValue: (v, decimals) => formattedValueToString(field.display!(v, decimals)), theme, grid: { show: customConfig.axisGridShow }, }); diff --git a/public/app/plugins/panel/heatmap/utils.ts b/public/app/plugins/panel/heatmap/utils.ts index ffd8a55aeae..4d6bc0427ce 100644 --- a/public/app/plugins/panel/heatmap/utils.ts +++ b/public/app/plugins/panel/heatmap/utils.ts @@ -408,7 +408,7 @@ export function prepConfig(opts: PrepConfigOpts) { size: yAxisConfig.axisWidth || null, label: yAxisConfig.axisLabel, theme: theme, - formatValue: (v, decimals) => formattedValueToString(dispY(v, yField.config.decimals ?? decimals)), + formatValue: (v, decimals) => formattedValueToString(dispY(v, decimals)), splits: isOrdianalY ? (self: uPlot) => { const meta = readHeatmapRowsCustomMeta(dataRef.current?.heatmap); diff --git a/public/app/plugins/panel/histogram/Histogram.tsx b/public/app/plugins/panel/histogram/Histogram.tsx index 437d5ec5403..85721e7d7d0 100644 --- a/public/app/plugins/panel/histogram/Histogram.tsx +++ b/public/app/plugins/panel/histogram/Histogram.tsx @@ -153,7 +153,7 @@ const prepConfig = (frame: DataFrame, theme: GrafanaTheme2) => { scaleKey: 'y', isTime: false, placement: AxisPlacement.Left, - formatValue: (v, decimals) => formattedValueToString(dispY!(v, countField.config.decimals ?? decimals)), + formatValue: (v, decimals) => formattedValueToString(dispY!(v, decimals)), //splits: config.xSplits, //values: config.xValues, //grid: false,