Skip to content

Commit 6bb138d

Browse files
authored
Merge pull request #11186 from marmelab/fix-@hookform/resolvers-compat
Fix compatibility with latest version of `@hookform/resolvers`
2 parents 289889c + 9455780 commit 6bb138d

File tree

9 files changed

+112
-27
lines changed

9 files changed

+112
-27
lines changed

packages/ra-core/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"build": "zshy --silent"
3939
},
4040
"devDependencies": {
41-
"@hookform/resolvers": "^3.2.0",
41+
"@hookform/resolvers": "^5.2.2",
4242
"@tanstack/react-query": "^5.90.2",
4343
"@tanstack/react-query-devtools": "^5.90.2",
4444
"@testing-library/react": "^15.0.7",
@@ -58,7 +58,7 @@
5858
"react-router-dom": "^6.28.1",
5959
"typescript": "^5.1.3",
6060
"yup": "^0.32.11",
61-
"zod": "^3.22.1",
61+
"zod": "^4.3.6",
6262
"zshy": "^0.5.0"
6363
},
6464
"peerDependencies": {

packages/ra-core/src/form/Form.spec.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,9 @@ describe('Form', () => {
871871
const translate = jest.spyOn(i18nProvider, 'translate');
872872
render(<ZodResolver i18nProvider={i18nProvider} />);
873873
fireEvent.click(screen.getByText('Submit'));
874-
await screen.findByText('Required');
874+
await screen.findByText(
875+
'Invalid input: expected string, received undefined'
876+
);
875877
await screen.findByText('This field is required');
876878
await screen.findByText('This field must be provided');
877879
await screen.findByText('app.validation.missing');

packages/ra-core/src/form/Form.stories.tsx

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
} from 'react-hook-form';
77
import { zodResolver } from '@hookform/resolvers/zod';
88
import * as z from 'zod';
9+
import { yupResolver } from '@hookform/resolvers/yup';
10+
import * as yup from 'yup';
911
import polyglotI18nProvider from 'ra-i18n-polyglot';
1012
import englishMessages from 'ra-language-english';
1113
import {
@@ -273,14 +275,10 @@ export const InputLevelValidation = ({
273275

274276
const zodSchema = z.object({
275277
defaultMessage: z.string(), //.min(1),
276-
customMessage: z.string({
277-
required_error: 'This field is required',
278-
}),
279-
customMessageTranslationKey: z.string({
280-
required_error: 'app.validation.required',
281-
}),
278+
customMessage: z.string({ error: 'This field is required' }),
279+
customMessageTranslationKey: z.string({ error: 'app.validation.required' }),
282280
missingCustomMessageTranslationKey: z.string({
283-
required_error: 'app.validation.missing',
281+
error: 'app.validation.missing',
284282
}),
285283
});
286284

@@ -308,6 +306,67 @@ export const ZodResolver = ({
308306
);
309307
};
310308

309+
type Schema = z.infer<typeof zodSchema>;
310+
311+
export const ZodResolverWithSchema = ({
312+
i18nProvider = defaultI18nProvider,
313+
}: {
314+
i18nProvider?: I18nProvider;
315+
}) => {
316+
const [result, setResult] = React.useState<any>();
317+
return (
318+
<CoreAdminContext i18nProvider={i18nProvider}>
319+
<Form<Schema>
320+
record={{}}
321+
onSubmit={data => setResult(data)}
322+
resolver={zodResolver(zodSchema)}
323+
>
324+
<Input source="defaultMessage" />
325+
<Input source="customMessage" />
326+
<Input source="customMessageTranslationKey" />
327+
<Input source="missingCustomMessageTranslationKey" />
328+
<button type="submit">Submit</button>
329+
</Form>
330+
<pre>{JSON.stringify(result, null, 2)}</pre>
331+
</CoreAdminContext>
332+
);
333+
};
334+
335+
const yupSchema = yup.object({
336+
defaultMessage: yup.string().required(),
337+
customMessage: yup.string().required('This field is required'),
338+
customMessageTranslationKey: yup
339+
.string()
340+
.required('app.validation.required'),
341+
missingCustomMessageTranslationKey: yup
342+
.string()
343+
.required('app.validation.missing'),
344+
});
345+
346+
export const YupResolver = ({
347+
i18nProvider = defaultI18nProvider,
348+
}: {
349+
i18nProvider?: I18nProvider;
350+
}) => {
351+
const [result, setResult] = React.useState<any>();
352+
return (
353+
<CoreAdminContext i18nProvider={i18nProvider}>
354+
<Form
355+
record={{}}
356+
onSubmit={data => setResult(data)}
357+
resolver={yupResolver(yupSchema)}
358+
>
359+
<Input source="defaultMessage" />
360+
<Input source="customMessage" />
361+
<Input source="customMessageTranslationKey" />
362+
<Input source="missingCustomMessageTranslationKey" />
363+
<button type="submit">Submit</button>
364+
</Form>
365+
<pre>{JSON.stringify(result, null, 2)}</pre>
366+
</CoreAdminContext>
367+
);
368+
};
369+
311370
const FormUnderTest = () => {
312371
const navigate = useNavigate();
313372
return (

packages/ra-core/src/form/Form.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export function Form<RecordType = any>(props: FormProps<RecordType>) {
6363
} = props;
6464
const record = useRecordContext(props);
6565
const resource = useResourceContext(props);
66-
const { form, formHandleSubmit } = useAugmentedForm(props);
66+
const { form, formHandleSubmit } = useAugmentedForm<RecordType>(props);
6767
const sourceContext = React.useMemo<SourceContextValue>(
6868
() => ({
6969
getSource: (source: string) => source,
@@ -113,7 +113,10 @@ export function Form<RecordType = any>(props: FormProps<RecordType>) {
113113
}
114114

115115
export type FormProps<RecordType = any> = FormOwnProps<RecordType> &
116-
Omit<UseFormProps, 'onSubmit'> & {
116+
Omit<
117+
UseFormProps<RecordType extends FieldValues ? RecordType : FieldValues>,
118+
'onSubmit'
119+
> & {
117120
validate?: ValidateForm;
118121
noValidate?: boolean;
119122
WarnWhenUnsavedChangesComponent?: React.ComponentType<{

packages/ra-core/src/form/useAugmentedForm.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,12 @@ export const useAugmentedForm = <RecordType = any>(
155155

156156
export type UseAugmentedFormProps<RecordType = any> =
157157
UseFormOwnProps<RecordType> &
158-
Omit<UseFormProps, 'onSubmit'> & {
158+
Omit<
159+
UseFormProps<
160+
RecordType extends FieldValues ? RecordType : FieldValues
161+
>,
162+
'onSubmit'
163+
> & {
159164
validate?: ValidateForm;
160165
};
161166

packages/ra-core/src/form/validation/getSimpleValidationResolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FieldValues } from 'react-hook-form';
1+
import { FieldValues, Resolver } from 'react-hook-form';
22

33
/**
44
* Convert a simple validation function that returns an object matching the form shape with errors
@@ -25,7 +25,7 @@ import { FieldValues } from 'react-hook-form';
2525
export const getSimpleValidationResolver =
2626
<TFieldValues extends FieldValues = FieldValues>(
2727
validate: ValidateForm<TFieldValues>
28-
) =>
28+
): Resolver<TFieldValues> =>
2929
async (data: TFieldValues) => {
3030
const errors = await validate(data);
3131

packages/ra-ui-materialui/src/form/SimpleForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ export const SimpleForm = (inProps: SimpleFormProps) => {
7676
);
7777
};
7878

79-
export interface SimpleFormProps
80-
extends Omit<FormProps, 'render'>,
79+
export interface SimpleFormProps<RecordType = any>
80+
extends Omit<FormProps<RecordType>, 'render'>,
8181
Omit<StackProps, 'onSubmit'> {
8282
children: ReactNode;
8383
className?: string;

packages/ra-ui-materialui/src/form/TabbedForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ const sanitizeRestProps = ({
110110
...rest
111111
}: TabbedFormProps) => rest;
112112

113-
export interface TabbedFormProps
114-
extends Omit<FormProps, 'render'>,
113+
export interface TabbedFormProps<RecordType = any>
114+
extends Omit<FormProps<RecordType>, 'render'>,
115115
Omit<
116116
HtmlHTMLAttributes<HTMLFormElement>,
117117
'defaultValue' | 'onSubmit' | 'children'

yarn.lock

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2627,12 +2627,14 @@ __metadata:
26272627
languageName: node
26282628
linkType: hard
26292629

2630-
"@hookform/resolvers@npm:^3.2.0":
2631-
version: 3.2.0
2632-
resolution: "@hookform/resolvers@npm:3.2.0"
2630+
"@hookform/resolvers@npm:^5.2.2":
2631+
version: 5.2.2
2632+
resolution: "@hookform/resolvers@npm:5.2.2"
2633+
dependencies:
2634+
"@standard-schema/utils": "npm:^0.3.0"
26332635
peerDependencies:
2634-
react-hook-form: ^7.0.0
2635-
checksum: 7eb79c480e006f08fcfe803e70b7b67eda03cc5c5bb8ce68a5399a0c6fdc34ee0fcc677fed9bea4a0baaa455ba39b15f86c8d2e3a702acdf762d6667988085b6
2636+
react-hook-form: ^7.55.0
2637+
checksum: 0692cd61dcc2a70cbb27b88a37f733c39e97f555c036ba04a81bd42b0467461cfb6bafacb46c16f173672f9c8a216bd7928a2330d4e49c700d130622bf1defaf
26362638
languageName: node
26372639
linkType: hard
26382640

@@ -5655,6 +5657,13 @@ __metadata:
56555657
languageName: node
56565658
linkType: hard
56575659

5660+
"@standard-schema/utils@npm:^0.3.0":
5661+
version: 0.3.0
5662+
resolution: "@standard-schema/utils@npm:0.3.0"
5663+
checksum: 6eb74cd13e52d5fc74054df51e37d947ef53f3ab9e02c085665dcca3c38c60ece8d735cebbdf18fbb13c775fbcb9becb3f53109b0e092a63f0f7389ce0993fd0
5664+
languageName: node
5665+
linkType: hard
5666+
56585667
"@storybook/addon-actions@npm:^8.6.11":
56595668
version: 8.6.11
56605669
resolution: "@storybook/addon-actions@npm:8.6.11"
@@ -20735,7 +20744,7 @@ __metadata:
2073520744
version: 0.0.0-use.local
2073620745
resolution: "ra-core@workspace:packages/ra-core"
2073720746
dependencies:
20738-
"@hookform/resolvers": "npm:^3.2.0"
20747+
"@hookform/resolvers": "npm:^5.2.2"
2073920748
"@tanstack/react-query": "npm:^5.90.2"
2074020749
"@tanstack/react-query-devtools": "npm:^5.90.2"
2074120750
"@testing-library/react": "npm:^15.0.7"
@@ -20763,7 +20772,7 @@ __metadata:
2076320772
react-router-dom: "npm:^6.28.1"
2076420773
typescript: "npm:^5.1.3"
2076520774
yup: "npm:^0.32.11"
20766-
zod: "npm:^3.22.1"
20775+
zod: "npm:^4.3.6"
2076720776
zshy: "npm:^0.5.0"
2076820777
peerDependencies:
2076920778
"@tanstack/react-query": ^5.83.0
@@ -26394,13 +26403,20 @@ __metadata:
2639426403
languageName: node
2639526404
linkType: hard
2639626405

26397-
"zod@npm:^3.22.1, zod@npm:^3.23.8, zod@npm:^3.24.2, zod@npm:^3.25.76":
26406+
"zod@npm:^3.23.8, zod@npm:^3.24.2, zod@npm:^3.25.76":
2639826407
version: 3.25.76
2639926408
resolution: "zod@npm:3.25.76"
2640026409
checksum: 5718ec35e3c40b600316c5b4c5e4976f7fee68151bc8f8d90ec18a469be9571f072e1bbaace10f1e85cf8892ea12d90821b200e980ab46916a6166a4260a983c
2640126410
languageName: node
2640226411
linkType: hard
2640326412

26413+
"zod@npm:^4.3.6":
26414+
version: 4.3.6
26415+
resolution: "zod@npm:4.3.6"
26416+
checksum: 860d25a81ab41d33aa25f8d0d07b091a04acb426e605f396227a796e9e800c44723ed96d0f53a512b57be3d1520f45bf69c0cb3b378a232a00787a2609625307
26417+
languageName: node
26418+
linkType: hard
26419+
2640426420
"zrender@npm:5.6.1":
2640526421
version: 5.6.1
2640626422
resolution: "zrender@npm:5.6.1"

0 commit comments

Comments
 (0)