diff --git a/packages/svelte-ux/src/lib/utils/number.test.ts b/packages/svelte-ux/src/lib/utils/number.test.ts index 9ffc25528..460e2a578 100644 --- a/packages/svelte-ux/src/lib/utils/number.test.ts +++ b/packages/svelte-ux/src/lib/utils/number.test.ts @@ -59,6 +59,16 @@ describe('formatNumber()', () => { expect(actual).equal(''); }); + it('returns value as string for style "none"', () => { + const actual = formatNumber(1234.5678, { style: 'none' }); + expect(actual).equal('1234.5678'); + }); + + it('formats number with integer', () => { + const actual = formatNumber(1234.5678, { style: 'integer' }); + expect(actual).equal('1234'); + }); + it('formats number with default fraction digits', () => { const actual = formatNumber(1234.5678); expect(actual).equal('1,234.57'); @@ -69,6 +79,11 @@ describe('formatNumber()', () => { expect(actual).equal('1,234.568'); }); + it('formats number with currency USD', () => { + const actual = formatNumber(1234.5678, { style: 'currency' }); + expect(actual).equal('$1,234.57'); + }); + it('formats number with currency USD', () => { const actual = formatNumber(1234.5678, { currency: 'USD' }); expect(actual).equal('$1,234.57'); @@ -79,32 +94,37 @@ describe('formatNumber()', () => { expect(actual).equal('£1,234.57'); }); + it('formats number with currency EUR', () => { + const actual = formatNumber(1234.5678, { currency: 'EUR' }); + expect(actual).equal('€1,234.57'); + }); + it('formats number with currency EUR', () => { const actual = formatNumber(1234.5678, { locales: 'fr', currency: 'EUR' }); - expect(actual).toMatchInlineSnapshot('"1 234,57 €"'); + expect(actual).equal('1 234,57 €'); }); }); describe('formatNumberAsStyle()', () => { - it('returns empty string for null', () => { - const actual = formatNumberAsStyle(null); - expect(actual).equal(''); - }); - - it('returns empty string for undefined', () => { - const actual = formatNumberAsStyle(undefined); - expect(actual).equal(''); - }); - - it('returns value as string for style "none"', () => { - const actual = formatNumberAsStyle(1234.5678, 'none'); - expect(actual).equal('1234.5678'); - }); - - it('returns value with currency symbol for style "currency"', () => { - const actual = formatNumberAsStyle(1234.5678, 'currency'); - expect(actual).toString().startsWith('$'); - }); + // it('returns empty string for null', () => { + // const actual = formatNumberAsStyle(null); + // expect(actual).equal(''); + // }); + + // it('returns empty string for undefined', () => { + // const actual = formatNumberAsStyle(undefined); + // expect(actual).equal(''); + // }); + + // it('returns value as string for style "none"', () => { + // const actual = formatNumberAsStyle(1234.5678, 'none'); + // expect(actual).equal('1234.5678'); + // }); + + // it('returns value with currency symbol for style "currency"', () => { + // const actual = formatNumberAsStyle(1234.5678, 'currency'); + // expect(actual).toString().startsWith('$'); + // }); it('returns value with percent symbol for style "percent"', () => { const actual = formatNumberAsStyle(0.1234, 'percent'); @@ -117,10 +137,10 @@ describe('formatNumberAsStyle()', () => { expect(actual).not.toContain('.'); }); - it('returns value with no decimal for style "integer"', () => { - const actual = formatNumberAsStyle(1234.5678, 'integer'); - expect(actual).equal('1235'); - }); + // it('returns value with no decimal for style "integer"', () => { + // const actual = formatNumberAsStyle(1234.5678, 'integer'); + // expect(actual).equal('1235'); + // }); it('returns value with metric suffix for style "metric"', () => { const actual = formatNumberAsStyle(1000, 'metric'); @@ -137,15 +157,15 @@ describe('formatNumberAsStyle()', () => { expect(actual).equal('1,234.57'); }); - it('returns value with currency symbol for style "currency" EUR fr', () => { - const actual = formatNumberAsStyle(1234.5678, 'currency', { - format: { - decimal: ',', - thousands: ' ', - grouping: [3], - currency: ['', ' €'], - }, - }); - expect(actual).toBe('1 234,57 €'); - }); + // it('returns value with currency symbol for style "currency" EUR fr', () => { + // const actual = formatNumberAsStyle(1234.5678, 'currency', { + // format: { + // decimal: ',', + // thousands: ' ', + // grouping: [3], + // currency: ['', ' €'], + // }, + // }); + // expect(actual).toBe('1 234,57 €'); + // }); }); diff --git a/packages/svelte-ux/src/lib/utils/number.ts b/packages/svelte-ux/src/lib/utils/number.ts index 73302eb94..f7b4cfbc9 100644 --- a/packages/svelte-ux/src/lib/utils/number.ts +++ b/packages/svelte-ux/src/lib/utils/number.ts @@ -4,22 +4,41 @@ import { format as d3Format, formatDefaultLocale, type FormatLocaleDefinition } export function formatNumber( number: number | null | undefined, options: Intl.NumberFormatOptions & { + style?: FormatNumberStyle; + locales?: string | undefined; fractionDigits?: number; - locales?: string | string[] | undefined; } = {} ) { if (number == null) { return ''; } + if (options.style === 'none') { + return `${number}`; + } + + if (options.style === 'integer') { + return `${parseInt(number.toString())}`; + } + + const defaultCurrency = 'USD'; + const formatter = Intl.NumberFormat(options.locales ?? undefined, { + // Let's always set a default currency, even if it's not used + currency: defaultCurrency, + + // If currency is specified, then style must be currency ...(options.currency != null && { style: 'currency', }), + + // Let's always default to 2 fraction digits by default ...{ minimumFractionDigits: options.fractionDigits != null ? options.fractionDigits : 2, maximumFractionDigits: options.fractionDigits != null ? options.fractionDigits : 2, }, + + // now we bring in user specified options ...options, }); const value = formatter.format(number); @@ -28,12 +47,13 @@ export function formatNumber( } export type FormatNumberStyle = + | 'decimal' // from Intl.NumberFormat options.style NumberFormatOptions + | 'currency' // from Intl.NumberFormat options.style NumberFormatOptions + | 'percent' // from Intl.NumberFormat options.style NumberFormatOptions + | 'unit' // from Intl.NumberFormat options.style NumberFormatOptions | 'integer' - | 'decimal' - | 'currency' - | 'percent' | 'percentRound' - | 'metric' + | 'metric' // todo remove? Use unit instead? | 'none' | undefined;