From 90e0ceccaef5bca863ac29cd6826de08a6531662 Mon Sep 17 00:00:00 2001 From: Zach Ovington Date: Fri, 20 Sep 2024 12:13:47 -0600 Subject: [PATCH] mills currency --- src/forms/BoundNumberField.test.tsx | 9 +++++++++ src/forms/BoundNumberField.tsx | 4 ++-- src/forms/formStateDomain.ts | 1 + src/inputs/NumberField.stories.tsx | 2 ++ src/inputs/NumberField.test.tsx | 18 ++++++++++++++++++ src/inputs/NumberField.tsx | 8 ++++++-- 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/forms/BoundNumberField.test.tsx b/src/forms/BoundNumberField.test.tsx index 88fc361f5..38ddc5f45 100644 --- a/src/forms/BoundNumberField.test.tsx +++ b/src/forms/BoundNumberField.test.tsx @@ -57,6 +57,14 @@ describe("BoundNumberField", () => { expect(r.royalties).toHaveValue("$1.00"); }); + it("drops the 'in mills' suffix from labels", async () => { + const author = createObjectState(formConfig, { royaltiesInMills: 1_00 }); + const r = await render(); + expect(r.royalties_label).toHaveTextContent("Royalties"); + expect(r.royalties_label).not.toHaveTextContent("Cents"); + expect(r.royalties).toHaveValue("$0.100"); + }); + it("retains 0 value", async () => { const author = createObjectState(formConfig, { royaltiesInCents: 1_00 }); const r = await render(); @@ -117,4 +125,5 @@ describe("BoundNumberField", () => { const formConfig: ObjectConfig = { heightInInches: { type: "value", rules: [required] }, royaltiesInCents: { type: "value" }, + royaltiesInMills: { type: "value" }, }; diff --git a/src/forms/BoundNumberField.tsx b/src/forms/BoundNumberField.tsx index 267be6e4c..55654de91 100644 --- a/src/forms/BoundNumberField.tsx +++ b/src/forms/BoundNumberField.tsx @@ -18,8 +18,8 @@ export function BoundNumberField(props: BoundNumberFieldProps) { field, readOnly, onChange = (value) => field.set(value), - label = defaultLabel(field.key.replace(/InCents$/, "")), - type = field.key.endsWith("InCents") ? "cents" : undefined, + label = defaultLabel(field.key.replace(/(InCents|InMills)$/, "")), + type = field.key.endsWith("InCents") ? "cents" : field.key.endsWith("InMills") ? "mills" : undefined, onFocus, onBlur, onEnter, diff --git a/src/forms/formStateDomain.ts b/src/forms/formStateDomain.ts index 00bea4279..e74856bc4 100644 --- a/src/forms/formStateDomain.ts +++ b/src/forms/formStateDomain.ts @@ -26,6 +26,7 @@ export interface AuthorInput { birthday?: Date | null; heightInInches?: number | null; royaltiesInCents?: number | null; + royaltiesInMills?: number | null; books?: BookInput[] | null; address?: AuthorAddress | null; favoriteSport?: string | null; diff --git a/src/inputs/NumberField.stories.tsx b/src/inputs/NumberField.stories.tsx index 07237f211..4a165bdda 100644 --- a/src/inputs/NumberField.stories.tsx +++ b/src/inputs/NumberField.stories.tsx @@ -45,6 +45,7 @@ export function NumberFieldStyles() {

Unit Types

+ @@ -80,6 +81,7 @@ export function NumberFieldReadOnly() { {}} readOnly={true} /> +
diff --git a/src/inputs/NumberField.test.tsx b/src/inputs/NumberField.test.tsx index 7d66a475a..c0481cbab 100644 --- a/src/inputs/NumberField.test.tsx +++ b/src/inputs/NumberField.test.tsx @@ -60,6 +60,14 @@ describe("NumberFieldTest", () => { expect(onChange).toBeCalledTimes(2); // change and blur }); + it("can set mills as dollars", async () => { + const r = await render(); + expect(r.cost).toHaveValue("$1.200"); + type(r.cost, "14"); + expect(r.cost).toHaveValue("$14.000"); + expect(lastSet).toEqual(14000); + }); + it("can set cents as dollars", async () => { const r = await render(); expect(r.cost).toHaveValue("$12.00"); @@ -92,6 +100,14 @@ describe("NumberFieldTest", () => { expect(onChange).toBeCalledTimes(2); // change and blur }); + it("can set mills as mills", async () => { + const r = await render(); + expect(r.cost).toHaveValue("$12.000"); + type(r.cost, ".145"); + expect(r.cost).toHaveValue("$0.145"); + expect(lastSet).toEqual(145); + }); + it("can set cents as cents", async () => { const r = await render(); expect(r.cost).toHaveValue("$12.00"); @@ -153,6 +169,7 @@ describe("NumberFieldTest", () => { const r = await render( <> + @@ -160,6 +177,7 @@ describe("NumberFieldTest", () => { , ); expect(r.days).toHaveValue("+123 days"); + expect(r.mills).toHaveValue("+$0.456"); expect(r.cents).toHaveValue("+$4.56"); expect(r.basisPoints).toHaveValue("+7.89%"); expect(r.percent).toHaveValue("+123%"); diff --git a/src/inputs/NumberField.tsx b/src/inputs/NumberField.tsx index efe8fc962..a5d4e8d7e 100644 --- a/src/inputs/NumberField.tsx +++ b/src/inputs/NumberField.tsx @@ -8,7 +8,7 @@ import { Css, Xss } from "src/Css"; import { maybeCall } from "src/utils"; import { TextFieldBase } from "./TextFieldBase"; -export type NumberFieldType = "cents" | "dollars" | "percent" | "basisPoints" | "days"; +export type NumberFieldType = "cents" | "dollars" | "percent" | "basisPoints" | "days" | "mills"; // exported for testing purposes export interface NumberFieldProps extends Pick { @@ -85,7 +85,9 @@ export function NumberField(props: NumberFieldProps) { const isDisabled = !!disabled; const isReadOnly = !!readOnly; - const factor = type === "percent" || type === "cents" ? 100 : type === "basisPoints" ? 10_000 : 1; + const factor = + type === "percent" ? 100 : type === "cents" ? 100 : type === "mills" ? 1_000 : type === "basisPoints" ? 10_000 : 1; + const signDisplay = displayDirection ? "always" : "auto"; const defaultFormatOptions: Intl.NumberFormatOptions = useMemo( () => ({ @@ -111,6 +113,8 @@ export function NumberField(props: NumberFieldProps) { ? { style: "percent", minimumFractionDigits: 2 } : type === "cents" ? { style: "currency", currency: "USD", minimumFractionDigits: 2 } + : type === "mills" + ? { style: "currency", currency: "USD", minimumFractionDigits: 3 } : type === "dollars" ? { style: "currency", currency: "USD", minimumFractionDigits: numFractionDigits ?? 2 } : type === "days"