diff --git a/src/components/Form/DynamicField.tsx b/src/components/Form/DynamicField.tsx index 8806aa4..5578018 100644 --- a/src/components/Form/DynamicField.tsx +++ b/src/components/Form/DynamicField.tsx @@ -30,7 +30,8 @@ export const DynamicField = ({ const [dependentValues, setDependentValues] = useState(pick(values, field.deps)); const staticField = useMemo(() => { - return field.render.call(undefined, values); + // only the values named in deps are provided, since a change to any other value does not trigger a rerender + return field.render.call(undefined, dependentValues as PartialFormDataType); }, [dependentValues, field.render]); useEffect(() => { diff --git a/src/components/Form/Form.test.tsx b/src/components/Form/Form.test.tsx index be37550..b5be59b 100644 --- a/src/components/Form/Form.test.tsx +++ b/src/components/Form/Form.test.tsx @@ -399,4 +399,31 @@ describe('Form', () => { expect(onSubmit).toHaveBeenCalledWith({ symptoms: new Set(['none']) }); }); }); + + describe('dynamic field deps', () => { + it('should pass only the values named in deps to the render function', async () => { + const renderField = vi.fn((data: { a?: boolean; b?: string; c?: string }) => { + return data.a ? ({ kind: 'string', label: 'Field C', variant: 'input' } as const) : null; + }); + render( +
+ ); + await userEvent.type(screen.getByLabelText('Field B'), 'abc'); + fireEvent.click(screen.getByLabelText('Field A')); + await waitFor(() => expect(screen.getByLabelText('Field C')).toBeInTheDocument()); + expect(renderField.mock.lastCall?.[0]).toStrictEqual({ a: true }); + for (const [data] of renderField.mock.calls) { + expect(data).not.toHaveProperty('b'); + } + }); + }); }); diff --git a/src/components/Form/utils.ts b/src/components/Form/utils.ts index c1a80f0..a33b59f 100644 --- a/src/components/Form/utils.ts +++ b/src/components/Form/utils.ts @@ -7,6 +7,7 @@ import type { StaticFormFields, UnknownFormField } from '@douglasneuroinformatics/libui-form-types'; +import { pick } from 'lodash-es'; export function getInitialValues(values: PartialNullableFormDataType) { const initialValues: { [key: string]: unknown } = {}; @@ -34,7 +35,7 @@ export function getFormFields(content: FormContent): /** * Given a set of data, resolve static content for form fields. Null values - * will be removed. + * will be removed. A dynamic field receives only the values named in its deps. */ export function resolveStaticFormFields(content: FormContent, data: PartialFormDataType) { const staticFormFields: Partial> = {}; @@ -42,7 +43,7 @@ export function resolveStaticFormFields(content: FormCon for (const fieldName in formFields) { const field: UnknownFormField = formFields[fieldName]; if (field.kind === 'dynamic') { - const resolvedField = field.render.call(undefined, data); + const resolvedField = field.render.call(undefined, pick(data, field.deps) as PartialFormDataType); if (resolvedField) { staticFormFields[fieldName] = resolvedField; }