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
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { fireEvent, render } from "@testing-library/react"
import { FormProvider, useForm, useFormContext } from "react-hook-form"
import { Text } from "#ui/atoms/Text"
import { HookedInputCheckboxGroup } from "./HookedInputCheckboxGroup"

interface FormValues {
items: Array<{
value: string
quantity?: number
reason?: string
}>
}

const options = [
{
value: "BABYBIBXA19D9D000000XXXX",
content: (
<Text size="small" tag="div" weight="bold">
Gray Baby Bib with Black Logo
</Text>
),
quantity: {
min: 1,
max: 5,
},
},
{
value: "BASEBHAT000000FFFFFFXXXX",
content: (
<Text size="small" tag="div" weight="bold">
Black Baseball Hat with White Logo
</Text>
),
quantity: {
min: 1,
max: 7,
},
},
]

function SetReasonButton(): React.JSX.Element {
const { setValue } = useFormContext<FormValues>()

return (
<button
type="button"
onClick={() => {
setValue("items.0.reason", "Damaged")
}}
>
Set reason
</button>
)
}

function ValuesPreview(): React.JSX.Element {
const { watch } = useFormContext<FormValues>()

return <pre data-testid="values">{JSON.stringify(watch("items"))}</pre>
}

function TestComponent(): React.JSX.Element {
const methods = useForm<FormValues>({
defaultValues: {
items: [
{
value: "BABYBIBXA19D9D000000XXXX",
quantity: 2,
},
{
value: "BASEBHAT000000FFFFFFXXXX",
quantity: 3,
},
],
},
})

return (
<FormProvider {...methods}>
<SetReasonButton />
<ValuesPreview />
<HookedInputCheckboxGroup name="items" title="Items" options={options} />
</FormProvider>
)
}

describe("HookedInputCheckboxGroup", () => {
test("preserves extra item fields after nested setValue and toggle", () => {
const { getAllByTestId, getByRole, getByTestId } = render(<TestComponent />)

fireEvent.click(getByRole("button", { name: "Set reason" }))
expect(getByTestId("values").textContent).toContain('"reason":"Damaged"')

const [, secondItem] = getAllByTestId("InputCheckboxGroupItem")
assertToBeDefined(secondItem)

fireEvent.click(secondItem)

const values = JSON.parse(getByTestId("values").textContent ?? "null")
expect(values).toEqual([
{
value: "BABYBIBXA19D9D000000XXXX",
quantity: 2,
reason: "Damaged",
},
])
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface HookedInputCheckboxGroupProps
export const HookedInputCheckboxGroup: React.FC<
HookedInputCheckboxGroupProps
> = ({ name, ...props }) => {
const { control } = useFormContext()
const { control, getValues } = useFormContext()
const feedback = useValidationFeedback(name)

return (
Expand All @@ -31,9 +31,29 @@ export const HookedInputCheckboxGroup: React.FC<
<InputCheckboxGroup
{...props}
feedback={feedback}
defaultValues={field.value ?? []}
defaultValues={Array.isArray(field.value) ? field.value : []}
Comment thread
gciotola marked this conversation as resolved.
onChange={(values) => {
field.onChange(values)
// Preserve any extra fields (e.g. `reason`) that the consumer
// stored on existing items via setValue. We need the latest form
// snapshot here because field.value can lag behind nested updates.
const currentValue = getValues(name)
Comment thread
gciotola marked this conversation as resolved.
Comment thread
gciotola marked this conversation as resolved.
Comment thread
gciotola marked this conversation as resolved.
const current: Array<{ value: string }> = Array.isArray(
currentValue,
)
? currentValue.filter(
(v): v is { value: string } =>
typeof v === "object" &&
v !== null &&
"value" in v &&
typeof (v as { value: unknown }).value === "string",
)
Comment thread
gciotola marked this conversation as resolved.
Comment thread
gciotola marked this conversation as resolved.
: []
field.onChange(
values.map((item) => {
const existing = current.find((v) => v.value === item.value)
return existing != null ? { ...existing, ...item } : item
}),
)
}}
/>
)}
Expand Down
Loading