Skip to content

Commit bdaa732

Browse files
committed
Expose "setErrors" function from useAjvForm
1 parent 39eb344 commit bdaa732

File tree

5 files changed

+94
-18
lines changed

5 files changed

+94
-18
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@programmer_network/use-ajv-form",
3-
"version": "1.0.12",
3+
"version": "1.0.13",
44
"description": "Custom React Hook that integrates with Ajv JSON Schema Validator",
55
"main": "dist/use-ajv-form.es.js",
66
"author": "Aleksandar Grbic",

src/index.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ const useAJVForm = <T extends Record<string, any>>(
9999
});
100100
};
101101

102-
const setErrors = (errors: useFormErrors<T>) => {
102+
const _setErrors = (errors: useFormErrors<T>) => {
103103
return Object.keys(errors).reduce(
104104
(acc, fieldName) => {
105105
const key = fieldName as keyof typeof state;
@@ -132,7 +132,7 @@ const useAJVForm = <T extends Record<string, any>>(
132132
);
133133

134134
setState(
135-
setErrors(
135+
_setErrors(
136136
Object.keys(errors).reduce((acc, fieldName) => {
137137
return {
138138
...acc,
@@ -168,6 +168,10 @@ const useAJVForm = <T extends Record<string, any>>(
168168
[state],
169169
);
170170

171+
const setErrors = (errors: ErrorObject[]): void => {
172+
setState(_setErrors(getErrors(errors, options?.userDefinedMessages)));
173+
};
174+
171175
const isValid = useMemo(() => isFormValid(state), [state]);
172176

173177
useEffect(() => {
@@ -183,12 +187,13 @@ const useAJVForm = <T extends Record<string, any>>(
183187
return;
184188
}
185189

186-
setState(setErrors(getErrors(options?.errors, options?.userDefinedMessages)));
190+
setState(_setErrors(getErrors(options?.errors, options?.userDefinedMessages)));
187191
}, [options?.errors]);
188192

189193
return {
190194
reset: resetForm,
191195
set: setFormState,
196+
setErrors,
192197
validate: validateForm,
193198
onBlur: handleBlur,
194199
isValid,

src/useAjvForm.test.tsx

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,76 @@ describe('useAJVForm with multiple field updates', () => {
437437

438438
expect(result.current.isDirty).toBe(true);
439439
});
440+
});
441+
442+
describe('useAJVForm should properly set errors programmatically using setErrors function', () => {
443+
it('sets the errors using form.setErrors without having any userDefinedMessages', () => {
444+
const initialData = { title: '', description: '' };
445+
const schema: JSONSchemaType<{ title: string; description: string }> = {
446+
type: 'object',
447+
required: ['title', 'description'],
448+
properties: {
449+
title: { type: 'string' },
450+
description: { type: 'string' },
451+
},
452+
};
453+
454+
const { result } = renderHook(() => useAJVForm(initialData, schema));
455+
456+
result.current.setErrors([
457+
{
458+
instancePath: '/title',
459+
keyword: 'required',
460+
params: { missingProperty: 'title' },
461+
schemaPath: '#/required',
462+
},
463+
{
464+
instancePath: '/description',
465+
keyword: 'required',
466+
params: { missingProperty: 'description' },
467+
schemaPath: '#/required',
468+
},
469+
]);
470+
471+
expect(result.current.state.title.error).toBe('title is required.');
472+
expect(result.current.state.description.error).toBe('description is required.');
473+
});
474+
475+
it('sets the errors using form.setErrors with userDefinedMessages', () => {
476+
const initialData = { title: '', description: '' };
477+
const schema: JSONSchemaType<{ title: string; description: string }> = {
478+
type: 'object',
479+
required: ['title', 'description'],
480+
properties: {
481+
title: { type: 'string' },
482+
description: { type: 'string' },
483+
},
484+
};
485+
486+
const { result } = renderHook(() =>
487+
useAJVForm(initialData, schema, {
488+
userDefinedMessages: {
489+
required: () => 'Monkey message',
490+
},
491+
}),
492+
);
440493

441-
// Additional tests can be written to cover other scenarios such as validation,
442-
// resetting the form, handling errors, etc., when multiple fields are involved.
494+
result.current.setErrors([
495+
{
496+
instancePath: '/title',
497+
keyword: 'required',
498+
params: { missingProperty: 'title' },
499+
schemaPath: '#/required',
500+
},
501+
{
502+
instancePath: '/description',
503+
keyword: 'required',
504+
params: { missingProperty: 'description' },
505+
schemaPath: '#/required',
506+
},
507+
]);
508+
509+
expect(result.current.state.title.error).toBe('Monkey message');
510+
expect(result.current.state.description.error).toBe('Monkey message');
511+
});
443512
});

src/utils/index.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ import Ajv, { ErrorObject, KeywordDefinition } from 'ajv';
22

33
import { AJVMessageFunction, InitialState, useFormErrors } from './types';
44

5-
import { DefaultAJVMessages } from './types';
5+
// import { DefaultAJVMessages } from './types';
66
import { defaultAJVMessages } from './constants';
77

8-
const getFieldName = (field: ErrorObject): string | null => {
9-
return field.instancePath.slice(1);
8+
const getFieldName = (instancePath: string): string | null => {
9+
if (!instancePath) {
10+
return null;
11+
}
12+
13+
return instancePath.slice(1);
1014
};
1115

1216
const getErrorMessage = (
@@ -16,13 +20,14 @@ const getErrorMessage = (
1620
const UNKNOWN_VALIDATION_ERROR = 'Unknown validation error';
1721

1822
try {
19-
const keyword = error.keyword as keyof DefaultAJVMessages;
23+
const errorObject = error as ErrorObject;
24+
const keyword = errorObject.keyword;
2025
const errorMessageFunction = { ...defaultAJVMessages, ...userDefinedMessages }[
2126
keyword
2227
];
2328

2429
if (typeof errorMessageFunction === 'function') {
25-
return errorMessageFunction(error.params);
30+
return errorMessageFunction(errorObject.params);
2631
}
2732

2833
return error.message || UNKNOWN_VALIDATION_ERROR;
@@ -36,7 +41,7 @@ export const getErrors = <T extends Record<string, any>>(
3641
userDefinedMessages?: Record<string, AJVMessageFunction>,
3742
): useFormErrors<T> => {
3843
return ajvErrors.reduce((acc, current) => {
39-
const fieldName: string | null = getFieldName(current);
44+
const fieldName: string | null = getFieldName(current?.instancePath);
4045
if (!fieldName) {
4146
return acc;
4247
}

src/utils/types.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ErrorObject } from 'ajv';
2+
13
export type AJVErrorParams = {
24
limit?: number;
35
missingProperty?: string;
@@ -20,12 +22,6 @@ export type DefaultAJVMessages = {
2022
format: AJVMessageFunction;
2123
};
2224

23-
export type ErrorObject = {
24-
keyword: keyof DefaultAJVMessages;
25-
params: AJVErrorParams;
26-
message?: string;
27-
};
28-
2925
export type FormField<T> = {
3026
[K in keyof T]: T[K];
3127
};
@@ -62,4 +58,5 @@ export interface UseFormReturn<T> {
6258
isDirty: boolean;
6359
isValid: boolean;
6460
onBlur: (fieldName: string) => void;
61+
setErrors: (errors: ErrorObject[]) => void;
6562
}

0 commit comments

Comments
 (0)