import type { Meta, StoryObj } from '@storybook/react-webpack5'; import { within, userEvent, fireEvent, expect } from 'storybook/test'; import { allModes } from '../../.storybook/modes'; import { lorem500 } from '../test-utils'; import { Field } from '../field/Field'; import Body from '../body'; import MoneyInput from './MoneyInput'; const meta = { title: 'Forms/MoneyInput/Tests', component: MoneyInput, args: { amount: 1000, id: 'moneyInput', currencies: [ { value: 'EUR', label: 'EUR', note: 'Euro', currency: 'eur', searchable: 'Spain, Germany, France, Austria', }, { value: 'GBP', label: 'GBP', note: 'British pound', currency: 'gbp', searchable: 'England, Scotland, Wales', }, ], selectedCurrency: { value: 'EUR', label: 'EUR', note: 'Euro', currency: 'eur', searchable: 'Spain, Germany, France, Austria', }, searchPlaceholder: '', onAmountChange: () => {}, onCurrencyChange: () => {}, }, tags: ['!autodocs'], } satisfies Meta; export default meta; type Story = StoryObj; const wait = async (duration = 500) => new Promise((resolve) => { setTimeout(resolve, duration); }); /** * This test ensures that when the SelectInput is used within a scrollable page, * opening the dropdown does not cause any unwanted scrolling or layout shifts. * Expected preview should start with green bar at the top, with yellow section * not in the viewport. The issue is particularly prominent on iOS Safari. * * NB: This test is disabled in Chromatic as there's no obvious way to control * element of a snapshot. It's to be primarily used in manual testing * on an actual device or a simulator as it cannot be reproduced with mobile * emulation modes on desktop browsers. */ export const SmoothScrollReset: Story = { decorators: [ (Story) => ( <>
This block should not be in the viewport.
{lorem500}
), ], play: async ({ canvasElement }) => { await wait(); document.documentElement.scrollTop = 400; await wait(); const canvas = within(canvasElement); // cannot use userEvent.click as it crashes on iOS Safari in the simulator await fireEvent.click(canvas.getByRole('combobox')); }, globals: { viewport: { value: allModes.largeMobile.viewport, isRotated: false }, }, parameters: { chromatic: { disableSnapshot: true, }, }, }; export const WithDecimals: Story = { args: { currencies: [], decimals: 4, amount: 1234.5678, }, play: async ({ canvas }) => { const input = canvas.getByRole('textbox'); await expect(input).toHaveValue('1,234.5678'); await userEvent.clear(input); await userEvent.type(input, '99.123456789'); await userEvent.tab(); await expect(input).toHaveValue('99.1235'); }, }; const eur = { value: 'EUR', label: 'EUR', note: 'Euro', currency: 'eur', } as const; const bhd = { value: 'BHD', label: 'BHD', note: 'Bahraini dinar', currency: 'bhd', } as const; const jpy = { value: 'JPY', label: 'JPY', note: 'Japanese yen', currency: 'jpy', } as const; const sharedProps = { currencies: [], onAmountChange: () => {}, onCurrencyChange: () => {}, } as const; function Row({ label, amount, currency, decimals, }: { label: string; amount: number; currency: { value: string; label: string; note: string; currency: string }; decimals: number; }) { return (
{label}
); } /** * Side-by-side comparison of formatAmount (no decimals prop) vs formatNumber (with decimals prop) * when decimals matches the currency default. Differences indicate a formatting parity issue. * * Problem 1: Trailing zero stripping — formatAmount strips ".00" for whole numbers, * formatNumber always pads to the requested precision. * * Problem 2: BHD decimal count — formatAmount renders 2 fractional digits for BHD, * but the currencyDecimals map says BHD has 3. formatNumber with decimals=3 shows 3. */ export const DecimalsFormatParity: Story = { render: () => (
EUR — whole numbers (trailing zero stripping) EUR — fractional amounts (should match) BHD — all amounts (formatAmount uses 2 digits, formatNumber uses 3) JPY — zero-decimal currency (should always match)
), parameters: { chromatic: { disableSnapshot: true, }, }, };