From d74f49e65f2c63ef9eca571d9470b2a9d5c5d380 Mon Sep 17 00:00:00 2001 From: m-akinc <7282195+m-akinc@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:08:44 -0600 Subject: [PATCH] Update usage of Intl.NumberFormat options (#1791) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Pull Request ## ๐Ÿคจ Rationale Fixes: #1442 ## ๐Ÿ‘ฉโ€๐Ÿ’ป Implementation The options cited in the issue are: - `useGrouping: 'auto'` - We previously were setting this to `true`, which means "display grouping separators even if the locale prefers otherwise." Instead we want `"auto"`, which is "display grouping separators based on the locale preference." The default for this option is `"auto"` unless `notation` is `"compact"`. We never set `notation` to `"compact"`, so we can just leave `useGrouping` to use the default. - `signDisplay: 'negative'` - Though this option value has been [supported in all browsers since 8/23](https://caniuse.com/mdn-javascript_builtins_intl_numberformat_numberformat_options_signdisplay_parameter_negative), there does not seem to be a version of Typescript whose definition of `Intl.NumberFormatOptions` allows it. There is an [open issue for adding support](https://github.com/microsoft/TypeScript/issues/56269). As a workaround, I've just added type assertions where necessary. - `roundingPriority: 'lessPrecision'` - Just like `signDisplay: 'negative'`, the `roundingPriority` option has been [supported in browsers since 8/23](https://caniuse.com/mdn-javascript_builtins_intl_numberformat_numberformat_options_roundingpriority_parameter), but Typescript doesn't have support yet. It is tracked by the same issue linked above, and our type assertion serves as a workaround for this, too. By using this option, we can get rid of the separate `leadingZeroFormatter` in `DefaultUnitFormat` and simplify the logic. ## ๐Ÿงช Testing Existing tests pass. ## โœ… Checklist - [x] I have updated the project documentation to reflect my changes or determined no changes are needed. --- ...-9d33a263-abcb-4b8e-a5f1-1ab02c9b4974.json | 7 ++ .../nimble-components/src/dialog/index.ts | 12 +-- .../src/dialog/tests/dialog.spec.ts | 10 +- .../nimble-components/src/drawer/index.ts | 3 +- .../src/global-type-extensions.d.ts | 28 ++++++ ...table-column-number-text-matrix.stories.ts | 2 +- .../unit-format/decimal-unit-format.ts | 20 ++-- .../unit-format/default-unit-format.ts | 94 +++++-------------- .../tests/decimal-unit-format.spec.ts | 63 +++++-------- .../tests/default-unit-format.spec.ts | 34 +------ .../tests/test-scaled-unit-format.ts | 29 ++++++ 11 files changed, 123 insertions(+), 179 deletions(-) create mode 100644 change/@ni-nimble-components-9d33a263-abcb-4b8e-a5f1-1ab02c9b4974.json create mode 100644 packages/nimble-components/src/global-type-extensions.d.ts create mode 100644 packages/nimble-components/src/utilities/unit-format/tests/test-scaled-unit-format.ts diff --git a/change/@ni-nimble-components-9d33a263-abcb-4b8e-a5f1-1ab02c9b4974.json b/change/@ni-nimble-components-9d33a263-abcb-4b8e-a5f1-1ab02c9b4974.json new file mode 100644 index 0000000000..835e7f8e90 --- /dev/null +++ b/change/@ni-nimble-components-9d33a263-abcb-4b8e-a5f1-1ab02c9b4974.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Update usage of Intl.NumberFormat options", + "packageName": "@ni/nimble-components", + "email": "7282195+m-akinc@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/nimble-components/src/dialog/index.ts b/packages/nimble-components/src/dialog/index.ts index 8d61a9181e..c79e131ec5 100644 --- a/packages/nimble-components/src/dialog/index.ts +++ b/packages/nimble-components/src/dialog/index.ts @@ -17,16 +17,6 @@ declare global { } } -/** - * This is a workaround for an incomplete definition of the native dialog element. Remove when using Typescript >=4.8.3. - * https://github.com/microsoft/TypeScript/issues/48267 - * @internal - */ -export interface ExtendedDialog extends HTMLDialogElement { - showModal(): void; - close(): void; -} - /** * A nimble-styled dialog. */ @@ -65,7 +55,7 @@ export class Dialog extends FoundationElement { * * @internal */ - public readonly dialogElement!: ExtendedDialog; + public readonly dialogElement!: HTMLDialogElement; /** @internal */ @observable diff --git a/packages/nimble-components/src/dialog/tests/dialog.spec.ts b/packages/nimble-components/src/dialog/tests/dialog.spec.ts index 7569edb740..1dc9f0f293 100644 --- a/packages/nimble-components/src/dialog/tests/dialog.spec.ts +++ b/packages/nimble-components/src/dialog/tests/dialog.spec.ts @@ -1,6 +1,6 @@ import { html } from '@microsoft/fast-element'; import { fixture, Fixture } from '../../utilities/tests/fixture'; -import { Dialog, dialogTag, ExtendedDialog, UserDismissed } from '..'; +import { Dialog, dialogTag, UserDismissed } from '..'; import { waitForUpdatesAsync } from '../../testing/async-helpers'; // eslint-disable-next-line @typescript-eslint/no-invalid-void-type @@ -19,10 +19,10 @@ async function setup( } describe('Dialog', () => { - function nativeDialogElement(nimbleDialogElement: Dialog): ExtendedDialog { - return nimbleDialogElement.shadowRoot!.querySelector( - 'dialog' - ) as ExtendedDialog; + function nativeDialogElement( + nimbleDialogElement: Dialog + ): HTMLDialogElement { + return nimbleDialogElement.shadowRoot!.querySelector('dialog')!; } it('should export its tag', () => { diff --git a/packages/nimble-components/src/drawer/index.ts b/packages/nimble-components/src/drawer/index.ts index 16ed8b1e40..45306082c4 100644 --- a/packages/nimble-components/src/drawer/index.ts +++ b/packages/nimble-components/src/drawer/index.ts @@ -6,7 +6,6 @@ import { FoundationElement } from '@microsoft/fast-foundation'; import { eventAnimationEnd } from '@microsoft/fast-web-utilities'; -import type { ExtendedDialog } from '../dialog'; import { UserDismissed } from '../patterns/dialog/types'; import { styles } from './styles'; import { template } from './template'; @@ -36,7 +35,7 @@ export class Drawer extends FoundationElement { @attr({ attribute: 'prevent-dismiss', mode: 'boolean' }) public preventDismiss = false; - public dialog!: ExtendedDialog; + public dialog!: HTMLDialogElement; private closing = false; private resolveShow?: (reason: CloseReason | UserDismissed) => void; diff --git a/packages/nimble-components/src/global-type-extensions.d.ts b/packages/nimble-components/src/global-type-extensions.d.ts new file mode 100644 index 0000000000..3199f3958c --- /dev/null +++ b/packages/nimble-components/src/global-type-extensions.d.ts @@ -0,0 +1,28 @@ +// This file has modifications to global types used for nimble-components builds: +// - The types file should not be in the build output +// - You should not see a file with these types created in the dist build +// - Type additions added to this file should not appear in any public API definitions +// - The fields added to global types should not be observable in any built .d.ts file by TypeScript. To verify you can do a text search of generated .d.ts files in the dist folder for the fields being added and verify they are not associated with the augmented types. +// - This file should only impact the nimble-components library build and not users of nimble-components. Any type changes that impact clients should not be placed here. + +/** + * This is a workaround for an incomplete definition of the native dialog element. Remove when using Typescript >=4.8.3. + * https://github.com/microsoft/TypeScript/issues/48267 + * @internal + */ +interface HTMLDialogElement { + showModal(): void; + close(returnValue?: string): void; +} + +declare namespace Intl { + // roundingPriority has been supported by browsers since 8/23, but TypeScript still hasn't + // added it to the type definition. See https://github.com/microsoft/TypeScript/issues/56269 + interface NumberFormatOptions { + roundingPriority?: + | 'auto' + | 'morePrecision' + | 'lessPrecision' + | undefined; + } +} diff --git a/packages/nimble-components/src/table-column/number-text/tests/table-column-number-text-matrix.stories.ts b/packages/nimble-components/src/table-column/number-text/tests/table-column-number-text-matrix.stories.ts index 144f05c096..7568e1abe9 100644 --- a/packages/nimble-components/src/table-column/number-text/tests/table-column-number-text-matrix.stories.ts +++ b/packages/nimble-components/src/table-column/number-text/tests/table-column-number-text-matrix.stories.ts @@ -30,7 +30,7 @@ const data = [ }, { id: '1', - number: -9.54021 + number: -9.5402111111 }, { id: '2' diff --git a/packages/nimble-components/src/utilities/unit-format/decimal-unit-format.ts b/packages/nimble-components/src/utilities/unit-format/decimal-unit-format.ts index 3768a20529..d4233a6dde 100644 --- a/packages/nimble-components/src/utilities/unit-format/decimal-unit-format.ts +++ b/packages/nimble-components/src/utilities/unit-format/decimal-unit-format.ts @@ -18,7 +18,6 @@ export class DecimalUnitFormat extends UnitFormat { private readonly maximumFractionDigits: number; private readonly scaledUnitFormatters = new Map(); - private readonly tenPowDecimalDigits: number; public constructor( locale: string, @@ -33,10 +32,14 @@ export class DecimalUnitFormat extends UnitFormat { } ) { super(); + // Workaround to avoid a ts error about signDisplay not accepting the value 'negative'. + // It has been supported by browsers since 8/23, but TypeScript still hasn't + // added it to the type definitions. See https://github.com/microsoft/TypeScript/issues/56269 + const signDisplay = 'negative' as Intl.NumberFormatOptions['signDisplay']; const intlNumberFormatOptions = { maximumFractionDigits, minimumFractionDigits, - useGrouping: true + signDisplay }; for (const scaledUnit of unitScale.supportedScaledUnits) { this.scaledUnitFormatters.set( @@ -47,7 +50,6 @@ export class DecimalUnitFormat extends UnitFormat { }) ); } - this.tenPowDecimalDigits = 10 ** maximumFractionDigits; this.unitScale = unitScale; this.minimumFractionDigits = minimumFractionDigits; this.maximumFractionDigits = maximumFractionDigits; @@ -63,19 +65,9 @@ export class DecimalUnitFormat extends UnitFormat { protected tryFormat(number: number): string { const { scaledValue, scaledUnit } = this.unitScale.scaleNumber(number); - - const numberNormalized = this.willRoundToZero(scaledValue) - ? 0 - : scaledValue; const scaledUnitFormatter = this.scaledUnitFormatters.get( scaledUnit.scaleFactor )!; - return scaledUnitFormatter.format(numberNormalized); - } - - private willRoundToZero(number: number): boolean { - // Multiply the value by 10 raised to maximumFractionDigits so that Math.round can be used to emulate rounding to - // maximumFractionDigits decimal places. If that rounded value is 0, then the value will be rendered with only 0s. - return Math.round(number * this.tenPowDecimalDigits) === 0; + return scaledUnitFormatter.format(scaledValue); } } diff --git a/packages/nimble-components/src/utilities/unit-format/default-unit-format.ts b/packages/nimble-components/src/utilities/unit-format/default-unit-format.ts index 62a986395b..f10f34181a 100644 --- a/packages/nimble-components/src/utilities/unit-format/default-unit-format.ts +++ b/packages/nimble-components/src/utilities/unit-format/default-unit-format.ts @@ -3,7 +3,10 @@ import type { ScaledUnitFormat } from './scaled-unit-format/scaled-unit-format'; import type { UnitScale } from './unit-scale/unit-scale'; import { passthroughUnitScale } from './unit-scale/passthrough-unit-scale'; -type NumberStyle = 'default' | 'leadingZero' | 'exponential'; +// Workaround to avoid ts errors about signDisplay not accepting the value 'negative'. +// It has been supported by browsers since 8/23, but TypeScript still hasn't +// added it to the type definitions. See https://github.com/microsoft/TypeScript/issues/56269 +const signDisplay = 'negative' as Intl.NumberFormatOptions['signDisplay']; // Allow consistent pattern for defining Options and ResolvedOptions // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -28,27 +31,15 @@ export class DefaultUnitFormat extends UnitFormat { private static readonly exponentialUpperBound = 999999.5; private readonly unitScale: UnitScale; - - // Format options to use by default. It renders the number with a maximum of 6 signficant digits. + // Format options to use by default. It renders the number with a maximum of 6 signficant digits (including zero before decimal point). private readonly defaultIntlNumberFormatOptions: Intl.NumberFormatOptions = { maximumSignificantDigits: DefaultUnitFormat.maximumDigits, - useGrouping: true - }; - - private readonly defaultScaledUnitFormatters = new Map< - number, - ScaledUnitFormat - >(); - - // Format options to use for numbers that have leading zeros. It limits the number of rendered - // digits using 'maximumFractionDigits', which will result in less than 6 significant digits - // in order to render no more than 6 total digits. - private readonly leadingZeroIntlNumberFormatOptions: Intl.NumberFormatOptions = { maximumFractionDigits: DefaultUnitFormat.maximumDigits - 1, - useGrouping: true + roundingPriority: 'lessPrecision', + signDisplay }; - private readonly leadingZeroScaledUnitFormatters = new Map< + private readonly defaultScaledUnitFormatters = new Map< number, ScaledUnitFormat >(); @@ -57,7 +48,8 @@ export class DefaultUnitFormat extends UnitFormat { // for numbers with magintudes over 'exponentialUpperBound' or under 'exponentialLowerBound'. private readonly exponentialIntlNumberFormatOptions: Intl.NumberFormatOptions = { maximumSignificantDigits: DefaultUnitFormat.maximumDigits, - notation: 'scientific' + notation: 'scientific', + signDisplay }; private readonly exponentialScaledUnitFormatter: ScaledUnitFormat; @@ -77,14 +69,6 @@ export class DefaultUnitFormat extends UnitFormat { intlNumberFormatOptions: this.defaultIntlNumberFormatOptions }) ); - this.leadingZeroScaledUnitFormatters.set( - unit.scaleFactor, - unit.scaledUnitFormatFactory({ - locale, - intlNumberFormatOptions: - this.leadingZeroIntlNumberFormatOptions - }) - ); } this.exponentialScaledUnitFormatter = unitScale.baseScaledUnit.scaledUnitFormatFactory({ locale, @@ -100,52 +84,16 @@ export class DefaultUnitFormat extends UnitFormat { } protected tryFormat(number: number): string { - // Normalize +0 / -0 --> +0 - const numberNormalized = number === 0 ? 0 : number; - - const { scaledValue, scaledUnit } = this.unitScale.scaleNumber(numberNormalized); - - const numberStyle = this.resolveNumberStyle(scaledValue); - switch (numberStyle) { - case 'default': { - const scaledUnitFormatter = this.defaultScaledUnitFormatters.get( - scaledUnit.scaleFactor - )!; - return scaledUnitFormatter.format(scaledValue); - } - case 'leadingZero': { - const scaledUnitFormatter = this.leadingZeroScaledUnitFormatters.get( - scaledUnit.scaleFactor - )!; - return scaledUnitFormatter.format(scaledValue); - } - case 'exponential': { - const scaledUnitFormatter = this.exponentialScaledUnitFormatter; - return scaledUnitFormatter.format(numberNormalized); - } - default: - throw new Error('Unexpected number format style'); - } - } - - private resolveNumberStyle(number: number): NumberStyle { - if (number === 0) { - return 'default'; - } - - const absoluteValue = Math.abs(number); - if ( - absoluteValue >= DefaultUnitFormat.exponentialUpperBound - || absoluteValue < DefaultUnitFormat.exponentialLowerBound - ) { - return 'exponential'; - } - // Ideally, we could set 'roundingPriority: "lessPrecision"' with a formatter that has both 'maximumSignificantDigits' and - // 'maximumFractionDigits' configured instead of having two different formatters that we conditionally choose between. However, - // 'roundingPrioirty' is not supported yet in all browsers. - if (absoluteValue < 1) { - return 'leadingZero'; - } - return 'default'; + const { scaledValue, scaledUnit } = this.unitScale.scaleNumber(number); + + const absoluteValue = Math.abs(scaledValue); + const useExponential = absoluteValue !== 0 + && (absoluteValue >= DefaultUnitFormat.exponentialUpperBound + || absoluteValue < DefaultUnitFormat.exponentialLowerBound); + return useExponential + ? this.exponentialScaledUnitFormatter.format(number) + : this.defaultScaledUnitFormatters + .get(scaledUnit.scaleFactor)! + .format(scaledValue); } } diff --git a/packages/nimble-components/src/utilities/unit-format/tests/decimal-unit-format.spec.ts b/packages/nimble-components/src/utilities/unit-format/tests/decimal-unit-format.spec.ts index d5f0c1fb3f..27a3ed2cfd 100644 --- a/packages/nimble-components/src/utilities/unit-format/tests/decimal-unit-format.spec.ts +++ b/packages/nimble-components/src/utilities/unit-format/tests/decimal-unit-format.spec.ts @@ -1,13 +1,10 @@ /* eslint-disable max-classes-per-file */ import { parameterizeSpec } from '@ni/jasmine-parameterized'; import { DecimalUnitFormat } from '../decimal-unit-format'; -import { - ScaledUnit, - ScaledUnitFormatFactoryOptions -} from '../scaled-unit/scaled-unit'; -import { ScaledUnitFormat } from '../scaled-unit-format/scaled-unit-format'; +import { ScaledUnit } from '../scaled-unit/scaled-unit'; import { UnitScale } from '../unit-scale/unit-scale'; import { passthroughUnitScale } from '../unit-scale/passthrough-unit-scale'; +import { TestScaledUnitFormat } from './test-scaled-unit-format'; describe('DecimalUnitFormat', () => { const testCases = [ @@ -144,38 +141,22 @@ describe('DecimalUnitFormat', () => { }); describe('with unit', () => { - class TestScaledUnitFormat extends ScaledUnitFormat { - public constructor( - scaledUnitFormatFactoryOptions: ScaledUnitFormatFactoryOptions, - private readonly scaleFactor: number - ) { - super(scaledUnitFormatFactoryOptions); - } - - public static createFactory(scaleFactor: number) { - return ( - scaledUnitFormatFactoryOptions: ScaledUnitFormatFactoryOptions - ) => new TestScaledUnitFormat( - scaledUnitFormatFactoryOptions, - scaleFactor - ); - } - - public format(value: number): string { - return `${value} x${this.scaleFactor}`; - } - } - class TestUnitScale extends UnitScale { public constructor() { super([ new ScaledUnit( 0.001, - TestScaledUnitFormat.createFactory(0.001) + TestScaledUnitFormat.createTestFactory(0.001) + ), + new ScaledUnit( + 1, + TestScaledUnitFormat.createTestFactory(1) ), - new ScaledUnit(1, TestScaledUnitFormat.createFactory(1)), - new ScaledUnit(2, TestScaledUnitFormat.createFactory(2)), - new ScaledUnit(4, TestScaledUnitFormat.createFactory(4)) + new ScaledUnit( + 2, + TestScaledUnitFormat.createTestFactory(2) + ), + new ScaledUnit(4, TestScaledUnitFormat.createTestFactory(4)) ]); } } @@ -189,7 +170,7 @@ describe('DecimalUnitFormat', () => { expect(resolvedOptions.unitScale).toBe(passthroughUnitScale); }); - it('minimum configured less than max default', () => { + it('minimum configured less than default maximum', () => { const formatter = new DecimalUnitFormat('en', { minimumFractionDigits: 1 }); @@ -199,7 +180,7 @@ describe('DecimalUnitFormat', () => { expect(resolvedOptions.unitScale).toBe(passthroughUnitScale); }); - it('minimum configured greater than max default', () => { + it('minimum configured greater than default maximum', () => { const formatter = new DecimalUnitFormat('en', { minimumFractionDigits: 10 }); @@ -212,13 +193,13 @@ describe('DecimalUnitFormat', () => { it('all configured', () => { const unitScale = new TestUnitScale(); const formatter = new DecimalUnitFormat('en', { - minimumFractionDigits: 20, - maximumFractionDigits: 30, + minimumFractionDigits: 10, + maximumFractionDigits: 20, unitScale }); const resolvedOptions = formatter.resolvedOptions(); - expect(resolvedOptions.minimumFractionDigits).toBe(20); - expect(resolvedOptions.maximumFractionDigits).toBe(30); + expect(resolvedOptions.minimumFractionDigits).toBe(10); + expect(resolvedOptions.maximumFractionDigits).toBe(20); expect(resolvedOptions.unitScale).toBe(unitScale); }); }); @@ -227,17 +208,17 @@ describe('DecimalUnitFormat', () => { { name: 'does not double-convert the value when a unit is specified', value: 3, - expectedFormattedValue: '1.5 x2' + expectedFormattedValue: '1.50 x2' }, { - name: 'does not zero-round until after scaling value', + name: 'does not zero-round before scaling value', value: 0.001, - expectedFormattedValue: '1 x0.001' + expectedFormattedValue: '1.00 x0.001' }, { name: 'does zero-rounding after scaling value', value: -0.000004, - expectedFormattedValue: '0 x0.001' + expectedFormattedValue: '0.00 x0.001' } ] as const; parameterizeSpec(appendedLabelUnitTestCases, (spec, name, value) => { diff --git a/packages/nimble-components/src/utilities/unit-format/tests/default-unit-format.spec.ts b/packages/nimble-components/src/utilities/unit-format/tests/default-unit-format.spec.ts index 24e2cd087e..b216a2bc0e 100644 --- a/packages/nimble-components/src/utilities/unit-format/tests/default-unit-format.spec.ts +++ b/packages/nimble-components/src/utilities/unit-format/tests/default-unit-format.spec.ts @@ -1,13 +1,10 @@ /* eslint-disable max-classes-per-file */ import { parameterizeSpec } from '@ni/jasmine-parameterized'; import { DefaultUnitFormat } from '../default-unit-format'; -import { IntlNumberFormatScaledUnitFormat } from '../scaled-unit-format/intl-number-format-scaled-unit-format'; -import { - ScaledUnit, - ScaledUnitFormatFactoryOptions -} from '../scaled-unit/scaled-unit'; +import { ScaledUnit } from '../scaled-unit/scaled-unit'; import { UnitScale } from '../unit-scale/unit-scale'; import { passthroughUnitScale } from '../unit-scale/passthrough-unit-scale'; +import { TestScaledUnitFormat } from './test-scaled-unit-format'; describe('DefaultUnitFormat', () => { const testCases = [ @@ -232,28 +229,6 @@ describe('DefaultUnitFormat', () => { }); describe('with unit', () => { - class TestScaledUnitFormat extends IntlNumberFormatScaledUnitFormat { - public constructor( - scaledUnitFormatFactoryOptions: ScaledUnitFormatFactoryOptions, - private readonly scaleFactor: number - ) { - super(scaledUnitFormatFactoryOptions, {}); - } - - public static createTestFactory(scaleFactor: number) { - return ( - scaledUnitFormatFactoryOptions: ScaledUnitFormatFactoryOptions - ): TestScaledUnitFormat => new TestScaledUnitFormat( - scaledUnitFormatFactoryOptions, - scaleFactor - ); - } - - public override format(value: number): string { - return `${super.format(value)} x${this.scaleFactor}`; - } - } - class TestUnitScale extends UnitScale { public constructor() { super([ @@ -303,11 +278,6 @@ describe('DefaultUnitFormat', () => { value: 2000000, expectedFormattedValue: '2,000 x1000' }, - { - name: 'uses unit-scaled value when deciding whether to format with leading-zero formatter', - value: 0.123456789, - expectedFormattedValue: '12.3457 x0.01' // rather than '12.34568 x0.01' - }, { name: 'always uses base unit if exponential notation is used', value: 2000000000, diff --git a/packages/nimble-components/src/utilities/unit-format/tests/test-scaled-unit-format.ts b/packages/nimble-components/src/utilities/unit-format/tests/test-scaled-unit-format.ts new file mode 100644 index 0000000000..61ea1ce65d --- /dev/null +++ b/packages/nimble-components/src/utilities/unit-format/tests/test-scaled-unit-format.ts @@ -0,0 +1,29 @@ +import { IntlNumberFormatScaledUnitFormat } from '../scaled-unit-format/intl-number-format-scaled-unit-format'; +import type { ScaledUnitFormatFactoryOptions } from '../scaled-unit/scaled-unit'; + +/** + * A ScaledUnitFormat that behaves like IntlNumberFormatScaledUnitFormat but also includes the scaleFactor in the formatted number + */ +export class TestScaledUnitFormat extends IntlNumberFormatScaledUnitFormat { + protected constructor( + scaledUnitFormatFactoryOptions: ScaledUnitFormatFactoryOptions, + private readonly scaleFactor: number + ) { + super(scaledUnitFormatFactoryOptions, {}); + } + + public static createTestFactory( + scaleFactor: number + ): (options: ScaledUnitFormatFactoryOptions) => TestScaledUnitFormat { + return ( + scaledUnitFormatFactoryOptions: ScaledUnitFormatFactoryOptions + ) => new TestScaledUnitFormat( + scaledUnitFormatFactoryOptions, + scaleFactor + ); + } + + public override format(value: number): string { + return `${super.format(value)} x${this.scaleFactor}`; + } +}