Price input: Get rid of stepper, improve validation, and use Text instead of Numeric input#6307
Price input: Get rid of stepper, improve validation, and use Text instead of Numeric input#6307
Conversation
|
There was a problem hiding this comment.
Pull request overview
This PR refactors monetary and percentage inputs to rely on text-based inputs with shared formatting utilities, removes numeric input spinners, and tightens validation/normalization for prices and tax rates.
Changes:
- Replace
type="number"price/tax inputs with text inputs (inputMode="decimal") and route all price handling throughPriceField+usePriceField, now backed byformatPriceInput/formatPercentInput. - Normalize the
PriceFieldandTaxInputAPIs (named exports, consistentonChangeevent shape) and update all call sites to work with the new event/value model. - Add comprehensive unit tests for the new price/percent formatting utilities and simplify currency decimal resolution logic.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/taxes/pages/TaxCountriesPage/TaxCountriesPage.tsx | Switch TaxInput import to named export to match the new TaxInput module API. |
| src/taxes/pages/TaxClassesPage/TaxClassesPage.tsx | Same TaxInput import adjustment as above for the tax classes page. |
| src/taxes/components/TaxInput/index.ts | Change TaxInput exports to a named export (export { TaxInput }) for consistency. |
| src/taxes/components/TaxInput/TaxInput.tsx | Rework TaxInput to use a text input with formatPercentInput, removing custom separator logic and numeric field spinners. |
| src/shipping/components/PricingCard/PricingCard.tsx | Update to named PriceField import; behavior otherwise unchanged. |
| src/shipping/components/OrderValue/OrderValue.tsx | Update to named PriceField import and adjust channel minValue/maxValue handlers to accept `string |
| src/products/components/ProductVariantPrice/ProductVariantPrice.tsx | Use named PriceField import and update per-channel pricing handlers to consume the new string-based price value. |
| src/orders/components/OrderSendRefundPage/components/TransactionCard.tsx | Use named PriceField import and keep existing numeric parsing of e.target.value (now coming from the formatted string). |
| src/orders/components/OrderReturnPage/components/TransactionSubmitCard/TransactionSubmitCard.tsx | Adjust refund value input to parse the formatted PriceField value via `parseFloat(e.target.value ?? "") |
| src/orders/components/OrderReturnPage/components/PaymentSubmitCard/RefundAmountInput.tsx | Update to named PriceField import; onChange still forwards the event into the refund form logic. |
| src/orders/components/OrderManualTransactionForm/hooks.ts | Change manual refund handleChangeAmount to accept the new PriceField event shape ({ target: { value: string | null } }) and parse it to a number. |
| src/orders/components/OrderManualTransactionForm/components/PriceInputField.tsx | Wire PriceField directly to the manual transaction context’s handleChangeAmount and amount using the new props/types. |
| src/orders/components/OrderGrantRefundPage/OrderGrantRefundPage.tsx | Use named PriceField import; rely on useGrantRefundForm.change to receive the new normalized price value. |
| src/orders/components/OrderDiscountCommonModal/OrderDiscountCommonModal.tsx | Use named PriceField and update handleSetDiscountValue to accept the new event type and validate the string value before parsing. |
| src/orders/components/OrderCaptureDialog/OrderCaptureDialog.tsx | Replace legacy decimal helpers with formatPriceInput for capture amount, then parse with parseFloat, keeping currency-aware decimal precision via getCurrencyDecimalPoints. |
| src/discounts/components/VoucherRequirements/VoucherRequirements.tsx | Swap to named PriceField import and continue to pass per-channel minimum spend as a string driven by the new formatter. |
| src/components/PriceField/utils.ts | Replace old normalization utilities with formatPercentInput and formatPriceInput, and simplify getCurrencyDecimalPoints implementation. |
| src/components/PriceField/utils.test.ts | Rewrite tests to cover the new formatting logic for prices and percentages, including locale-style inputs, paste cases, and edge conditions. |
| src/components/PriceField/usePriceField.ts | Simplify to a single onChange handler that runs formatPriceInput and passes a { target: { name, value: string | null } } event to consumers. |
| src/components/PriceField/index.ts | Replace default export with named PriceField and re-export PriceFieldProps type. |
| src/components/PriceField/consts.ts | Remove obsolete separator constants now that formatting is handled centrally. |
| src/components/PriceField/PriceField.tsx | Make PriceField a named component; switch to text input + inputMode="decimal", accept a custom onChange signature, and route changes through usePriceField. |
| src/components/Datagrid/customCells/Money/MoneyCell.tsx | Replace usePriceField with a plain <input type="number">, using getCurrencyDecimalPoints only for step and local parsing/keyboard filtering for editing. |
| .github/workflows/publish-containers.yml | Remove stray blank lines for minor workflow formatting cleanup. |
| const maxDecimalPlaces = useMemo( | ||
| () => getCurrencyDecimalPoints(cell.data.currency), | ||
| [cell.data.currency], | ||
| ); | ||
| const step = 1 / Math.pow(10, maxDecimalPlaces ?? 2); | ||
|
|
||
| const handleChange = (e: ChangeEvent<HTMLInputElement>) => { | ||
| onChangeBase({ | ||
| ...cell, | ||
| data: { | ||
| ...cell.data, | ||
| value: event.target.value, | ||
| value: e.target.value ? parseFloat(e.target.value) : null, | ||
| }, |
There was a problem hiding this comment.
The previous implementation relied on usePriceField to enforce currency-specific decimal precision (including blocking decimal input for zero-decimal currencies like JPY) and to normalize pasted values with locale-specific separators. The new MoneyCellEdit now only uses step and a basic parseFloat, so maxDecimalPlaces is not actually used to constrain the input and users can enter more fractional digits than the currency allows (and even fractions for zero-decimal currencies), which is inconsistent with PriceField and may lead to invalid values being submitted. Consider either reusing formatPriceInput/usePriceField here or applying similar decimal-length trimming inside handleChange so that the datagrid editor respects maxDecimalPlaces in the same way as other price inputs.
Reasons for updating it: