Skip to content

Commit c1d8eb1

Browse files
committed
refactor(react-form): fix typing for using group.Field
1 parent 56f87cc commit c1d8eb1

File tree

4 files changed

+181
-22
lines changed

4 files changed

+181
-22
lines changed

packages/form-core/src/FieldGroupApi.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ export class FieldGroupApi<
186186
const newProps = { ...props }
187187
const validators = newProps.validators
188188

189+
newProps.name = this.getFormFieldName(props.name)
190+
189191
if (
190192
validators &&
191193
(validators.onChangeListenTo || validators.onBlurListenTo)

packages/form-core/tests/FieldGroupApi.spec.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,49 @@ describe('field group api', () => {
919919
)
920920
})
921921

922+
it('should remap the name of field options correctly', () => {
923+
const form = new FormApi({
924+
defaultValues: {
925+
account: {
926+
password: '',
927+
confirmPassword: '',
928+
},
929+
userPassword: '',
930+
userConfirmPassword: '',
931+
},
932+
})
933+
form.mount()
934+
935+
const fieldGroupString = new FieldGroupApi({
936+
form,
937+
fields: 'account',
938+
defaultValues: { password: '' },
939+
})
940+
fieldGroupString.mount()
941+
942+
const props1 = {
943+
name: 'password',
944+
}
945+
const remappedProps1 = fieldGroupString.getFormFieldOptions(props1)
946+
expect(remappedProps1.name).toBe('account.password')
947+
948+
const fieldGroupObject = new FieldGroupApi({
949+
form,
950+
fields: {
951+
password: 'userPassword',
952+
confirmPassword: 'userConfirmPassword',
953+
},
954+
defaultValues: { password: '' },
955+
})
956+
fieldGroupObject.mount()
957+
958+
const props2 = {
959+
name: 'password',
960+
}
961+
const remappedProps2 = fieldGroupObject.getFormFieldOptions(props2)
962+
expect(remappedProps2.name).toBe('userPassword')
963+
})
964+
922965
it('should remap listener paths with its remapFieldProps method', () => {
923966
const form = new FormApi({
924967
defaultValues: {

packages/react-form/src/useField.tsx

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
DeepValue,
88
FieldAsyncValidateOrFn,
99
FieldValidateOrFn,
10+
FieldValidators,
1011
FormAsyncValidateOrFn,
1112
FormValidateOrFn,
1213
} from '@tanstack/form-core'
@@ -446,28 +447,57 @@ export type LensFieldComponent<
446447
>({
447448
children,
448449
...fieldOptions
449-
}: FieldComponentBoundProps<
450-
unknown,
451-
string,
452-
TData,
453-
TOnMount,
454-
TOnChange,
455-
TOnChangeAsync,
456-
TOnBlur,
457-
TOnBlurAsync,
458-
TOnSubmit,
459-
TOnSubmitAsync,
460-
undefined | FormValidateOrFn<unknown>,
461-
undefined | FormValidateOrFn<unknown>,
462-
undefined | FormAsyncValidateOrFn<unknown>,
463-
undefined | FormValidateOrFn<unknown>,
464-
undefined | FormAsyncValidateOrFn<unknown>,
465-
undefined | FormValidateOrFn<unknown>,
466-
undefined | FormAsyncValidateOrFn<unknown>,
467-
undefined | FormAsyncValidateOrFn<unknown>,
468-
TParentSubmitMeta,
469-
ExtendedApi
470-
> & { name: TName }) => ReactNode
450+
}: Omit<
451+
FieldComponentBoundProps<
452+
unknown,
453+
string,
454+
TData,
455+
TOnMount,
456+
TOnChange,
457+
TOnChangeAsync,
458+
TOnBlur,
459+
TOnBlurAsync,
460+
TOnSubmit,
461+
TOnSubmitAsync,
462+
undefined | FormValidateOrFn<unknown>,
463+
undefined | FormValidateOrFn<unknown>,
464+
undefined | FormAsyncValidateOrFn<unknown>,
465+
undefined | FormValidateOrFn<unknown>,
466+
undefined | FormAsyncValidateOrFn<unknown>,
467+
undefined | FormValidateOrFn<unknown>,
468+
undefined | FormAsyncValidateOrFn<unknown>,
469+
undefined | FormAsyncValidateOrFn<unknown>,
470+
TParentSubmitMeta,
471+
ExtendedApi
472+
>,
473+
'name' | 'validators'
474+
> & {
475+
name: TName
476+
validators?: Omit<
477+
FieldValidators<
478+
unknown,
479+
string,
480+
TData,
481+
TOnMount,
482+
TOnChange,
483+
TOnChangeAsync,
484+
TOnBlur,
485+
TOnBlurAsync,
486+
TOnSubmit,
487+
TOnSubmitAsync
488+
>,
489+
'onChangeListenTo' | 'onBlurListenTo'
490+
> & {
491+
/**
492+
* An optional list of field names that should trigger this field's `onChange` and `onChangeAsync` events when its value changes
493+
*/
494+
onChangeListenTo?: DeepKeys<TLensData>[]
495+
/**
496+
* An optional list of field names that should trigger this field's `onBlur` and `onBlurAsync` events when its value changes
497+
*/
498+
onBlurListenTo?: DeepKeys<TLensData>[]
499+
}
500+
}) => ReactNode
471501

472502
/**
473503
* A function component that takes field options and a render function as children and returns a React component.

packages/react-form/tests/createFormHook.test.tsx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,4 +450,88 @@ describe('createFormHook', () => {
450450
const inputField1 = getByLabelText('unrelated')
451451
expect(inputField1).toHaveValue('John')
452452
})
453+
454+
it('should remap GroupFieldApi.Field validators to the correct names', () => {
455+
const FieldGroupString = withFieldGroup({
456+
defaultValues: { password: '', confirmPassword: '' },
457+
render: function Render({ group }) {
458+
return (
459+
<group.Field
460+
name="password"
461+
validators={{
462+
onChange: () => null,
463+
onChangeListenTo: ['password'],
464+
onBlur: () => null,
465+
onBlurListenTo: ['confirmPassword'],
466+
}}
467+
>
468+
{(field) => {
469+
expect(field.options.validators?.onChangeListenTo).toStrictEqual([
470+
'account.password',
471+
])
472+
expect(field.options.validators?.onBlurListenTo).toStrictEqual([
473+
'account.confirmPassword',
474+
])
475+
return <></>
476+
}}
477+
</group.Field>
478+
)
479+
},
480+
})
481+
482+
const FieldGroupObject = withFieldGroup({
483+
defaultValues: { password: '', confirmPassword: '' },
484+
render: function Render({ group }) {
485+
return (
486+
<group.Field
487+
name="password"
488+
validators={{
489+
onChange: () => null,
490+
onChangeListenTo: ['password'],
491+
onBlur: () => null,
492+
onBlurListenTo: ['confirmPassword'],
493+
}}
494+
>
495+
{(field) => {
496+
expect(field.options.validators?.onChangeListenTo).toStrictEqual([
497+
'userPassword',
498+
])
499+
expect(field.options.validators?.onBlurListenTo).toStrictEqual([
500+
'userConfirmPassword',
501+
])
502+
return <></>
503+
}}
504+
</group.Field>
505+
)
506+
},
507+
})
508+
509+
const Parent = () => {
510+
const form = useAppForm({
511+
defaultValues: {
512+
account: {
513+
password: '',
514+
confirmPassword: '',
515+
},
516+
userPassword: '',
517+
userConfirmPassword: '',
518+
},
519+
})
520+
521+
return (
522+
<>
523+
<FieldGroupString form={form} fields="account" />
524+
<FieldGroupObject
525+
form={form}
526+
fields={{
527+
password: 'userPassword',
528+
confirmPassword: 'userConfirmPassword',
529+
}}
530+
/>
531+
</>
532+
)
533+
}
534+
535+
render(<Parent />)
536+
})
453537
})

0 commit comments

Comments
 (0)