From 5eb5779a827e8bc97f94a2893950edb33648aa85 Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Thu, 9 Jun 2022 01:33:17 -0600 Subject: [PATCH] Formatting: Make SI number formats more robust (#50117) (#50292) Closes #49372 (cherry picked from commit a9a276a9dae50e04b8b823fff43ca998b8e3110a) Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com> --- .../src/valueFormats/symbolFormatters.test.ts | 4 +- .../src/valueFormats/symbolFormatters.ts | 54 ++++--------------- .../src/valueFormats/valueFormats.ts | 30 +++++------ 3 files changed, 26 insertions(+), 62 deletions(-) diff --git a/packages/grafana-data/src/valueFormats/symbolFormatters.test.ts b/packages/grafana-data/src/valueFormats/symbolFormatters.test.ts index 63f0e466f0e..ac0fa6ef38c 100644 --- a/packages/grafana-data/src/valueFormats/symbolFormatters.test.ts +++ b/packages/grafana-data/src/valueFormats/symbolFormatters.test.ts @@ -13,7 +13,7 @@ describe('currency', () => { ${1000000} | ${'M'} | ${'1'} ${1000000000} | ${'B'} | ${'1'} ${1000000000000} | ${'T'} | ${'1'} - ${1000000000000000} | ${undefined} | ${'NA'} + ${1000000000000000} | ${'T'} | ${'1000'} ${-1000000000000} | ${'T'} | ${'-1'} ${-1000000000} | ${'B'} | ${'-1'} ${-1000000} | ${'M'} | ${'-1'} @@ -38,7 +38,7 @@ describe('currency', () => { ${1000000} | ${'M@'} | ${'1'} ${1000000000} | ${'B@'} | ${'1'} ${1000000000000} | ${'T@'} | ${'1'} - ${1000000000000000} | ${undefined} | ${'NA'} + ${1000000000000000} | ${'T@'} | ${'1000'} ${-1000000000000} | ${'T@'} | ${'-1'} ${-1000000000} | ${'B@'} | ${'-1'} ${-1000000} | ${'M@'} | ${'-1'} diff --git a/packages/grafana-data/src/valueFormats/symbolFormatters.ts b/packages/grafana-data/src/valueFormats/symbolFormatters.ts index cb59e83f464..9f23db1228b 100644 --- a/packages/grafana-data/src/valueFormats/symbolFormatters.ts +++ b/packages/grafana-data/src/valueFormats/symbolFormatters.ts @@ -19,54 +19,22 @@ export function currency(symbol: string, asSuffix?: boolean): ValueFormatter { }; } +const SI_PREFIXES = ['f', 'p', 'n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; +const SI_BASE_INDEX = SI_PREFIXES.indexOf(''); + export function getOffsetFromSIPrefix(c: string): number { - switch (c) { - case 'f': - return -5; - case 'p': - return -4; - case 'n': - return -3; - case 'μ': // Two different unicode chars for µ - case 'µ': - return -2; - case 'm': - return -1; - case '': - return 0; - case 'k': - return 1; - case 'M': - return 2; - case 'G': - return 3; - case 'T': - return 4; - case 'P': - return 5; - case 'E': - return 6; - case 'Z': - return 7; - case 'Y': - return 8; - } - return 0; + const charIndex = SI_PREFIXES.findIndex((prefix) => prefix.normalize('NFKD') === c.normalize('NFKD')); + return charIndex < 0 ? 0 : charIndex - SI_BASE_INDEX; } +const BIN_PREFIXES = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; + export function binaryPrefix(unit: string, offset = 0): ValueFormatter { - const prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'].slice(offset); - const units = prefixes.map((p) => { - return ' ' + p + unit; - }); - return scaledUnits(1024, units); + const units = BIN_PREFIXES.map((p) => ' ' + p + unit); + return scaledUnits(1024, units, offset); } export function SIPrefix(unit: string, offset = 0): ValueFormatter { - let prefixes = ['f', 'p', 'n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; - prefixes = prefixes.slice(5 + (offset || 0)); - const units = prefixes.map((p) => { - return ' ' + p + unit; - }); - return scaledUnits(1000, units); + const units = SI_PREFIXES.map((p) => ' ' + p + unit); + return scaledUnits(1000, units, SI_BASE_INDEX + offset); } diff --git a/packages/grafana-data/src/valueFormats/valueFormats.ts b/packages/grafana-data/src/valueFormats/valueFormats.ts index 805f53fa740..b150afa7da5 100644 --- a/packages/grafana-data/src/valueFormats/valueFormats.ts +++ b/packages/grafana-data/src/valueFormats/valueFormats.ts @@ -1,3 +1,5 @@ +import { clamp } from 'lodash'; + import { TimeZone } from '../types'; import { DecimalCount } from '../types/displayValue'; @@ -129,31 +131,25 @@ export function booleanValueFormatter(t: string, f: string): ValueFormatter { }; } -// Formatter which scales the unit string geometrically according to the given -// numeric factor. Repeatedly scales the value down by the factor until it is -// less than the factor in magnitude, or the end of the array is reached. -export function scaledUnits(factor: number, extArray: string[]): ValueFormatter { - return (size: number, decimals?: DecimalCount, scaledDecimals?: DecimalCount) => { +const logb = (b: number, x: number) => Math.log10(x) / Math.log10(b); + +export function scaledUnits(factor: number, extArray: string[], offset = 0): ValueFormatter { + return (size: number, decimals?: DecimalCount) => { if (size === null) { return { text: '' }; } + if (size === Number.NEGATIVE_INFINITY || size === Number.POSITIVE_INFINITY || isNaN(size)) { return { text: size.toLocaleString() }; } - let steps = 0; - const limit = extArray.length; + const siIndex = Math.floor(logb(factor, Math.abs(size))); + const suffix = extArray[clamp(offset + siIndex, 0, extArray.length - 1)]; - while (Math.abs(size) >= factor) { - steps++; - size /= factor; - - if (steps >= limit) { - return { text: 'NA' }; - } - } - - return { text: toFixed(size, decimals), suffix: extArray[steps] }; + return { + text: toFixed(size / factor ** clamp(siIndex, -offset, extArray.length - offset - 1), decimals), + suffix, + }; }; }