Commit 3b0356a1 by Torkel Ödegaard Committed by GitHub

Decimals: Improving auto decimals logic for high numbers and scaled units (#30262)

* Big improvement to decimals logic is finally working

* Removed unused elements from old solution

* Updated test
parent 3d7748d9
import { getDecimalsForValue, getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor'; import { getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor';
import { DisplayProcessor, DisplayValue } from '../types/displayValue'; import { DisplayProcessor, DisplayValue } from '../types/displayValue';
import { MappingType, ValueMapping } from '../types/valueMapping'; import { MappingType, ValueMapping } from '../types/valueMapping';
import { FieldConfig, FieldType, ThresholdsMode } from '../types'; import { FieldConfig, FieldType, ThresholdsMode } from '../types';
...@@ -147,14 +147,20 @@ describe('Format value', () => { ...@@ -147,14 +147,20 @@ describe('Format value', () => {
it('should set auto decimals, 1 significant', () => { it('should set auto decimals, 1 significant', () => {
const value = 3.23; const value = 3.23;
const instance = getDisplayProcessorFromConfig({ decimals: null }); const instance = getDisplayProcessorFromConfig({ decimals: null });
expect(instance(value).text).toEqual('3.2'); expect(instance(value).text).toEqual('3.23');
}); });
it('should set auto decimals, 2 significant', () => { it('should set auto decimals, 2 significant', () => {
const value = 0.0245; const value = 0.0245;
const instance = getDisplayProcessorFromConfig({ decimals: null }); const instance = getDisplayProcessorFromConfig({ decimals: null });
expect(instance(value).text).toEqual('0.025'); 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', () => { it('should use override decimals', () => {
...@@ -205,7 +211,7 @@ describe('Format value', () => { ...@@ -205,7 +211,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.000'); expect(disp.text).toEqual('1.0');
expect(disp.suffix).toEqual(' K'); expect(disp.suffix).toEqual(' K');
}); });
...@@ -213,7 +219,7 @@ describe('Format value', () => { ...@@ -213,7 +219,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.200'); expect(disp.text).toEqual('1.2');
expect(disp.suffix).toEqual(' K'); expect(disp.suffix).toEqual(' K');
}); });
...@@ -221,7 +227,7 @@ describe('Format value', () => { ...@@ -221,7 +227,7 @@ describe('Format value', () => {
const value = 1250; const value = 1250;
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.250'); expect(disp.text).toEqual('1.25');
expect(disp.suffix).toEqual(' K'); expect(disp.suffix).toEqual(' K');
}); });
...@@ -229,7 +235,15 @@ describe('Format value', () => { ...@@ -229,7 +235,15 @@ 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.000'); expect(disp.text).toEqual('1.0');
expect(disp.suffix).toEqual(' Mil');
});
it('with value 15000000 and unit short', () => {
const value = 1500000;
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
const disp = instance(value);
expect(disp.text).toEqual('1.5');
expect(disp.suffix).toEqual(' Mil'); expect(disp.suffix).toEqual(' Mil');
}); });
}); });
...@@ -349,21 +363,3 @@ describe('getRawDisplayProcessor', () => { ...@@ -349,21 +363,3 @@ describe('getRawDisplayProcessor', () => {
expect(result).toEqual({ text: expected, numeric: null }); expect(result).toEqual({ text: expected, numeric: null });
}); });
}); });
describe('getDecimalsForValue', () => {
it.each`
value | expected
${0} | ${0}
${13.37} | ${0}
${-13.37} | ${0}
${12679.3712345811212} | ${0}
${-12679.3712345811212} | ${0}
${0.3712345} | ${2}
${-0.37123458} | ${2}
${-0.04671994403853774} | ${3}
${0.04671994403853774} | ${3}
`('should return correct suggested decimal count', ({ value, expected }) => {
const result = getDecimalsForValue(value);
expect(result.decimals).toEqual(expected);
});
});
...@@ -137,41 +137,49 @@ function toStringProcessor(value: any): DisplayValue { ...@@ -137,41 +137,49 @@ 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 { export function getDecimalsForValue(value: number, decimalOverride?: DecimalCount): DecimalInfo {
if (_.isNumber(decimalOverride)) { if (_.isNumber(decimalOverride)) {
// It's important that scaledDecimals is null here // It's important that scaledDecimals is null here
return { decimals: decimalOverride, scaledDecimals: null }; return { decimals: decimalOverride, scaledDecimals: null };
} }
let dec = -Math.floor(Math.log(Math.abs(value)) / Math.LN10) + 1; 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 magn = Math.pow(10, -dec);
const norm = value / magn; // norm is between 1.0 and 10.0 const norm = value / magn; // norm is between 1.0 and 10.0
let size;
if (norm < 1.5) {
size = 1;
} else if (norm < 3) {
size = 2;
// special case for 2.5, requires an extra decimal
if (norm > 2.25) {
size = 2.5;
++dec;
}
} else if (norm < 7.5) {
size = 5;
} else {
size = 10;
}
size *= magn; // special case for 2.5, requires an extra decimal
if (norm > 2.25) {
++dec;
}
// reduce starting decimals if not needed
if (value % 1 === 0) { if (value % 1 === 0) {
dec = 0; dec = 0;
} }
const decimals = Math.max(0, dec); const decimals = Math.max(0, dec);
const scaledDecimals = decimals - Math.floor(Math.log(size) / Math.LN10) + 2; const scaledDecimals = decimals - log10 + digits - 1;
return { decimals, scaledDecimals }; return { decimals, scaledDecimals };
} }
......
...@@ -257,7 +257,7 @@ describe('SingleStatCtrl', () => { ...@@ -257,7 +257,7 @@ describe('SingleStatCtrl', () => {
}); });
it('should set formatted value', () => { it('should set formatted value', () => {
expect(ctx.data.display!.text).toBe('100'); expect(ctx.data.display!.text).toBe('100.0');
}); });
} }
); );
...@@ -373,7 +373,7 @@ describe('SingleStatCtrl', () => { ...@@ -373,7 +373,7 @@ describe('SingleStatCtrl', () => {
}); });
it('should set formatted falue', () => { it('should set formatted falue', () => {
expect(ctx.data.display!.text).toBe('100'); expect(ctx.data.display!.text).toBe('100.0');
}); });
} }
); );
......
...@@ -3059,7 +3059,7 @@ Licensed under the MIT license. ...@@ -3059,7 +3059,7 @@ Licensed under the MIT license.
if (!e.cancelable) { if (!e.cancelable) {
return; return;
} }
if (!eventHolder.is(e.target) && eventHolder.has(e.target).length === 0) { if (!eventHolder.is(e.target) && eventHolder.has(e.target).length === 0) {
triggerClickHoverEvent("plotleave", e, function (s) { false; }); triggerClickHoverEvent("plotleave", e, function (s) { false; });
return; return;
...@@ -3080,7 +3080,7 @@ Licensed under the MIT license. ...@@ -3080,7 +3080,7 @@ Licensed under the MIT license.
} }
var original = e.originalEvent; var original = e.originalEvent;
if (original.changedTouches.length === 0) { if (original.changedTouches.length === 0) {
return e; return e;
} }
......
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