diff --git a/tensorboard/webapp/metrics/views/card_renderer/scalar_card_component.ts b/tensorboard/webapp/metrics/views/card_renderer/scalar_card_component.ts index 2586ec237a..ca6ca63b9a 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/scalar_card_component.ts +++ b/tensorboard/webapp/metrics/views/card_renderer/scalar_card_component.ts @@ -29,6 +29,7 @@ import { Formatter, numberFormatter, relativeTimeFormatter, + intlNumberFormatter, siNumberFormatter, } from '../../../widgets/line_chart_v2/lib/formatter'; import {LineChartComponent} from '../../../widgets/line_chart_v2/line_chart_component'; @@ -110,7 +111,7 @@ export class ScalarCardComponent { readonly relativeXFormatter = relativeTimeFormatter; readonly valueFormatter = numberFormatter; - readonly stepFormatter = siNumberFormatter; + readonly stepFormatter = intlNumberFormatter; getCustomXFormatter(): Formatter | undefined { switch (this.xAxisType) { diff --git a/tensorboard/webapp/metrics/views/card_renderer/scalar_card_test.ts b/tensorboard/webapp/metrics/views/card_renderer/scalar_card_test.ts index 77e09738ed..7e79b12ec9 100644 --- a/tensorboard/webapp/metrics/views/card_renderer/scalar_card_test.ts +++ b/tensorboard/webapp/metrics/views/card_renderer/scalar_card_test.ts @@ -1384,7 +1384,7 @@ describe('scalar card', () => { assertTooltipRows(fixture, [ ['', 'Row 2', '-500', '1,000', anyString, anyString], - ['', 'Row 3', '3', '10k', anyString, anyString], + ['', 'Row 3', '3', '10,000', anyString, anyString], ['', 'Row 1', '1000', '10', anyString, anyString], ]); })); @@ -1453,7 +1453,7 @@ describe('scalar card', () => { assertTooltipRows(fixture, [ ['', 'Row 1', '1000', '10', anyString, anyString], - ['', 'Row 3', '3', '10k', anyString, anyString], + ['', 'Row 3', '3', '10,000', anyString, anyString], ['', 'Row 2', '-500', '1,000', anyString, anyString], ]); })); @@ -1523,7 +1523,7 @@ describe('scalar card', () => { assertTooltipRows(fixture, [ ['', 'Row 2', '-500', '1,000', anyString, anyString], ['', 'Row 1', '1000', '0', anyString, anyString], - ['', 'Row 3', '3', '10k', anyString, anyString], + ['', 'Row 3', '3', '10,000', anyString, anyString], ]); setCursorLocation(fixture, {x: 500, y: 600}); @@ -1531,13 +1531,13 @@ describe('scalar card', () => { assertTooltipRows(fixture, [ ['', 'Row 1', '1000', '0', anyString, anyString], ['', 'Row 2', '-500', '1,000', anyString, anyString], - ['', 'Row 3', '3', '10k', anyString, anyString], + ['', 'Row 3', '3', '10,000', anyString, anyString], ]); setCursorLocation(fixture, {x: 10000, y: -100}); fixture.detectChanges(); assertTooltipRows(fixture, [ - ['', 'Row 3', '3', '10k', anyString, anyString], + ['', 'Row 3', '3', '10,000', anyString, anyString], ['', 'Row 2', '-500', '1,000', anyString, anyString], ['', 'Row 1', '1000', '0', anyString, anyString], ]); @@ -1548,7 +1548,7 @@ describe('scalar card', () => { assertTooltipRows(fixture, [ ['', 'Row 1', '1000', '0', anyString, anyString], ['', 'Row 2', '-500', '1,000', anyString, anyString], - ['', 'Row 3', '3', '10k', anyString, anyString], + ['', 'Row 3', '3', '10,000', anyString, anyString], ]); })); }); diff --git a/tensorboard/webapp/widgets/line_chart_v2/lib/formatter.ts b/tensorboard/webapp/widgets/line_chart_v2/lib/formatter.ts index a814bcf9c6..8d3b6d1479 100644 --- a/tensorboard/webapp/widgets/line_chart_v2/lib/formatter.ts +++ b/tensorboard/webapp/widgets/line_chart_v2/lib/formatter.ts @@ -86,6 +86,27 @@ export const numberFormatter: Formatter = { formatLong: d3LongFormatter, }; +/** + * =================== + * INTL NUMBER FORMATTER + * =================== + */ + +const IntlNumberFormatter = new Intl.NumberFormat(undefined, { + maximumFractionDigits: 3, +}); + +function formatIntlNumber(x: number): string { + return IntlNumberFormatter.format(x); +} + +export const intlNumberFormatter: Formatter = { + formatTick: formatIntlNumber, + formatShort: formatIntlNumber, + formatReadable: formatIntlNumber, + formatLong: formatIntlNumber, +}; + /** * =================== * SI NUMBER FORMATTER diff --git a/tensorboard/webapp/widgets/line_chart_v2/lib/formatter_test.ts b/tensorboard/webapp/widgets/line_chart_v2/lib/formatter_test.ts index f6784dea3c..85bf48e05c 100644 --- a/tensorboard/webapp/widgets/line_chart_v2/lib/formatter_test.ts +++ b/tensorboard/webapp/widgets/line_chart_v2/lib/formatter_test.ts @@ -15,6 +15,7 @@ limitations under the License. import { numberFormatter, relativeTimeFormatter, + intlNumberFormatter, siNumberFormatter, TEST_ONLY, wallTimeFormatter, @@ -167,6 +168,32 @@ describe('line_chart_v2/lib/formatter test', () => { }); }); + describe('#intlNumberFormatter', () => { + for (const {name, fn} of [ + {name: 'formatTick', fn: intlNumberFormatter.formatTick}, + {name: 'formatShort', fn: intlNumberFormatter.formatShort}, + {name: 'formatReadable', fn: intlNumberFormatter.formatReadable}, + {name: 'formatLong', fn: intlNumberFormatter.formatLong}, + ]) { + describe(`#${name}`, () => { + it('formats numbers and keeps three decimal places', () => { + expect(fn(1)).toBe('1'); + expect(fn(5)).toBe('5'); + expect(fn(-100.4)).toBe('-100.4'); + expect(fn(3.01)).toBe('3.01'); + expect(fn(9999)).toBe('9,999'); + expect(fn(9999.9123)).toBe('9,999.912'); + expect(fn(0.09)).toBe('0.09'); + expect(fn(0.0005)).toBe('0.001'); + expect(fn(0.00005)).toBe('0'); + expect(fn(10001)).toBe('10,001'); + expect(fn(-10000)).toBe('-10,000'); + expect(fn(-1.004e6)).toBe('-1,004,000'); + }); + }); + } + }); + describe('#siNumberFormatter', () => { for (const {name, fn} of [ {name: 'formatTick', fn: siNumberFormatter.formatTick}, @@ -175,7 +202,7 @@ describe('line_chart_v2/lib/formatter test', () => { {name: 'formatLong', fn: siNumberFormatter.formatLong}, ]) { describe(`#${name}`, () => { - it('formats without si-suffix for number less than 10k', () => { + it('formats without si-suffix', () => { expect(fn(1)).toBe('1'); expect(fn(5)).toBe('5'); expect(fn(-100.4)).toBe('-100.4'); @@ -183,6 +210,9 @@ describe('line_chart_v2/lib/formatter test', () => { expect(fn(9999)).toBe('9,999'); expect(fn(9999.9123)).toBe('9,999.912'); expect(fn(0.09)).toBe('0.09'); + }); + + it('formats with si-suffix', () => { expect(fn(10000)).toBe('10k'); expect(fn(10001)).toBe('10k'); expect(fn(-10000)).toBe('-10k');