Commit 6bdc9fac by Torkel Ödegaard Committed by GitHub

Decimals: Big Improvements to auto decimals and fixes to auto decimals bug found…

Decimals: Big Improvements to auto decimals and fixes to auto decimals bug found in 7.4-beta1  (#30519)

* Decimals: Nukes scaledDecimals from the earth it was an abomination

* Moved move tests

* Fixed test

* Updated tests

* Updated test
parent be8ba8ef
...@@ -144,33 +144,6 @@ describe('Format value', () => { ...@@ -144,33 +144,6 @@ describe('Format value', () => {
expect(result.text).toEqual('10.0'); expect(result.text).toEqual('10.0');
}); });
it('should set auto decimals, 1 significant', () => {
const value = 3.23;
const instance = getDisplayProcessorFromConfig({ decimals: null });
expect(instance(value).text).toEqual('3.23');
});
it('should set auto decimals, 2 significant', () => {
const value = 0.0245;
const instance = getDisplayProcessorFromConfig({ decimals: null });
expect(instance(value).text).toEqual('0.0245');
});
it('should set auto decimals correctly for value 0.333333333333', () => {
const value = 1 / 3;
const instance = getDisplayProcessorFromConfig({ decimals: null });
expect(instance(value).text).toEqual('0.333');
});
it('should use override decimals', () => {
const value = 100030303;
const instance = getDisplayProcessorFromConfig({ decimals: 2, unit: 'bytes' });
const disp = instance(value);
expect(disp.text).toEqual('95.40');
expect(disp.suffix).toEqual(' MiB');
});
it('should return mapped value if there are matching value mappings', () => { it('should return mapped value if there are matching value mappings', () => {
const valueMappings: ValueMapping[] = [ const valueMappings: ValueMapping[] = [
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' }, { id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
...@@ -211,7 +184,7 @@ describe('Format value', () => { ...@@ -211,7 +184,7 @@ describe('Format value', () => {
const value = 1000; const value = 1000;
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' }); const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
const disp = instance(value); const disp = instance(value);
expect(disp.text).toEqual('1.0'); expect(disp.text).toEqual('1');
expect(disp.suffix).toEqual(' K'); expect(disp.suffix).toEqual(' K');
}); });
...@@ -219,7 +192,7 @@ describe('Format value', () => { ...@@ -219,7 +192,7 @@ describe('Format value', () => {
const value = 1200; const value = 1200;
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' }); const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
const disp = instance(value); const disp = instance(value);
expect(disp.text).toEqual('1.2'); expect(disp.text).toEqual('1.20');
expect(disp.suffix).toEqual(' K'); expect(disp.suffix).toEqual(' K');
}); });
...@@ -235,7 +208,7 @@ describe('Format value', () => { ...@@ -235,7 +208,7 @@ describe('Format value', () => {
const value = 1000000; const value = 1000000;
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' }); const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
const disp = instance(value); const disp = instance(value);
expect(disp.text).toEqual('1.0'); expect(disp.text).toEqual('1');
expect(disp.suffix).toEqual(' Mil'); expect(disp.suffix).toEqual(' Mil');
}); });
...@@ -243,9 +216,17 @@ describe('Format value', () => { ...@@ -243,9 +216,17 @@ describe('Format value', () => {
const value = 1500000; const value = 1500000;
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' }); const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
const disp = instance(value); const disp = instance(value);
expect(disp.text).toEqual('1.5'); expect(disp.text).toEqual('1.50');
expect(disp.suffix).toEqual(' Mil'); expect(disp.suffix).toEqual(' Mil');
}); });
it('with value 128000000 and unit bytes', () => {
const value = 1280000125;
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'bytes' });
const disp = instance(value);
expect(disp.text).toEqual('1.19');
expect(disp.suffix).toEqual(' GiB');
});
}); });
describe('Date display options', () => { describe('Date display options', () => {
......
...@@ -4,7 +4,7 @@ import _ from 'lodash'; ...@@ -4,7 +4,7 @@ import _ from 'lodash';
// Types // Types
import { Field, FieldType } from '../types/dataFrame'; import { Field, FieldType } from '../types/dataFrame';
import { GrafanaTheme } from '../types/theme'; import { GrafanaTheme } from '../types/theme';
import { DecimalCount, DecimalInfo, DisplayProcessor, DisplayValue } from '../types/displayValue'; import { DisplayProcessor, DisplayValue } from '../types/displayValue';
import { getValueFormat } from '../valueFormats/valueFormats'; import { getValueFormat } from '../valueFormats/valueFormats';
import { getMappedValue } from '../utils/valueMappings'; import { getMappedValue } from '../utils/valueMappings';
import { dateTime } from '../datetime'; import { dateTime } from '../datetime';
...@@ -86,8 +86,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP ...@@ -86,8 +86,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
if (!isNaN(numeric)) { if (!isNaN(numeric)) {
if (shouldFormat && !_.isBoolean(value)) { if (shouldFormat && !_.isBoolean(value)) {
const { decimals, scaledDecimals } = getDecimalsForValue(value, config.decimals); const v = formatFunc(numeric, config.decimals, null, options.timeZone);
const v = formatFunc(numeric, decimals, scaledDecimals, options.timeZone);
text = v.text; text = v.text;
suffix = v.suffix; suffix = v.suffix;
prefix = v.prefix; prefix = v.prefix;
...@@ -137,53 +136,6 @@ function toStringProcessor(value: any): DisplayValue { ...@@ -137,53 +136,6 @@ function toStringProcessor(value: any): DisplayValue {
return { text: _.toString(value), numeric: toNumber(value) }; return { text: _.toString(value), numeric: toNumber(value) };
} }
function getSignificantDigitCount(n: number) {
//remove decimal and make positive
n = Math.abs(+String(n).replace('.', ''));
if (n === 0) {
return 0;
}
// kill the 0s at the end of n
while (n !== 0 && n % 10 === 0) {
n /= 10;
}
// get number of digits
return Math.floor(Math.log(n) / Math.LN10) + 1;
}
export function getDecimalsForValue(value: number, decimalOverride?: DecimalCount): DecimalInfo {
if (_.isNumber(decimalOverride)) {
// It's important that scaledDecimals is null here
return { decimals: decimalOverride, scaledDecimals: null };
}
if (value === 0) {
return { decimals: 0, scaledDecimals: 0 };
}
const digits = getSignificantDigitCount(value);
const log10 = Math.floor(Math.log(Math.abs(value)) / Math.LN10);
let dec = -log10 + 1;
const magn = Math.pow(10, -dec);
const norm = value / magn; // norm is between 1.0 and 10.0
// special case for 2.5, requires an extra decimal
if (norm > 2.25) {
++dec;
}
if (value % 1 === 0) {
dec = 0;
}
const decimals = Math.max(0, dec);
const scaledDecimals = decimals - log10 + digits - 1;
return { decimals, scaledDecimals };
}
export function getRawDisplayProcessor(): DisplayProcessor { export function getRawDisplayProcessor(): DisplayProcessor {
return (value: any) => ({ return (value: any) => ({
text: `${value}`, text: `${value}`,
......
...@@ -31,7 +31,7 @@ const INTERVALS_IN_SECONDS: IntervalsInSeconds = { ...@@ -31,7 +31,7 @@ const INTERVALS_IN_SECONDS: IntervalsInSeconds = {
[Interval.Millisecond]: 0.001, [Interval.Millisecond]: 0.001,
}; };
export function toNanoSeconds(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount): FormattedValue { export function toNanoSeconds(size: number, decimals?: DecimalCount): FormattedValue {
if (size === null) { if (size === null) {
return { text: '' }; return { text: '' };
} }
...@@ -39,21 +39,21 @@ export function toNanoSeconds(size: number, decimals?: DecimalCount, scaledDecim ...@@ -39,21 +39,21 @@ export function toNanoSeconds(size: number, decimals?: DecimalCount, scaledDecim
if (Math.abs(size) < 1000) { if (Math.abs(size) < 1000) {
return { text: toFixed(size, decimals), suffix: ' ns' }; return { text: toFixed(size, decimals), suffix: ' ns' };
} else if (Math.abs(size) < 1000000) { } else if (Math.abs(size) < 1000000) {
return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' µs'); return toFixedScaled(size / 1000, decimals, ' µs');
} else if (Math.abs(size) < 1000000000) { } else if (Math.abs(size) < 1000000000) {
return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' ms'); return toFixedScaled(size / 1000000, decimals, ' ms');
} else if (Math.abs(size) < 60000000000) { } else if (Math.abs(size) < 60000000000) {
return toFixedScaled(size / 1000000000, decimals, scaledDecimals, 9, ' s'); return toFixedScaled(size / 1000000000, decimals, ' s');
} else if (Math.abs(size) < 3600000000000) { } else if (Math.abs(size) < 3600000000000) {
return toFixedScaled(size / 60000000000, decimals, scaledDecimals, 12, ' min'); return toFixedScaled(size / 60000000000, decimals, ' min');
} else if (Math.abs(size) < 86400000000000) { } else if (Math.abs(size) < 86400000000000) {
return toFixedScaled(size / 3600000000000, decimals, scaledDecimals, 13, ' hour'); return toFixedScaled(size / 3600000000000, decimals, ' hour');
} else { } else {
return toFixedScaled(size / 86400000000000, decimals, scaledDecimals, 14, ' day'); return toFixedScaled(size / 86400000000000, decimals, ' day');
} }
} }
export function toMicroSeconds(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount): FormattedValue { export function toMicroSeconds(size: number, decimals?: DecimalCount): FormattedValue {
if (size === null) { if (size === null) {
return { text: '' }; return { text: '' };
} }
...@@ -61,9 +61,9 @@ export function toMicroSeconds(size: number, decimals?: DecimalCount, scaledDeci ...@@ -61,9 +61,9 @@ export function toMicroSeconds(size: number, decimals?: DecimalCount, scaledDeci
if (Math.abs(size) < 1000) { if (Math.abs(size) < 1000) {
return { text: toFixed(size, decimals), suffix: ' µs' }; return { text: toFixed(size, decimals), suffix: ' µs' };
} else if (Math.abs(size) < 1000000) { } else if (Math.abs(size) < 1000000) {
return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' ms'); return toFixedScaled(size / 1000, decimals, ' ms');
} else { } else {
return toFixedScaled(size / 1000000, decimals, scaledDecimals, 6, ' s'); return toFixedScaled(size / 1000000, decimals, ' s');
} }
} }
...@@ -76,19 +76,19 @@ export function toMilliSeconds(size: number, decimals?: DecimalCount, scaledDeci ...@@ -76,19 +76,19 @@ export function toMilliSeconds(size: number, decimals?: DecimalCount, scaledDeci
return { text: toFixed(size, decimals), suffix: ' ms' }; return { text: toFixed(size, decimals), suffix: ' ms' };
} else if (Math.abs(size) < 60000) { } else if (Math.abs(size) < 60000) {
// Less than 1 min // Less than 1 min
return toFixedScaled(size / 1000, decimals, scaledDecimals, 3, ' s'); return toFixedScaled(size / 1000, decimals, ' s');
} else if (Math.abs(size) < 3600000) { } else if (Math.abs(size) < 3600000) {
// Less than 1 hour, divide in minutes // Less than 1 hour, divide in minutes
return toFixedScaled(size / 60000, decimals, scaledDecimals, 5, ' min'); return toFixedScaled(size / 60000, decimals, ' min');
} else if (Math.abs(size) < 86400000) { } else if (Math.abs(size) < 86400000) {
// Less than one day, divide in hours // Less than one day, divide in hours
return toFixedScaled(size / 3600000, decimals, scaledDecimals, 7, ' hour'); return toFixedScaled(size / 3600000, decimals, ' hour');
} else if (Math.abs(size) < 31536000000) { } else if (Math.abs(size) < 31536000000) {
// Less than one year, divide in days // Less than one year, divide in days
return toFixedScaled(size / 86400000, decimals, scaledDecimals, 8, ' day'); return toFixedScaled(size / 86400000, decimals, ' day');
} }
return toFixedScaled(size / 31536000000, decimals, scaledDecimals, 10, ' year'); return toFixedScaled(size / 31536000000, decimals, ' year');
} }
export function trySubstract(value1: DecimalCount, value2: DecimalCount): DecimalCount { export function trySubstract(value1: DecimalCount, value2: DecimalCount): DecimalCount {
...@@ -98,7 +98,7 @@ export function trySubstract(value1: DecimalCount, value2: DecimalCount): Decima ...@@ -98,7 +98,7 @@ export function trySubstract(value1: DecimalCount, value2: DecimalCount): Decima
return undefined; return undefined;
} }
export function toSeconds(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount): FormattedValue { export function toSeconds(size: number, decimals?: DecimalCount): FormattedValue {
if (size === null) { if (size === null) {
return { text: '' }; return { text: '' };
} }
...@@ -110,37 +110,37 @@ export function toSeconds(size: number, decimals?: DecimalCount, scaledDecimals? ...@@ -110,37 +110,37 @@ export function toSeconds(size: number, decimals?: DecimalCount, scaledDecimals?
// Less than 1 µs, divide in ns // Less than 1 µs, divide in ns
if (Math.abs(size) < 0.000001) { if (Math.abs(size) < 0.000001) {
return toFixedScaled(size * 1e9, decimals, trySubstract(scaledDecimals, decimals), -9, ' ns'); return toFixedScaled(size * 1e9, decimals, ' ns');
} }
// Less than 1 ms, divide in µs // Less than 1 ms, divide in µs
if (Math.abs(size) < 0.001) { if (Math.abs(size) < 0.001) {
return toFixedScaled(size * 1e6, decimals, trySubstract(scaledDecimals, decimals), -6, ' µs'); return toFixedScaled(size * 1e6, decimals, ' µs');
} }
// Less than 1 second, divide in ms // Less than 1 second, divide in ms
if (Math.abs(size) < 1) { if (Math.abs(size) < 1) {
return toFixedScaled(size * 1e3, decimals, trySubstract(scaledDecimals, decimals), -3, ' ms'); return toFixedScaled(size * 1e3, decimals, ' ms');
} }
if (Math.abs(size) < 60) { if (Math.abs(size) < 60) {
return { text: toFixed(size, decimals), suffix: ' s' }; return { text: toFixed(size, decimals), suffix: ' s' };
} else if (Math.abs(size) < 3600) { } else if (Math.abs(size) < 3600) {
// Less than 1 hour, divide in minutes // Less than 1 hour, divide in minutes
return toFixedScaled(size / 60, decimals, scaledDecimals, 1, ' min'); return toFixedScaled(size / 60, decimals, ' min');
} else if (Math.abs(size) < 86400) { } else if (Math.abs(size) < 86400) {
// Less than one day, divide in hours // Less than one day, divide in hours
return toFixedScaled(size / 3600, decimals, scaledDecimals, 4, ' hour'); return toFixedScaled(size / 3600, decimals, ' hour');
} else if (Math.abs(size) < 604800) { } else if (Math.abs(size) < 604800) {
// Less than one week, divide in days // Less than one week, divide in days
return toFixedScaled(size / 86400, decimals, scaledDecimals, 5, ' day'); return toFixedScaled(size / 86400, decimals, ' day');
} else if (Math.abs(size) < 31536000) { } else if (Math.abs(size) < 31536000) {
// Less than one year, divide in week // Less than one year, divide in week
return toFixedScaled(size / 604800, decimals, scaledDecimals, 6, ' week'); return toFixedScaled(size / 604800, decimals, ' week');
} }
return toFixedScaled(size / 3.15569e7, decimals, scaledDecimals, 7, ' year'); return toFixedScaled(size / 3.15569e7, decimals, ' year');
} }
export function toMinutes(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount): FormattedValue { export function toMinutes(size: number, decimals?: DecimalCount): FormattedValue {
if (size === null) { if (size === null) {
return { text: '' }; return { text: '' };
} }
...@@ -148,17 +148,17 @@ export function toMinutes(size: number, decimals?: DecimalCount, scaledDecimals? ...@@ -148,17 +148,17 @@ export function toMinutes(size: number, decimals?: DecimalCount, scaledDecimals?
if (Math.abs(size) < 60) { if (Math.abs(size) < 60) {
return { text: toFixed(size, decimals), suffix: ' min' }; return { text: toFixed(size, decimals), suffix: ' min' };
} else if (Math.abs(size) < 1440) { } else if (Math.abs(size) < 1440) {
return toFixedScaled(size / 60, decimals, scaledDecimals, 2, ' hour'); return toFixedScaled(size / 60, decimals, ' hour');
} else if (Math.abs(size) < 10080) { } else if (Math.abs(size) < 10080) {
return toFixedScaled(size / 1440, decimals, scaledDecimals, 3, ' day'); return toFixedScaled(size / 1440, decimals, ' day');
} else if (Math.abs(size) < 604800) { } else if (Math.abs(size) < 604800) {
return toFixedScaled(size / 10080, decimals, scaledDecimals, 4, ' week'); return toFixedScaled(size / 10080, decimals, ' week');
} else { } else {
return toFixedScaled(size / 5.25948e5, decimals, scaledDecimals, 5, ' year'); return toFixedScaled(size / 5.25948e5, decimals, ' year');
} }
} }
export function toHours(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount): FormattedValue { export function toHours(size: number, decimals?: DecimalCount): FormattedValue {
if (size === null) { if (size === null) {
return { text: '' }; return { text: '' };
} }
...@@ -166,15 +166,15 @@ export function toHours(size: number, decimals?: DecimalCount, scaledDecimals?: ...@@ -166,15 +166,15 @@ export function toHours(size: number, decimals?: DecimalCount, scaledDecimals?:
if (Math.abs(size) < 24) { if (Math.abs(size) < 24) {
return { text: toFixed(size, decimals), suffix: ' hour' }; return { text: toFixed(size, decimals), suffix: ' hour' };
} else if (Math.abs(size) < 168) { } else if (Math.abs(size) < 168) {
return toFixedScaled(size / 24, decimals, scaledDecimals, 2, ' day'); return toFixedScaled(size / 24, decimals, ' day');
} else if (Math.abs(size) < 8760) { } else if (Math.abs(size) < 8760) {
return toFixedScaled(size / 168, decimals, scaledDecimals, 3, ' week'); return toFixedScaled(size / 168, decimals, ' week');
} else { } else {
return toFixedScaled(size / 8760, decimals, scaledDecimals, 4, ' year'); return toFixedScaled(size / 8760, decimals, ' year');
} }
} }
export function toDays(size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount): FormattedValue { export function toDays(size: number, decimals?: DecimalCount): FormattedValue {
if (size === null) { if (size === null) {
return { text: '' }; return { text: '' };
} }
...@@ -182,9 +182,9 @@ export function toDays(size: number, decimals?: DecimalCount, scaledDecimals?: D ...@@ -182,9 +182,9 @@ export function toDays(size: number, decimals?: DecimalCount, scaledDecimals?: D
if (Math.abs(size) < 7) { if (Math.abs(size) < 7) {
return { text: toFixed(size, decimals), suffix: ' day' }; return { text: toFixed(size, decimals), suffix: ' day' };
} else if (Math.abs(size) < 365) { } else if (Math.abs(size) < 365) {
return toFixedScaled(size / 7, decimals, scaledDecimals, 2, ' week'); return toFixedScaled(size / 7, decimals, ' week');
} else { } else {
return toFixedScaled(size / 365, decimals, scaledDecimals, 3, ' year'); return toFixedScaled(size / 365, decimals, ' year');
} }
} }
...@@ -340,8 +340,8 @@ export function toDurationInDaysHoursMinutesSeconds(size: number): FormattedValu ...@@ -340,8 +340,8 @@ export function toDurationInDaysHoursMinutesSeconds(size: number): FormattedValu
return { text: dayString + hmsString.text }; return { text: dayString + hmsString.text };
} }
export function toTimeTicks(size: number, decimals: DecimalCount, scaledDecimals: DecimalCount): FormattedValue { export function toTimeTicks(size: number, decimals: DecimalCount): FormattedValue {
return toSeconds(size / 100, decimals, scaledDecimals); return toSeconds(size / 100, decimals);
} }
export function toClockMilliseconds(size: number, decimals: DecimalCount): FormattedValue { export function toClockMilliseconds(size: number, decimals: DecimalCount): FormattedValue {
......
...@@ -17,6 +17,10 @@ const formatTests: ValueFormatTest[] = [ ...@@ -17,6 +17,10 @@ const formatTests: ValueFormatTest[] = [
{ id: 'currencyUSD', decimals: 2, value: 1532.82, result: '$1.53K' }, { id: 'currencyUSD', decimals: 2, value: 1532.82, result: '$1.53K' },
{ id: 'currencyKRW', decimals: 2, value: 1532.82, result: '₩1.53K' }, { id: 'currencyKRW', decimals: 2, value: 1532.82, result: '₩1.53K' },
{ id: 'currencyIDR', decimals: 2, value: 1532.82, result: 'Rp1.53K' }, { id: 'currencyIDR', decimals: 2, value: 1532.82, result: 'Rp1.53K' },
// no unit
{ id: 'none', decimals: null, value: 3.23, result: '3.23' },
{ id: 'none', decimals: null, value: 0.0245, result: '0.0245' },
{ id: 'none', decimals: null, value: 1 / 3, result: '0.333' },
// Standard // Standard
{ id: 'ms', decimals: 4, value: 0.0024, result: '0.0024 ms' }, { id: 'ms', decimals: 4, value: 0.0024, result: '0.0024 ms' },
...@@ -24,11 +28,17 @@ const formatTests: ValueFormatTest[] = [ ...@@ -24,11 +28,17 @@ const formatTests: ValueFormatTest[] = [
{ id: 'ms', decimals: 2, value: 1250, result: '1.25 s' }, { id: 'ms', decimals: 2, value: 1250, result: '1.25 s' },
{ id: 'ms', decimals: 1, value: 10000086.123, result: '2.8 hour' }, { id: 'ms', decimals: 1, value: 10000086.123, result: '2.8 hour' },
{ id: 'ms', decimals: 0, value: 1200, result: '1 s' }, { id: 'ms', decimals: 0, value: 1200, result: '1 s' },
{ id: 'short', decimals: 0, scaledDecimals: -1, value: 98765, result: '98.77 K' }, { id: 'short', decimals: null, value: 1000, result: '1 K' },
{ id: 'short', decimals: 0, scaledDecimals: 0, value: 9876543, result: '9.876543 Mil' }, { id: 'short', decimals: null, value: 1200, result: '1.20 K' },
{ id: 'short', decimals: 2, scaledDecimals: null, value: 9876543, result: '9.88 Mil' }, { id: 'short', decimals: null, value: 1250, result: '1.25 K' },
{ id: 'kbytes', decimals: 3, value: 10000000, result: '9.537 GiB' }, { id: 'short', decimals: null, value: 1000000, result: '1 Mil' },
{ id: 'deckbytes', decimals: 3, value: 10000000, result: '10.000 GB' }, { id: 'short', decimals: null, value: 1500000, result: '1.50 Mil' },
{ id: 'short', decimals: null, value: 1000120, result: '1.00 Mil' },
{ id: 'short', decimals: null, value: 98765, result: '98.8 K' },
{ id: 'short', decimals: null, value: 9876543, result: '9.88 Mil' },
{ id: 'short', decimals: null, value: 9876543, result: '9.88 Mil' },
{ id: 'kbytes', decimals: null, value: 10000000, result: '9.54 GiB' },
{ id: 'deckbytes', decimals: null, value: 10000000, result: '10 GB' },
{ id: 'megwatt', decimals: 3, value: 1000, result: '1.000 GW' }, { id: 'megwatt', decimals: 3, value: 1000, result: '1.000 GW' },
{ id: 'kohm', decimals: 3, value: 1000, result: '1.000 MΩ' }, { id: 'kohm', decimals: 3, value: 1000, result: '1.000 MΩ' },
{ id: 'Mohm', decimals: 3, value: 1000, result: '1.000 GΩ' }, { id: 'Mohm', decimals: 3, value: 1000, result: '1.000 GΩ' },
......
...@@ -45,10 +45,15 @@ export function toFixed(value: number, decimals?: DecimalCount): string { ...@@ -45,10 +45,15 @@ export function toFixed(value: number, decimals?: DecimalCount): string {
if (value === null) { if (value === null) {
return ''; return '';
} }
if (value === Number.NEGATIVE_INFINITY || value === Number.POSITIVE_INFINITY) { if (value === Number.NEGATIVE_INFINITY || value === Number.POSITIVE_INFINITY) {
return value.toLocaleString(); return value.toLocaleString();
} }
if (decimals === null || decimals === undefined) {
decimals = getDecimalsForValue(value);
}
const factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1; const factor = decimals ? Math.pow(10, Math.max(0, decimals)) : 1;
const formatted = String(Math.round(value * factor) / factor); const formatted = String(Math.round(value * factor) / factor);
...@@ -57,31 +62,37 @@ export function toFixed(value: number, decimals?: DecimalCount): string { ...@@ -57,31 +62,37 @@ export function toFixed(value: number, decimals?: DecimalCount): string {
return formatted; return formatted;
} }
// If tickDecimals was specified, ensure that we have exactly that const decimalPos = formatted.indexOf('.');
// much precision; otherwise default to the value's own precision. const precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1;
if (decimals != null) { if (precision < decimals) {
const decimalPos = formatted.indexOf('.'); return (precision ? formatted : formatted + '.') + String(factor).substr(1, decimals - precision);
const precision = decimalPos === -1 ? 0 : formatted.length - decimalPos - 1;
if (precision < decimals) {
return (precision ? formatted : formatted + '.') + String(factor).substr(1, decimals - precision);
}
} }
return formatted; return formatted;
} }
export function toFixedScaled( function getDecimalsForValue(value: number): number {
value: number, const log10 = Math.floor(Math.log(Math.abs(value)) / Math.LN10);
decimals: DecimalCount, let dec = -log10 + 1;
scaledDecimals: DecimalCount, const magn = Math.pow(10, -dec);
additionalDecimals: number, const norm = value / magn; // norm is between 1.0 and 10.0
ext?: string
): FormattedValue { // special case for 2.5, requires an extra decimal
if (scaledDecimals === null || scaledDecimals === undefined) { if (norm > 2.25) {
return { text: toFixed(value, decimals), suffix: ext }; ++dec;
} }
if (value % 1 === 0) {
dec = 0;
}
const decimals = Math.max(0, dec);
return decimals;
}
export function toFixedScaled(value: number, decimals: DecimalCount, ext?: string): FormattedValue {
return { return {
text: toFixed(value, scaledDecimals + additionalDecimals), text: toFixed(value, decimals),
suffix: ext, suffix: ext,
}; };
} }
...@@ -126,10 +137,6 @@ export function scaledUnits(factor: number, extArray: string[]): ValueFormatter ...@@ -126,10 +137,6 @@ export function scaledUnits(factor: number, extArray: string[]): ValueFormatter
} }
} }
if (steps > 0 && scaledDecimals !== null && scaledDecimals !== undefined) {
decimals = scaledDecimals + 3 * steps;
}
return { text: toFixed(size, decimals), suffix: extArray[steps] }; return { text: toFixed(size, decimals), suffix: extArray[steps] };
}; };
} }
......
...@@ -599,7 +599,7 @@ describe('logSeriesToLogsModel', () => { ...@@ -599,7 +599,7 @@ describe('logSeriesToLogsModel', () => {
hasUniqueLabels: false, hasUniqueLabels: false,
meta: [ meta: [
{ label: 'Limit', value: '1000 (0 returned)', kind: 1 }, { label: 'Limit', value: '1000 (0 returned)', kind: 1 },
{ label: 'Total bytes processed', value: '97 kB', kind: 1 }, { label: 'Total bytes processed', value: '97.0 kB', kind: 1 },
], ],
rows: [], rows: [],
}; };
......
...@@ -4,7 +4,6 @@ import { DecimalCount, TimeZone } from '@grafana/data'; ...@@ -4,7 +4,6 @@ import { DecimalCount, TimeZone } from '@grafana/data';
interface ValueFormatTest { interface ValueFormatTest {
id: string; id: string;
decimals?: DecimalCount; decimals?: DecimalCount;
scaledDecimals?: DecimalCount;
timeZone?: TimeZone; timeZone?: TimeZone;
value: number; value: number;
result: string; result: string;
...@@ -22,8 +21,8 @@ const formatTests: ValueFormatTest[] = [ ...@@ -22,8 +21,8 @@ const formatTests: ValueFormatTest[] = [
{ id: 'ms', decimals: 2, value: 1250, result: '1.25 s' }, { id: 'ms', decimals: 2, value: 1250, result: '1.25 s' },
{ id: 'ms', decimals: 1, value: 10000086.123, result: '2.8 hour' }, { id: 'ms', decimals: 1, value: 10000086.123, result: '2.8 hour' },
{ id: 'ms', decimals: 0, value: 1200, result: '1 s' }, { id: 'ms', decimals: 0, value: 1200, result: '1 s' },
{ id: 'short', decimals: 0, scaledDecimals: -1, value: 98765, result: '98.77 K' }, { id: 'short', decimals: 0, value: 98765, result: '99 K' },
{ id: 'short', decimals: 0, scaledDecimals: 0, value: 9876543, result: '9.876543 Mil' }, { id: 'short', decimals: 0, value: 9876543, result: '10 Mil' },
{ id: 'kbytes', decimals: 3, value: 10000000, result: '9.537 GiB' }, { id: 'kbytes', decimals: 3, value: 10000000, result: '9.537 GiB' },
{ id: 'deckbytes', decimals: 3, value: 10000000, result: '10.000 GB' }, { id: 'deckbytes', decimals: 3, value: 10000000, result: '10.000 GB' },
{ id: 'megwatt', decimals: 3, value: 1000, result: '1.000 GW' }, { id: 'megwatt', decimals: 3, value: 1000, result: '1.000 GW' },
...@@ -45,7 +44,7 @@ describe('Chcek KBN value formats', () => { ...@@ -45,7 +44,7 @@ describe('Chcek KBN value formats', () => {
for (const test of formatTests) { for (const test of formatTests) {
describe(`value format: ${test.id}`, () => { describe(`value format: ${test.id}`, () => {
it(`should translate ${test.value} as ${test.result}`, () => { it(`should translate ${test.value} as ${test.result}`, () => {
const result = kbn.valueFormats[test.id](test.value, test.decimals, test.scaledDecimals); const result = kbn.valueFormats[test.id](test.value, test.decimals);
expect(result).toBe(test.result); expect(result).toBe(test.result);
}); });
}); });
......
...@@ -743,7 +743,7 @@ class GraphElement { ...@@ -743,7 +743,7 @@ class GraphElement {
}; };
// Use 'short' format for histogram values // Use 'short' format for histogram values
this.configureAxisMode(options.xaxis, 'short'); this.configureAxisMode(options.xaxis, 'short', null);
} }
addXTableAxis(options: any) { addXTableAxis(options: any) {
...@@ -794,13 +794,15 @@ class GraphElement { ...@@ -794,13 +794,15 @@ class GraphElement {
this.applyLogScale(options.yaxes[1], data); this.applyLogScale(options.yaxes[1], data);
this.configureAxisMode( this.configureAxisMode(
options.yaxes[1], options.yaxes[1],
this.panel.percentage && this.panel.stack ? 'percent' : this.panel.yaxes[1].format this.panel.percentage && this.panel.stack ? 'percent' : this.panel.yaxes[1].format,
this.panel.yaxes[1].decimals
); );
} }
this.applyLogScale(options.yaxes[0], data); this.applyLogScale(options.yaxes[0], data);
this.configureAxisMode( this.configureAxisMode(
options.yaxes[0], options.yaxes[0],
this.panel.percentage && this.panel.stack ? 'percent' : this.panel.yaxes[0].format this.panel.percentage && this.panel.stack ? 'percent' : this.panel.yaxes[0].format,
this.panel.yaxes[0].decimals
); );
} }
...@@ -915,14 +917,19 @@ class GraphElement { ...@@ -915,14 +917,19 @@ class GraphElement {
return ticks; return ticks;
} }
configureAxisMode(axis: { tickFormatter: (val: any, axis: any) => string }, format: string) { configureAxisMode(
axis: { tickFormatter: (val: any, axis: any) => string },
format: string,
decimals?: number | null
) {
axis.tickFormatter = (val, axis) => { axis.tickFormatter = (val, axis) => {
const formatter = getValueFormat(format); const formatter = getValueFormat(format);
if (!formatter) { if (!formatter) {
throw new Error(`Unit '${format}' is not supported`); throw new Error(`Unit '${format}' is not supported`);
} }
return formattedValueToString(formatter(val, axis.tickDecimals, axis.scaledDecimals));
return formattedValueToString(formatter(val, decimals));
}; };
} }
} }
......
...@@ -1783,11 +1783,6 @@ Licensed under the MIT license. ...@@ -1783,11 +1783,6 @@ Licensed under the MIT license.
axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
axis.tickSize = opts.tickSize || size; axis.tickSize = opts.tickSize || size;
// grafana addition
if (opts.tickDecimals === null || opts.tickDecimals === undefined) {
axis.scaledDecimals = axis.tickDecimals + dec;
}
// Time mode was moved to a plug-in in 0.8, and since so many people use it // Time mode was moved to a plug-in in 0.8, and since so many people use it
// we'll add an especially friendly reminder to make sure they included it. // we'll add an especially friendly reminder to make sure they included it.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment