From 84d196776e2427ce837024259ca95a034f749617 Mon Sep 17 00:00:00 2001 From: Germain Date: Tue, 8 Aug 2023 12:45:20 +0100 Subject: [PATCH] Change formatCount impl to use Intl.NumberFormat (#11379) * Change formatCount impl to use Intl.NumberFormat * Update formatCount JSDoc description --- src/utils/FormattingUtils.ts | 25 +++++++++++------- test/utils/FormattingUtils-test.ts | 42 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 test/utils/FormattingUtils-test.ts diff --git a/src/utils/FormattingUtils.ts b/src/utils/FormattingUtils.ts index 83447aa745..d6f6cc1ef0 100644 --- a/src/utils/FormattingUtils.ts +++ b/src/utils/FormattingUtils.ts @@ -17,28 +17,33 @@ limitations under the License. import { ReactElement, ReactNode } from "react"; -import { _t } from "../languageHandler"; +import { _t, getCurrentLanguage } from "../languageHandler"; import { jsxJoin } from "./ReactUtils"; +const locale = getCurrentLanguage(); + +// It's quite costly to instanciate `Intl.NumberFormat`, hence why we do not do +// it in every function call +const compactFormatter = new Intl.NumberFormat(locale, { + notation: "compact", +}); /** - * formats numbers to fit into ~3 characters, suitable for badge counts - * e.g: 999, 9.9K, 99K, 0.9M, 9.9M, 99M, 0.9B, 9.9B + * formats and rounds numbers to fit into ~3 characters, suitable for badge counts + * e.g: 999, 10K, 99K, 1M, 10M, 99M, 1B, 10B, ... */ export function formatCount(count: number): string { - if (count < 1000) return count.toString(); - if (count < 10000) return (count / 1000).toFixed(1) + "K"; - if (count < 100000) return (count / 1000).toFixed(0) + "K"; - if (count < 10000000) return (count / 1000000).toFixed(1) + "M"; - if (count < 100000000) return (count / 1000000).toFixed(0) + "M"; - return (count / 1000000000).toFixed(1) + "B"; // 10B is enough for anyone, right? :S + return compactFormatter.format(count); } +// It's quite costly to instanciate `Intl.NumberFormat`, hence why we do not do +// it in every function call +const formatter = new Intl.NumberFormat(locale); + /** * Format a count showing the whole number but making it a bit more readable. * e.g: 1000 => 1,000 */ export function formatCountLong(count: number): string { - const formatter = new Intl.NumberFormat(); return formatter.format(count); } diff --git a/test/utils/FormattingUtils-test.ts b/test/utils/FormattingUtils-test.ts new file mode 100644 index 0000000000..bbe49053c3 --- /dev/null +++ b/test/utils/FormattingUtils-test.ts @@ -0,0 +1,42 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { formatCount, formatCountLong } from "../../src/utils/FormattingUtils"; + +jest.mock("../../src/dispatcher/dispatcher"); + +describe("FormattingUtils", () => { + describe("formatCount", () => { + it.each([ + { count: 999, expectedCount: "999" }, + { count: 9999, expectedCount: "10K" }, + { count: 99999, expectedCount: "100K" }, + { count: 999999, expectedCount: "1M" }, + { count: 9999999, expectedCount: "10M" }, + { count: 99999999, expectedCount: "100M" }, + { count: 999999999, expectedCount: "1B" }, + { count: 9999999999, expectedCount: "10B" }, + ])("formats $count as $expectedCount", ({ count, expectedCount }) => { + expect(formatCount(count)).toBe(expectedCount); + }); + }); + + describe("formatCountLong", () => { + it("formats numbers according to the locale", () => { + expect(formatCountLong(1000)).toBe("1,000"); + }); + }); +});