From abaa7aa4d7eeff4a3b6bcf03a5c24482dd744844 Mon Sep 17 00:00:00 2001 From: Nikita Zolotykh Date: Tue, 14 Nov 2023 11:38:24 +0300 Subject: [PATCH] feat: add external errors --- src/lib/core/components/Form/Controller.tsx | 3 +- src/lib/core/components/Form/DynamicField.tsx | 7 ++-- .../core/components/Form/hooks/useField.tsx | 36 ++++++++++++++++++- src/lib/core/components/Form/types/context.ts | 3 +- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/lib/core/components/Form/Controller.tsx b/src/lib/core/components/Form/Controller.tsx index e33b1eaa..ba07d3b4 100644 --- a/src/lib/core/components/Form/Controller.tsx +++ b/src/lib/core/components/Form/Controller.tsx @@ -34,7 +34,7 @@ export const Controller = ({ parentOnChange, parentOnUnmount, }: ControllerProps) => { - const {tools, __mirror} = useDynamicFormsCtx(); + const {tools, externalErrors, __mirror} = useDynamicFormsCtx(); const {inputEntity, Layout} = useComponents(spec); const render = useRender({name, spec, inputEntity, Layout}); const validate = useValidate(spec); @@ -47,6 +47,7 @@ export const Controller = ({ tools, parentOnChange, parentOnUnmount, + externalErrors, }); const withSearch = useSearch(spec, renderProps.input.value, name); diff --git a/src/lib/core/components/Form/DynamicField.tsx b/src/lib/core/components/Form/DynamicField.tsx index 6cf73b1e..e2d5a8d3 100644 --- a/src/lib/core/components/Form/DynamicField.tsx +++ b/src/lib/core/components/Form/DynamicField.tsx @@ -16,7 +16,7 @@ import { useSearchStore, useStore, } from './hooks'; -import {DynamicFormConfig, FieldValue, WonderMirror} from './types'; +import {BaseValidateError, DynamicFormConfig, FieldValue, WonderMirror} from './types'; import {getDefaultSearchFunction, isCorrectConfig} from './utils'; export interface DynamicFieldProps { @@ -27,6 +27,7 @@ export interface DynamicFieldProps { search?: string | ((spec: Spec, input: FieldValue, name: string) => boolean); generateRandomValue?: (spec: StringSpec) => string; withoutInsertFFDebounce?: boolean; + errors?: Record; __mirror?: WonderMirror; } @@ -38,6 +39,7 @@ export const DynamicField: React.FC = ({ generateRandomValue, search, withoutInsertFFDebounce, + errors: externalErrors, __mirror, }) => { const DynamicFormsCtx = useCreateContext(); @@ -52,9 +54,10 @@ export const DynamicField: React.FC = ({ Monaco: isValidElementType(Monaco) ? Monaco : undefined, generateRandomValue, tools, + externalErrors, __mirror, }), - [tools, config, Monaco, __mirror, generateRandomValue], + [tools, config, Monaco, __mirror, generateRandomValue, externalErrors], ); const searchContext = React.useMemo( diff --git a/src/lib/core/components/Form/hooks/useField.tsx b/src/lib/core/components/Form/hooks/useField.tsx index 525a95f5..e7d2ee3f 100644 --- a/src/lib/core/components/Form/hooks/useField.tsx +++ b/src/lib/core/components/Form/hooks/useField.tsx @@ -6,6 +6,7 @@ import {isArraySpec, isNumberSpec, isObjectSpec} from '../../../helpers'; import {Spec} from '../../../types'; import {OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG} from '../constants'; import { + BaseValidateError, DynamicFormsContext, FieldArrayValue, FieldObjectValue, @@ -30,6 +31,7 @@ export interface UseFieldProps ) => void) | null; parentOnUnmount: ((childName: string) => void) | null; + externalErrors?: Record; } export const useField = ({ @@ -41,6 +43,7 @@ export const useField = ({ tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, + externalErrors, }: UseFieldProps): FieldRenderProps => { const firstRenderRef = React.useRef(true); @@ -67,7 +70,19 @@ export const useField = ({ } } - const error = validate?.(value); + let externalError = _.get(externalErrors, name); + + if ( + !( + _.isString(externalError) || + _.isBoolean(externalError) || + _.isUndefined(externalError) + ) + ) { + externalError = undefined; + } + + const error = validate?.(value) || externalError; const dirty = !_.isEqual(value, initialValue); return { @@ -254,6 +269,25 @@ export const useField = ({ } }, [state.value]); + React.useEffect(() => { + const externalError = _.get(externalErrors, name); + + if ( + !firstRenderRef.current && + (_.isString(externalError) || + _.isBoolean(externalError) || + _.isUndefined(externalError)) && + state.error !== externalError && + !(state.error && !externalError) + ) { + setState({...state, error: externalError}); + (parentOnChange ? parentOnChange : tools.onChange)(name, state.value, { + ...state.childErrors, + [name]: externalError, + }); + } + }, [externalErrors]); + React.useEffect(() => { firstRenderRef.current = false; diff --git a/src/lib/core/components/Form/types/context.ts b/src/lib/core/components/Form/types/context.ts index 62eac16d..7e3c9808 100644 --- a/src/lib/core/components/Form/types/context.ts +++ b/src/lib/core/components/Form/types/context.ts @@ -4,7 +4,7 @@ import type {MonacoEditorProps} from 'react-monaco-editor/lib/types'; import {StringSpec} from '../../../types'; -import {DynamicFormConfig, FieldValue, ValidateError, WonderMirror} from './'; +import {BaseValidateError, DynamicFormConfig, FieldValue, ValidateError, WonderMirror} from './'; export interface DynamicFormsContext { config: DynamicFormConfig; @@ -16,5 +16,6 @@ export interface DynamicFormsContext { onUnmount: (name: string) => void; submitFailed: boolean; }; + externalErrors?: Record; __mirror?: WonderMirror; }