Skip to content

Commit 4c93b37

Browse files
authored
feat: add external errors (#137)
1 parent 1e19619 commit 4c93b37

File tree

5 files changed

+48
-9
lines changed

5 files changed

+48
-9
lines changed

src/lib/core/components/Form/Controller.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const Controller = <Value extends FieldValue, SpecType extends Spec>({
3434
parentOnChange,
3535
parentOnUnmount,
3636
}: ControllerProps<Value, SpecType>) => {
37-
const {tools, __mirror} = useDynamicFormsCtx();
37+
const {tools, externalErrors, __mirror} = useDynamicFormsCtx();
3838
const {inputEntity, Layout} = useComponents(spec);
3939
const render = useRender({name, spec, inputEntity, Layout});
4040
const validate = useValidate(spec);
@@ -47,6 +47,7 @@ export const Controller = <Value extends FieldValue, SpecType extends Spec>({
4747
tools,
4848
parentOnChange,
4949
parentOnUnmount,
50+
externalErrors,
5051
});
5152
const withSearch = useSearch(spec, renderProps.input.value, name);
5253

src/lib/core/components/Form/DynamicField.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
useSearchStore,
1717
useStore,
1818
} from './hooks';
19-
import {DynamicFormConfig, FieldValue, WonderMirror} from './types';
19+
import {BaseValidateError, DynamicFormConfig, FieldValue, WonderMirror} from './types';
2020
import {getDefaultSearchFunction, isCorrectConfig} from './utils';
2121

2222
export interface DynamicFieldProps {
@@ -27,6 +27,7 @@ export interface DynamicFieldProps {
2727
search?: string | ((spec: Spec, input: FieldValue, name: string) => boolean);
2828
generateRandomValue?: (spec: StringSpec) => string;
2929
withoutInsertFFDebounce?: boolean;
30+
errors?: Record<string, BaseValidateError>;
3031
__mirror?: WonderMirror;
3132
}
3233

@@ -38,6 +39,7 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
3839
generateRandomValue,
3940
search,
4041
withoutInsertFFDebounce,
42+
errors: externalErrors,
4143
__mirror,
4244
}) => {
4345
const DynamicFormsCtx = useCreateContext();
@@ -52,9 +54,10 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
5254
Monaco: isValidElementType(Monaco) ? Monaco : undefined,
5355
generateRandomValue,
5456
tools,
57+
externalErrors,
5558
__mirror,
5659
}),
57-
[tools, config, Monaco, __mirror, generateRandomValue],
60+
[tools, config, Monaco, __mirror, generateRandomValue, externalErrors],
5861
);
5962

6063
const searchContext = React.useMemo(

src/lib/core/components/Form/hooks/__tests__/useField.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ describe('Form/hooks/useField', () => {
8282

8383
expect(mirror.field.useStore?.store.values[name]).not.toBe(value[name]);
8484
expect(mirror.field.useStore?.store.values[name]).toMatchObject(value[name]);
85-
expect(mirror.field.useStore?.store.errors[name]).toBe(false);
85+
expect(mirror.field.useStore?.store.errors[name]).toBe(undefined);
8686
});
8787

8888
test('initialization with required object spec', () => {
@@ -117,7 +117,7 @@ describe('Form/hooks/useField', () => {
117117
expect(mirror.controller[name]?.useField?.arrayInput.value).toMatchObject(objectDefault);
118118

119119
expect(mirror.field.useStore?.store.values[name]).toMatchObject(objectDefault);
120-
expect(mirror.field.useStore?.store.errors[name]).toBe(false);
120+
expect(mirror.field.useStore?.store.errors[name]).toBe(undefined);
121121
});
122122

123123
test('initialization with required array spec', () => {
@@ -152,7 +152,7 @@ describe('Form/hooks/useField', () => {
152152
expect(mirror.controller[name]?.useField?.arrayInput.value).toMatchObject(arrayDefault);
153153

154154
expect(mirror.field.useStore?.store.values[name]).toMatchObject(arrayDefault);
155-
expect(mirror.field.useStore?.store.errors[name]).toBe(false);
155+
expect(mirror.field.useStore?.store.errors[name]).toBe(undefined);
156156
});
157157

158158
test('initialization with error', () => {
@@ -498,7 +498,7 @@ describe('Form/hooks/useField', () => {
498498
);
499499

500500
expect(mirror.field.useStore?.store.values[name]).toMatchObject({});
501-
expect(mirror.field.useStore?.store.errors[name]).toBe(false);
501+
expect(mirror.field.useStore?.store.errors[name]).toBe(undefined);
502502

503503
rerender(
504504
<Form initialValues={{}} onSubmit={_.noop}>

src/lib/core/components/Form/hooks/useField.tsx

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {isArraySpec, isNumberSpec, isObjectSpec} from '../../../helpers';
66
import {Spec} from '../../../types';
77
import {OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG} from '../constants';
88
import {
9+
BaseValidateError,
910
DynamicFormsContext,
1011
FieldArrayValue,
1112
FieldObjectValue,
@@ -30,6 +31,7 @@ export interface UseFieldProps<Value extends FieldValue, SpecType extends Spec>
3031
) => void)
3132
| null;
3233
parentOnUnmount: ((childName: string) => void) | null;
34+
externalErrors?: Record<string, BaseValidateError>;
3335
}
3436

3537
export const useField = <Value extends FieldValue, SpecType extends Spec>({
@@ -41,6 +43,7 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
4143
tools,
4244
parentOnChange,
4345
parentOnUnmount: externalParentOnUnmount,
46+
externalErrors,
4447
}: UseFieldProps<Value, SpecType>): FieldRenderProps<Value> => {
4548
const firstRenderRef = React.useRef(true);
4649

@@ -67,7 +70,19 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
6770
}
6871
}
6972

70-
const error = validate?.(value);
73+
let externalError = _.get(externalErrors, name);
74+
75+
if (
76+
!(
77+
_.isString(externalError) ||
78+
_.isBoolean(externalError) ||
79+
_.isUndefined(externalError)
80+
)
81+
) {
82+
externalError = undefined;
83+
}
84+
85+
const error = validate?.(value) || externalError;
7186
const dirty = !_.isEqual(value, initialValue);
7287

7388
return {
@@ -254,6 +269,25 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
254269
}
255270
}, [state.value]);
256271

272+
React.useEffect(() => {
273+
const externalError = _.get(externalErrors, name);
274+
275+
if (
276+
!firstRenderRef.current &&
277+
(_.isString(externalError) ||
278+
_.isBoolean(externalError) ||
279+
_.isUndefined(externalError)) &&
280+
state.error !== externalError &&
281+
!(state.error && !externalError)
282+
) {
283+
setState({...state, error: externalError});
284+
(parentOnChange ? parentOnChange : tools.onChange)(name, state.value, {
285+
...state.childErrors,
286+
[name]: externalError,
287+
});
288+
}
289+
}, [externalErrors]);
290+
257291
React.useEffect(() => {
258292
firstRenderRef.current = false;
259293

src/lib/core/components/Form/types/context.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {MonacoEditorProps} from 'react-monaco-editor/lib/types';
44

55
import {StringSpec} from '../../../types';
66

7-
import {DynamicFormConfig, FieldValue, ValidateError, WonderMirror} from './';
7+
import {BaseValidateError, DynamicFormConfig, FieldValue, ValidateError, WonderMirror} from './';
88

99
export interface DynamicFormsContext {
1010
config: DynamicFormConfig;
@@ -16,5 +16,6 @@ export interface DynamicFormsContext {
1616
onUnmount: (name: string) => void;
1717
submitFailed: boolean;
1818
};
19+
externalErrors?: Record<string, BaseValidateError>;
1920
__mirror?: WonderMirror;
2021
}

0 commit comments

Comments
 (0)