diff --git a/src/lib/utils.ts b/src/lib/utils.ts index faf252e..5296904 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -28,10 +28,10 @@ export function formatAmount( const abs = Math.abs(num); // Tiny positive numbers: show '< 0.0001' for values > 0 and < 0.0001 if (abs > 0 && abs < 0.0001) return `< 0.0001`; - // Cap maximum decimals to 4 to avoid very long fractional displays + // Cap decimals to valid range (0-4) and ensure min <= max const maxDecimals = Math.min(4, Math.max(0, decimals)); return num.toLocaleString("en-US", { - minimumFractionDigits: 2, + minimumFractionDigits: Math.min(2, maxDecimals), maximumFractionDigits: maxDecimals, }); } @@ -51,9 +51,10 @@ export function formatCurrency( return `${currencySymbol}${abbreviateNumber(num, 1)}`; } // For smaller numbers, show with requested decimals (no grouping abbreviation) + const safeDecimals = Math.max(0, decimals); return `${currencySymbol}${num.toLocaleString("en-US", { - minimumFractionDigits: decimals, - maximumFractionDigits: decimals, + minimumFractionDigits: safeDecimals, + maximumFractionDigits: safeDecimals, })}`; } @@ -64,9 +65,10 @@ export function formatPercent( if (value === null || value === undefined) return `0%`; const num = Number(value); if (!Number.isFinite(num)) return `0%`; + const safeDecimals = Math.max(0, decimals); return `${Number(value).toLocaleString("en-US", { - minimumFractionDigits: decimals, - maximumFractionDigits: decimals, + minimumFractionDigits: safeDecimals, + maximumFractionDigits: safeDecimals, })}%`; } diff --git a/test/lib/utils.test.ts b/test/lib/utils.test.ts index f17fbb5..e8ddee2 100644 --- a/test/lib/utils.test.ts +++ b/test/lib/utils.test.ts @@ -80,6 +80,21 @@ describe("formatAmount", () => { it("should respect custom decimals", () => { expect(formatAmount(1.123456, 2)).toBe("1.12"); }); + + it("should not throw RangeError when decimals is 0", () => { + expect(() => formatAmount(100000, 0)).not.toThrow(); + expect(formatAmount(100000, 0)).toBe("100,000"); + }); + + it("should not throw RangeError when decimals is 1", () => { + expect(() => formatAmount(1234.5, 1)).not.toThrow(); + expect(formatAmount(1234.5, 1)).toBe("1,234.5"); + }); + + it("should not throw RangeError when decimals is negative", () => { + expect(() => formatAmount(1234, -1)).not.toThrow(); + expect(formatAmount(1234, -1)).toBe("1,234"); + }); }); describe("formatCurrency", () => { @@ -110,6 +125,16 @@ describe("formatCurrency", () => { it("should not abbreviate when disabled", () => { expect(formatCurrency(5000, 2, "$", false)).toBe("$5,000.00"); }); + + it("should not throw RangeError when decimals is 0", () => { + expect(() => formatCurrency(100, 0)).not.toThrow(); + expect(formatCurrency(100, 0, "$", false)).toBe("$100"); + }); + + it("should not throw RangeError when decimals is negative", () => { + expect(() => formatCurrency(100, -1)).not.toThrow(); + expect(formatCurrency(100, -1, "$", false)).toBe("$100"); + }); }); describe("formatPercent", () => { @@ -132,6 +157,11 @@ describe("formatPercent", () => { it("should respect custom decimals", () => { expect(formatPercent(33.3333, 1)).toBe("33.3%"); }); + + it("should not throw RangeError when decimals is negative", () => { + expect(() => formatPercent(50, -1)).not.toThrow(); + expect(formatPercent(50, -1)).toBe("50%"); + }); }); describe("maskString", () => {