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
22 changes: 22 additions & 0 deletions apps/www/src/components/playground/amount-examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,28 @@ export function AmountExamples() {
/>
</Text>
</Flex>
<Flex gap={5} align='center' direction='column'>
<Text>
Compact: <Amount value={120000000} notation='compact' />
</Text>
<Text>
Narrow symbol:{' '}
<Amount
value={1299}
locale='en-CA'
currencyDisplay='narrowSymbol'
/>
</Text>
<Text>
Gain: <Amount value={1299} signDisplay='always' />
</Text>
<Text>
Loss: <Amount value={-1299} signDisplay='always' />
</Text>
<Text>
Proportional figures: <Amount value={1111} tabularNums={false} />
</Text>
</Flex>
</Flex>
</PlaygroundLayout>
);
Expand Down
54 changes: 53 additions & 1 deletion apps/www/src/content/docs/components/amount/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@ export const playground = {
},
currencyDisplay: {
type: 'select',
options: ['symbol', 'code', 'name'],
options: ['symbol', 'narrowSymbol', 'code', 'name'],
defaultValue: 'symbol'
},
notation: {
type: 'select',
options: ['standard', 'compact'],
defaultValue: 'standard'
},
signDisplay: {
type: 'select',
options: ['auto', 'always', 'exceptZero', 'never'],
defaultValue: 'auto'
},
minimumFractionDigits: {
type: 'number',
defaultValue: undefined
Expand All @@ -50,6 +60,10 @@ export const playground = {
hideCurrency: {
type: 'checkbox',
defaultValue: false
},
tabularNums: {
type: 'checkbox',
defaultValue: true
}
},
getCode
Expand Down Expand Up @@ -123,6 +137,44 @@ export const currencyDisplayDemo = {
`
};

export const notationDemo = {
type: 'code',
code: `
<Flex gap={4}>
<Amount value={120000000} notation="compact" /> {/* $1.2M */}
<Amount value={1300000} notation="compact" /> {/* $13K */}
<Amount value={120000000} /> {/* $1,200,000.00 */}
</Flex>
`
};

export const signDisplayDemo = {
type: 'code',
code: `
<Flex gap={4}>
<Amount value={1299} signDisplay="always" /> {/* +$12.99 */}
<Amount value={-1299} signDisplay="always" /> {/* -$12.99 */}
<Amount value={0} signDisplay="exceptZero" /> {/* $0.00 */}
<Amount value={-1299} signDisplay="never" /> {/* $12.99 */}
</Flex>
`
};

export const tabularNumsDemo = {
type: 'code',
code: `
<Flex direction="column" gap={2}>
{/* Tabular figures (default) keep digits aligned across rows */}
<Amount value={111111} />
<Amount value={909090} />
{/* Proportional figures read better in running text */}
<Text>
You saved <Amount value={1299} tabularNums={false} /> today
</Text>
</Flex>
`
};

export const hideCurrencyDemo = {
type: 'code',
code: `
Expand Down
21 changes: 21 additions & 0 deletions apps/www/src/content/docs/components/amount/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import {
localeDemo,
hideDecimalsDemo,
currencyDisplayDemo,
notationDemo,
signDisplayDemo,
tabularNumsDemo,
hideCurrencyDemo,
groupDigitsDemo,
withTextDemo,
Expand Down Expand Up @@ -62,6 +65,24 @@ Formats and displays monetary values with locale and currency support.

<Demo data={currencyDisplayDemo} />

### notation

Use `compact` to abbreviate large values, e.g. `$1.2M`. Handy for dashboards and summary views. Compact rounds values by design, so avoid it where the exact amount matters.

<Demo data={notationDemo} />

### signDisplay

Control when the `+`/`-` sign appears. Useful for showing gains and losses.

<Demo data={signDisplayDemo} />

### tabularNums

Tabular (fixed-width) figures are on by default so amounts align digit-for-digit when stacked in table rows. Turn it off in running text, where proportional figures look more natural.

<Demo data={tabularNumsDemo} />

### hideCurrency

Render only the formatted number, without any currency symbol, code, or name. Locale-driven separators and decimal places are preserved.
Expand Down
114 changes: 114 additions & 0 deletions packages/raystack/components/amount/__tests__/amount.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,4 +321,118 @@ describe('Amount', () => {
expect(screen.getByText('12.990')).toBeInTheDocument();
});
});

describe('narrowSymbol', () => {
it('renders the narrow symbol instead of the locale-prefixed one', () => {
// en-CA formats USD as "US$12.99" with 'symbol'; narrowSymbol drops the prefix.
render(
<Amount
value={1299}
currency='USD'
locale='en-CA'
currencyDisplay='narrowSymbol'
/>
);
expect(screen.getByText('$12.99')).toBeInTheDocument();
});

it('matches symbol output for the home locale', () => {
render(<Amount value={1299} currencyDisplay='narrowSymbol' />);
expect(screen.getByText('$12.99')).toBeInTheDocument();
});
});

describe('signDisplay', () => {
it('always shows the sign when signDisplay is always', () => {
render(<Amount value={1299} signDisplay='always' />);
expect(screen.getByText('+$12.99')).toBeInTheDocument();
});

it('shows the sign except for zero when signDisplay is exceptZero', () => {
const { rerender } = render(
<Amount value={1299} signDisplay='exceptZero' />
);
expect(screen.getByText('+$12.99')).toBeInTheDocument();
rerender(<Amount value={0} signDisplay='exceptZero' />);
expect(screen.getByText('$0.00')).toBeInTheDocument();
});

it('hides the sign for negative values when signDisplay is never', () => {
render(<Amount value={-1299} signDisplay='never' />);
expect(screen.getByText('$12.99')).toBeInTheDocument();
});

it('keeps the sign when hideCurrency strips the currency token', () => {
render(<Amount value={1299} signDisplay='always' hideCurrency />);
expect(screen.getByText('+12.99')).toBeInTheDocument();
});
});

describe('notation', () => {
it('renders compact notation for large values', () => {
render(<Amount value={120000000} notation='compact' />);
expect(screen.getByText('$1.2M')).toBeInTheDocument();
});

it('renders standard notation by default', () => {
render(<Amount value={120000000} />);
expect(screen.getByText('$1,200,000.00')).toBeInTheDocument();
});

it('rounds small values to compact defaults (no abbreviation below 1K)', () => {
// Compact notation caps fraction digits aggressively: 12.99 → "$13".
render(<Amount value={1299} notation='compact' />);
expect(screen.getByText('$13')).toBeInTheDocument();
});

it('works with hideDecimals', () => {
render(<Amount value={125000000} notation='compact' hideDecimals />);
expect(screen.getByText('$1M')).toBeInTheDocument();
});

it('works with string values', () => {
render(<Amount value='120000000' notation='compact' />);
expect(screen.getByText('$1.2M')).toBeInTheDocument();
});

it('works with hideCurrency', () => {
render(<Amount value={120000000} notation='compact' hideCurrency />);
expect(screen.getByText('1.2M')).toBeInTheDocument();
});

it('respects explicit fraction digits', () => {
render(
<Amount
value={123400000}
notation='compact'
minimumFractionDigits={2}
maximumFractionDigits={2}
/>
);
expect(screen.getByText('$1.23M')).toBeInTheDocument();
});
});

describe('tabularNums', () => {
it('applies tabular figures by default', () => {
const { container } = render(<Amount value={1299} />);
const span = container.querySelector('span');
expect(span?.className).toContain('tabular');
});

it('omits tabular figures when tabularNums is false', () => {
const { container } = render(<Amount value={1299} tabularNums={false} />);
const span = container.querySelector('span');
expect(span?.className).not.toContain('tabular');
});

it('keeps custom className alongside the tabular class', () => {
const { container } = render(
<Amount value={1299} className='custom-class' />
);
const span = container.querySelector('span');
expect(span?.className).toContain('custom-class');
expect(span?.className).toContain('tabular');
});
});
});
2 changes: 1 addition & 1 deletion packages/raystack/components/amount/amount.module.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
.amount {
.tabular {
font-variant-numeric: tabular-nums;
}
Loading
Loading