Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions apps/www/src/content/docs/components/amount/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export const playground = {
groupDigits: {
type: 'checkbox',
defaultValue: true
},
hideCurrency: {
type: 'checkbox',
defaultValue: false
}
},
getCode
Expand Down Expand Up @@ -119,6 +123,17 @@ export const currencyDisplayDemo = {
`
};

export const hideCurrencyDemo = {
type: 'code',
code: `
<Flex gap={4}>
<Amount value={1299} hideCurrency /> {/* 12.99 */}
<Amount value={1299} currency="JPY" hideCurrency /> {/* 1,299 */}
<Amount value={1299} hideCurrency currencyDisplay="code" />{/* 12.99 — currencyDisplay is ignored */}
</Flex>
`
};

export const groupDigitsDemo = {
type: 'code',
code: `
Expand Down Expand Up @@ -149,13 +164,24 @@ export const withTextDemo = {
export const largeNumbersDemo = {
type: 'code',
code: `
<Flex gap={4}>
{/* For large numbers, use string to maintain precision */}
<Flex direction='column' gap={4}>
{/*
For large numbers, use string (supports decimals) or bigint (integer-only)
to maintain precision
*/}
<Amount value="999999999999999" /> {/* $9,999,999,999,999.99 */}
<Amount value="10000100091636935" valueInMinorUnits={false} hideDecimals /> {/* $10,000,100,091,636,935 */}

{/* Numbers exceeding safe integer limit will show warning in console */}
<Amount value={999999999999999} /> {/* Will show warning */}
<Amount value="10000100091636935"
valueInMinorUnits={false} hideDecimals />{/* $10,000,100,091,636,935 */}

{/*
BigInt is always treated as major units — valueInMinorUnits is ignored
*/}
<Amount value={BigInt("9999999999999999999")} valueInMinorUnits={false} />{/* $9,999,999,999,999,999,999.00 */}

{/*
Numbers exceeding safe integer limit will show warning in console
*/}
<Amount value={99999999999999999} />{/* Exceeds Number.MAX_SAFE_INTEGER (~9 × 10^15) — logs a console warning */}
</Flex>
`
};
9 changes: 8 additions & 1 deletion apps/www/src/content/docs/components/amount/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
localeDemo,
hideDecimalsDemo,
currencyDisplayDemo,
hideCurrencyDemo,
groupDigitsDemo,
withTextDemo,
largeNumbersDemo,
Expand Down Expand Up @@ -61,13 +62,19 @@ Formats and displays monetary values with locale and currency support.

<Demo data={currencyDisplayDemo} />

### hideCurrency

Render only the formatted number, without any currency symbol, code, or name. Locale-driven separators and decimal places are preserved.

<Demo data={hideCurrencyDemo} />

### groupDigits

<Demo data={groupDigitsDemo} />

### Large Numbers

For numbers larger than JavaScript's safe integer limit (2^53 - 1), pass the value as a string to maintain precision.
For numbers larger than JavaScript's safe integer limit (2^53 - 1), pass the value as a `string` (supports decimals) or a `bigint` (integer-only). BigInt values are always treated as already in major units, so `valueInMinorUnits` is ignored for them.

<Demo data={largeNumbersDemo} />

Expand Down
22 changes: 18 additions & 4 deletions apps/www/src/content/docs/components/amount/props.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
export interface AmountProps {
/**
* The monetary value to display
* For large numbers (> 2^53), pass the value as string to maintain precision
* The monetary value to display.
* For exact precision beyond 2^53, pass either:
* - a `string` — supports decimals (e.g. "1299" or "12.99")
* - a `bigint` — integer-only; treated as already in major units, so
* `valueInMinorUnits` is ignored when value is a bigint
* @default 0
* @example
* valueInMinorUnits=true: 1299 => "$12.99"
* valueInMinorUnits=false: 12.99 => "$12.99"
* Large numbers: "999999999999999" => "$9,999,999,999,999.99"
* Large strings: "999999999999999" => "$9,999,999,999,999.99"
* BigInt: 1299n => "$1,299.00" (always major units)
*/
value: number | string;
value: number | string | bigint;

/**
* ISO 4217 currency code
Expand Down Expand Up @@ -65,4 +69,14 @@ export interface AmountProps {
* @default true
*/
groupDigits?: boolean;

/**
* Render the formatted number without a currency symbol, code, or name.
* Locale-driven separators, grouping, and fraction digits are preserved.
* When true, `currencyDisplay` is ignored.
* @default false
* @example
* <Amount value={1299} hideCurrency /> => "12.99"
*/
hideCurrency?: boolean;
}
65 changes: 65 additions & 0 deletions packages/raystack/components/amount/__tests__/amount.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,5 +203,70 @@ describe('Amount', () => {
expect(element).toBeInTheDocument();
consoleSpy.mockRestore();
});

it('handles negative string values in minor units', () => {
render(<Amount value='-1299' />);
expect(screen.getByText('-$12.99')).toBeInTheDocument();
});
});

describe('BigInt support', () => {
it('formats a bigint as major units regardless of valueInMinorUnits', () => {
render(<Amount value={1299n} />);
expect(screen.getByText('$1,299.00')).toBeInTheDocument();
});

it('matches the major-units result when valueInMinorUnits is false', () => {
render(<Amount value={1299n} valueInMinorUnits={false} />);
expect(screen.getByText('$1,299.00')).toBeInTheDocument();
});

it('preserves precision beyond Number.MAX_SAFE_INTEGER', () => {
render(<Amount value={9999999999999999999n} valueInMinorUnits={false} />);
expect(
screen.getByText('$9,999,999,999,999,999,999.00')
).toBeInTheDocument();
});

it('formats negative bigint values', () => {
render(<Amount value={-1299n} />);
expect(screen.getByText('-$1,299.00')).toBeInTheDocument();
});

it('formats bigint with a zero-decimal currency', () => {
render(<Amount value={1299n} currency='JPY' locale='en-US' />);
expect(screen.getByText('¥1,299')).toBeInTheDocument();
});

it('does not warn about safe integer limit for bigint values', () => {
const consoleSpy = vi
.spyOn(console, 'warn')
.mockImplementation(() => null);
render(<Amount value={9999999999999999999n} valueInMinorUnits={false} />);
expect(consoleSpy).not.toHaveBeenCalled();
consoleSpy.mockRestore();
});
});

describe('hideCurrency', () => {
it('hides the currency symbol while preserving formatting', () => {
render(<Amount value={1299} hideCurrency />);
expect(screen.getByText('12.99')).toBeInTheDocument();
});

it('hides currency for bigint values', () => {
render(<Amount value={1299n} hideCurrency />);
expect(screen.getByText('1,299')).toBeInTheDocument();
});

it('overrides currencyDisplay when set', () => {
render(<Amount value={1299} hideCurrency currencyDisplay='code' />);
expect(screen.getByText('12.99')).toBeInTheDocument();
});

it('respects the currency for decimal-place math even when hidden', () => {
render(<Amount value={1299} currency='JPY' hideCurrency />);
expect(screen.getByText('1,299')).toBeInTheDocument();
});
});
Comment on lines +251 to 271
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

hideCurrency test suite covers only non-round amounts

All four hideCurrency tests use value={1299} (USD → 12.99) or a zero-decimal currency (JPY). None exercise a round amount like value={1200} (USD → 12.00) where the fraction digit discrepancy from the major issue above would manifest. The coverage gap means the bug is undetected by the test suite even though the docs promise "fraction digits are preserved."

These tests will need to be updated once the fix proposed in the amount.tsx comment is applied.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/raystack/components/amount/__tests__/amount.test.tsx` around lines
251 - 271, The hideCurrency test suite only covers non-round amounts; add tests
that assert behavior for round amounts (e.g., render <Amount value={1200}
hideCurrency /> and expect "12.00") and for bigint round values (e.g.,
value={1200n} hideCurrency expecting "1,200") to ensure fraction digits are
preserved, and duplicate the override and zero-decimal (JPY) cases with round
values (e.g., currencyDisplay='code' and currency='JPY' with value=1200) so the
suite catches the fraction-digit regression in the Amount component.

});
97 changes: 69 additions & 28 deletions packages/raystack/components/amount/amount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import { type ComponentProps } from 'react';

export interface AmountProps extends ComponentProps<'span'> {
/**
* The monetary value to display
* For large numbers (> 2^53), pass the value as string to maintain precision
\ * The monetary value to display.
* For exact precision beyond 2^53, pass either:
* - a `string` — supports decimals (e.g. "1299" or "12.99")
* - a `bigint` — integer-only; treated as already in major units, so
* `valueInMinorUnits` is ignored when value is a bigint
* @default 0
* @example
* valueInMinorUnits=true: 1299 => "$12.99"
* valueInMinorUnits=false: 12.99 => "$12.99"
* bigint: 1299n => "$1,299.00" (always major units)
*/
value: number | string;
value: number | string | bigint;

/**
* ISO 4217 currency code
Expand Down Expand Up @@ -66,6 +70,16 @@ export interface AmountProps extends ComponentProps<'span'> {
* @default true
*/
groupDigits?: boolean;

/**
* Render the formatted number without a currency symbol, code, or name.
* Locale-driven separators, grouping, and fraction digits are preserved.
* When true, `currencyDisplay` is ignored.
* @default false
* @example
* <Amount value={1299} hideCurrency /> => "12.99"
*/
hideCurrency?: boolean;
}

/**
Expand Down Expand Up @@ -151,6 +165,7 @@ export const Amount = ({
maximumFractionDigits,
groupDigits = true,
valueInMinorUnits = true,
hideCurrency = false,
...props
}: AmountProps) => {
try {
Expand All @@ -160,7 +175,7 @@ export const Amount = ({
) {
console.warn(
`Warning: The number ${value} exceeds JavaScript's safe integer limit (${Number.MAX_SAFE_INTEGER}). ` +
'For large numbers, pass the value as a string to maintain precision.'
'For large numbers, pass the value as a bigint or string to maintain precision.'
);
}

Expand All @@ -171,37 +186,63 @@ export const Amount = ({

const decimals = getCurrencyDecimals(validCurrency);

// Handle minor units - use string manipulation for strings and Math.pow for numbers
const baseValue =
valueInMinorUnits && decimals > 0
? typeof value === 'string'
? value.slice(0, -decimals) + '.' + value.slice(-decimals)
: value / Math.pow(10, decimals)
: value;

// Remove decimals if hideDecimals is true - handle string and number separately
// Note: Not all numbers passed is converted to string as methods like Math.trunc
// or toString cannot handle large numbers thus, we need to handle it separately (large numbers passed in value throws console warning).
const finalBaseValue = hideDecimals
? typeof baseValue === 'string'
? baseValue.split('.')[0]
: Math.trunc(baseValue)
: baseValue;

const formattedValue = new Intl.NumberFormat(locale, {
style: 'currency' as const,
currency: validCurrency.toUpperCase(),
currencyDisplay,
// Convert minor → major units. Three input shapes: bigint, string, number.
// bigint is always treated as already in major units (it cannot represent
// fractions), so `valueInMinorUnits` is ignored for bigint.
let baseValue: number | string | bigint;
if (typeof value === 'bigint') {
baseValue = value;
} else if (valueInMinorUnits && decimals > 0) {
if (typeof value === 'string') {
const isNegative = value.startsWith('-');
const digits = isNegative ? value.slice(1) : value;
const padded = digits.padStart(decimals + 1, '0');
const major = padded.slice(0, -decimals);
const minor = padded.slice(-decimals);
baseValue = `${isNegative ? '-' : ''}${major}.${minor}`;
} else {
baseValue = value / Math.pow(10, decimals);
}
} else {
baseValue = value;
}

// Remove decimals when hideDecimals is true. bigint has no decimals, so
// it's a no-op there.
let finalBaseValue: number | string | bigint;
if (!hideDecimals) {
finalBaseValue = baseValue;
} else if (typeof baseValue === 'bigint') {
finalBaseValue = baseValue;
} else if (typeof baseValue === 'string') {
finalBaseValue = baseValue.split('.')[0];
} else {
finalBaseValue = Math.trunc(baseValue);
}

const formatOptions: Intl.NumberFormatOptions = {
minimumFractionDigits: hideDecimals ? 0 : minimumFractionDigits,
maximumFractionDigits: hideDecimals ? 0 : maximumFractionDigits,
useGrouping: groupDigits
useGrouping: groupDigits,
...(hideCurrency
? { style: 'decimal' }
: {
style: 'currency',
currency: validCurrency.toUpperCase(),
currencyDisplay
})
};
Comment on lines +223 to +234
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

hideCurrency=true does not preserve currency-driven fraction digits

The spread { style: 'decimal' } for hideCurrency doesn't override minimumFractionDigits, so it inherits undefined from the base object. For style: 'decimal', the default minimumFractionDigits is 0, whereas the default maximum digit value is 3 for plain values, 2 for currency. This means for round amounts, trailing zeros are stripped: value={1200} (USD minor units) formats to "12" instead of "12.00", directly contradicting the JSDoc comment on Line 77 ("fraction digits are preserved") and the PR summary ("fraction-digits remain").

The test suite covers only value={1299} (which produces non-zero decimal digits 12.99), so this discrepancy is invisible in tests. The same issue affects bigint values: 1299n with hideCurrency=false gives "$1,299.00" but hideCurrency=true gives "1,299".

🛠️ Proposed fix — propagate `decimals` as default in the `hideCurrency` branch
-    const formatOptions: Intl.NumberFormatOptions = {
-      minimumFractionDigits: hideDecimals ? 0 : minimumFractionDigits,
-      maximumFractionDigits: hideDecimals ? 0 : maximumFractionDigits,
-      useGrouping: groupDigits,
-      ...(hideCurrency
-        ? { style: 'decimal' }
-        : {
-            style: 'currency',
-            currency: validCurrency.toUpperCase(),
-            currencyDisplay
-          })
-    };
+    const formatOptions: Intl.NumberFormatOptions = {
+      useGrouping: groupDigits,
+      ...(hideCurrency
+        ? {
+            style: 'decimal',
+            minimumFractionDigits: hideDecimals ? 0 : (minimumFractionDigits ?? decimals),
+            maximumFractionDigits: hideDecimals ? 0 : (maximumFractionDigits ?? decimals),
+          }
+        : {
+            style: 'currency',
+            currency: validCurrency.toUpperCase(),
+            currencyDisplay,
+            minimumFractionDigits: hideDecimals ? 0 : minimumFractionDigits,
+            maximumFractionDigits: hideDecimals ? 0 : maximumFractionDigits,
+          })
+    };

After this fix, also update the bigint hideCurrency test expectation:

- expect(screen.getByText('1,299')).toBeInTheDocument();
+ expect(screen.getByText('1,299.00')).toBeInTheDocument();

And add a coverage case for round amounts:

it('preserves currency fraction digits for round amounts', () => {
  render(<Amount value={1200} hideCurrency />);
  expect(screen.getByText('12.00')).toBeInTheDocument();
});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const formatOptions: Intl.NumberFormatOptions = {
minimumFractionDigits: hideDecimals ? 0 : minimumFractionDigits,
maximumFractionDigits: hideDecimals ? 0 : maximumFractionDigits,
useGrouping: groupDigits
useGrouping: groupDigits,
...(hideCurrency
? { style: 'decimal' }
: {
style: 'currency',
currency: validCurrency.toUpperCase(),
currencyDisplay
})
};
const formatOptions: Intl.NumberFormatOptions = {
useGrouping: groupDigits,
...(hideCurrency
? {
style: 'decimal',
minimumFractionDigits: hideDecimals ? 0 : (minimumFractionDigits ?? decimals),
maximumFractionDigits: hideDecimals ? 0 : (maximumFractionDigits ?? decimals),
}
: {
style: 'currency',
currency: validCurrency.toUpperCase(),
currencyDisplay,
minimumFractionDigits: hideDecimals ? 0 : minimumFractionDigits,
maximumFractionDigits: hideDecimals ? 0 : maximumFractionDigits,
})
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/raystack/components/amount/amount.tsx` around lines 223 - 234, The
current formatOptions object (in Amount component) leaves
minimumFractionDigits/maximumFractionDigits undefined when hideCurrency is true,
causing decimal digits to be stripped for round values; update the hideCurrency
branch inside formatOptions so it sets style: 'decimal' and also explicitly
applies the currency-driven fraction digits (use minimumFractionDigits and
maximumFractionDigits or the computed decimals value used elsewhere) so decimals
are preserved when hideCurrency=true; adjust tests for bigint expectations and
add a new test case asserting round amounts (e.g., value=1200) still render the
expected trailing zeros.


const formattedValue = new Intl.NumberFormat(
locale,
formatOptions
// @ts-expect-error TS lib types omit `string` from format() params, but Intl.NumberFormat accepts numeric strings at runtime — needed for large values that would lose precision as `number`.
} as Intl.NumberFormatOptions).format(finalBaseValue);
).format(finalBaseValue);
Comment on lines 239 to +240
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n packages/raystack/components/amount/amount.tsx | sed -n '230,250p'

Repository: raystack/apsara

Length of output: 860


🏁 Script executed:

find . -name "package.json" -type f | head -5

Repository: raystack/apsara

Length of output: 206


🏁 Script executed:

cat package.json | jq '.browserslist, .engines, .targets' 2>/dev/null || cat package.json | grep -A 5 -E '(browserslist|engines|targets)'

Repository: raystack/apsara

Length of output: 90


🏁 Script executed:

cat packages/raystack/package.json | jq '.browserslist, .engines, .targets, .exports, .main' 2>/dev/null || cat packages/raystack/package.json

Repository: raystack/apsara

Length of output: 691


🌐 Web query:

Intl.NumberFormat V3 browser support timeline string precision ES2023

💡 Result:

Intl.NumberFormat V3 refers to the ECMA-402 proposal adding advanced features like roundingPriority ("auto", "morePrecision", "lessPrecision"), roundingMode, and enhanced string precision control via minimumSignificantDigits/maximumSignificantDigits (1-21 range) in conjunction with fractionDigits, resolving conflicts via roundingPriority. These enable precise string-based number formatting without floating-point loss, key for ES2023 Intl updates.[1][2][3][4] Browser support timeline (full V3 features like roundingMode, roundingPriority): - Chrome/Edge: 106+ (Oct 2022)[1][5][6] - Firefox: 93 (initial), full 116+ (2023)[1][6] - Safari: 15.4+ (Mar 2022)[1][6] By 2026-05-07, universally supported in modern browsers (global usage >98%). Basic significantDigits supported earlier (Chrome 24+, Firefox 29+, Safari 10+).[7] Example for string precision: new Intl.NumberFormat('en-US', { maximumSignificantDigits: 3, roundingPriority: 'lessPrecision' }).format('1234.56789'); // "1,230" (respects both sig/maxFraction via priority)[3][4] Official spec in ECMAScript 2023 Intl API (10.0).[8]

Citations:


🏁 Script executed:

grep -r "Amount" packages/raystack/package.json | head -10

Repository: raystack/apsara

Length of output: 41


🏁 Script executed:

grep -B5 "finalBaseValue" packages/raystack/components/amount/amount.tsx | head -20

Repository: raystack/apsara

Length of output: 826


🏁 Script executed:

cat packages/raystack/README.md 2>/dev/null | head -50 || echo "No README found"

Repository: raystack/apsara

Length of output: 1785


🏁 Script executed:

find . -maxdepth 2 -name ".browserslistrc" -o -name "tsconfig.json" -o -name "tsconfig.build.json" -o -name "vite.config.ts" -o -name "tsup.config.ts" 2>/dev/null | head -10

Repository: raystack/apsara

Length of output: 41


🏁 Script executed:

cat tsconfig.json | jq '.compilerOptions.lib, .compilerOptions.target, .compilerOptions.useDefineForClassFields' 2>/dev/null || grep -A 5 '"target"' tsconfig.json

Repository: raystack/apsara

Length of output: 105


🏁 Script executed:

find packages/raystack -name "tsconfig.json" -o -name "vite.config.ts" -o -name "tsup.config.ts" 2>/dev/null

Repository: raystack/apsara

Length of output: 91


🏁 Script executed:

find . -maxdepth 3 -name ".browserslistrc" 2>/dev/null

Repository: raystack/apsara

Length of output: 41


🏁 Script executed:

cat packages/raystack/tsconfig.json 2>/dev/null || echo "Not found"

Repository: raystack/apsara

Length of output: 568


🏁 Script executed:

cat packages/tools-config/tsconfig/react.json 2>/dev/null || find . -path "*tools-config*" -name "*.json" -type f | head -10

Repository: raystack/apsara

Length of output: 310


🏁 Script executed:

find . -maxdepth 2 -name "*.config.ts" -o -name "*.config.js" -type f | grep -E "(vite|tsup|esbuild|webpack)" | head -5

Repository: raystack/apsara

Length of output: 41


🏁 Script executed:

cat packages/raystack/package.json | jq '.browserslist' 2>/dev/null

Repository: raystack/apsara

Length of output: 64


🏁 Script executed:

cat packages/tools-config/tsconfig/base.json 2>/dev/null | head -30

Repository: raystack/apsara

Length of output: 580


🌐 Web query:

Intl.NumberFormat string parameter support es5 es6 when added specification history

💡 Result:

Intl.NumberFormat supports string parameters for the locales argument (a BCP 47 language tag string or array of strings) and accepts an options object as the second parameter. This API is not part of ES5 (ECMA-262 5th edition, 2009), which has no Intl object or NumberFormat.[1][2] It was first specified in ECMA-402 1st edition (December 2012), the ECMAScript Internationalization API Specification, with the constructor defined as Intl.NumberFormat([locales [, options ]]), where locales can be a string.[3][4][5][6] ECMA-402 1st edition aligned with ES6 (ECMA-262 6th edition, June 2015) development timeline, but the core API (including string locales support) was present from the initial 2012 spec and unchanged in basic constructor signature across editions.[7][8][9] Browser support began pre-ES6: Chrome 24 (2013), Firefox 29 (2014), Safari 10 (2016).[10][11] No evidence of string parameter support in ES5; Intl was added via ECMA-402.[12][13]

Citations:


Verify minimum browser targets for Intl.NumberFormat string-precision support

The code passes strings to Intl.NumberFormat.format() for large-value precision preservation, relying on V3 spec behavior that treats strings with exact precision rather than converting to Number first. However, the library has no explicit browser version constraints (TypeScript target is "es6" with no browserslist), making it unclear whether all deployment environments meet the V3 adoption timeline (Chrome 106+, Firefox 116+, Safari 15.4+ — ~2022–2023).

Confirm that the minimum supported browsers include full Intl.NumberFormat V3 string-precision support, or add a feature-detection guard:

const supportsStringFormat = new Intl.NumberFormat('und').format('11111111111111111112').indexOf('2') !== -1;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/raystack/components/amount/amount.tsx` around lines 239 - 240, The
code calls Intl.NumberFormat.format(finalBaseValue) with finalBaseValue as a
string to preserve precision, but environments may not support V3
string-precision; either ensure your supported browsers include V3 (update
browserslist/README) or add a runtime feature-detection and fallback: implement
the suggested supportsStringFormat check (new
Intl.NumberFormat('und').format('11111111111111111112').indexOf('2') !== -1)
and, if false, convert finalBaseValue to a safe fallback (e.g.,
Number(finalBaseValue) or a BigInt-to-string grouping formatter) before calling
Intl.NumberFormat.format; update the code around the
Intl.NumberFormat.format(...) call in amount.tsx that uses finalBaseValue
accordingly.


return <span {...props}>{formattedValue}</span>;
} catch (error) {
console.error('Error formatting amount:', error);
return <span {...props}>{value}</span>;
return <span {...props}>{String(value)}</span>;
}
};

Expand Down
Loading