diff --git a/apps/admin-x-settings/src/admin-x-ds/global/form/CurrencyField.stories.tsx b/apps/admin-x-settings/src/admin-x-ds/global/form/CurrencyField.stories.tsx new file mode 100644 index 0000000000..309997860c --- /dev/null +++ b/apps/admin-x-settings/src/admin-x-ds/global/form/CurrencyField.stories.tsx @@ -0,0 +1,36 @@ +import {ReactNode} from 'react'; +import {useArgs} from '@storybook/preview-api'; +import type {Meta, StoryObj} from '@storybook/react'; + +import CurrencyField from './CurrencyField'; + +const meta = { + title: 'Global / Form / Currency field', + component: CurrencyField, + tags: ['autodocs'], + decorators: [(_story: () => ReactNode) => (
{_story()}
)], + argTypes: { + hint: { + control: 'text' + }, + rightPlaceholder: { + control: 'text' + } + } +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const WithValue: Story = { + render: function Component(args) { + const [, updateArgs] = useArgs(); + + return updateArgs({valueInCents})} />; + }, + args: { + title: 'Amount', + hint: 'Notice how the value is the integer number of cents', + valueInCents: 500 + } +}; diff --git a/apps/admin-x-settings/src/admin-x-ds/global/form/CurrencyField.tsx b/apps/admin-x-settings/src/admin-x-ds/global/form/CurrencyField.tsx index 16a30256c1..939c85d966 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/form/CurrencyField.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/form/CurrencyField.tsx @@ -2,17 +2,25 @@ import React, {useState} from 'react'; import TextField, {TextFieldProps} from './TextField'; import {currencyFromDecimal, currencyToDecimal} from '../../../utils/currency'; -export type CurrencyFieldProps = Omit & { - currency?: string - onChange?: (value: number) => void +export type CurrencyFieldProps = Omit & { + valueInCents?: number; + currency?: string; + onChange?: (cents: number) => void; } +/** + * A CurrencyField is a special type of [TextField](?path=/docs/global-form-textfield--docs) with + * some parsing to input currency values. While editing you can enter any number of decimals, but + * the value in `onChange` will be rounded and multiplied to get an integer number of cents. + * + * Available options are generally the same as TextField. + */ const CurrencyField: React.FC = ({ - value, + valueInCents, onChange, ...props }) => { - const [localValue, setLocalValue] = useState(currencyToDecimal(parseInt(value || '0')).toString()); + const [localValue, setLocalValue] = useState(currencyToDecimal(valueInCents || 0).toString()); // While the user is editing we allow more lenient input, e.g. "1.32.566" to make it easier to type and change const stripNonNumeric = (input: string) => input.replace(/[^\d.]+/g, ''); diff --git a/apps/admin-x-settings/src/components/settings/membership/TipsOrDonations.tsx b/apps/admin-x-settings/src/components/settings/membership/TipsOrDonations.tsx index e6458d7f4d..1885168457 100644 --- a/apps/admin-x-settings/src/components/settings/membership/TipsOrDonations.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/TipsOrDonations.tsx @@ -105,7 +105,7 @@ const TipsOrDonations: React.FC<{ keywords: string[] }> = ({keywords}) => { /> )} title='Suggested amount' - value={donationsSuggestedAmount} + valueInCents={parseInt(donationsSuggestedAmount)} onBlur={validate} onChange={cents => updateSetting('donations_suggested_amount', cents.toString())} onKeyDown={() => clearError('donationsSuggestedAmount')} diff --git a/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx b/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx index b4853615e3..773cd28358 100644 --- a/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx @@ -170,7 +170,7 @@ const TierDetailModal: React.FC = ({tier}) => { placeholder='1' rightPlaceholder={`${formState.currency}/month`} title='Monthly price' - value={formState.monthly_price?.toString() || ''} + valueInCents={formState.monthly_price || 0} hideTitle onBlur={() => validators.monthly_price()} onChange={price => updateForm(state => ({...state, monthly_price: price}))} @@ -181,7 +181,7 @@ const TierDetailModal: React.FC = ({tier}) => { placeholder='10' rightPlaceholder={`${formState.currency}/year`} title='Yearly price' - value={formState.yearly_price?.toString() || ''} + valueInCents={formState.yearly_price || 0} hideTitle onBlur={() => validators.yearly_price()} onChange={price => updateForm(state => ({...state, yearly_price: price}))}