Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/lib/unstable/core/SchemaRenderer/SchemaRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,32 @@ const SchemaRendererComponent: React.FC<SchemaRendererProps> = ({
config,
errorMessages,
mode,
name,
name: headName,
schema,
}) => {
const serviceFieldName = `DYNAMIC_FORMS_SERVICE_FIELD/${name}`;
const serviceFieldName = `DYNAMIC_FORMS_SERVICE_FIELD.${headName}`;

const context: SchemaRendererContextType = React.useMemo(
() => ({
config,
mode,
name,
headName,
schema,
serviceFieldName,
}),
[config, mode, name, schema, serviceFieldName],
[config, mode, headName, schema, serviceFieldName],
);

return (
<SchemaRendererContext.Provider value={context}>
<SchemaRendererServiceField
config={config}
errorMessages={errorMessages}
name={name}
headName={headName}
mainSchema={schema}
serviceFieldName={serviceFieldName}
/>
<Entity name={name} schema={schema} />
<Entity name={headName} schema={schema} />
</SchemaRendererContext.Provider>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/lib/unstable/core/SchemaRendererContext/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {JsonSchema, SchemaRendererConfig} from '../types';
export interface SchemaRendererContextType {
config: SchemaRendererConfig;
mode: SchemaRendererMode;
name: string;
headName: string;
schema: JsonSchema;
serviceFieldName: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import {getValidate} from './utils';
export interface SchemaRendererProps {
config: SchemaRendererConfig;
errorMessages?: ErrorMessages;
name: string;
headName: string;
mainSchema: JsonSchema;
serviceFieldName: string;
}

const SchemaRendererServiceFieldComponent: React.FC<SchemaRendererProps> = ({
config,
errorMessages,
name,
headName,
mainSchema,
serviceFieldName,
}) => {
Expand All @@ -28,7 +28,7 @@ const SchemaRendererServiceFieldComponent: React.FC<SchemaRendererProps> = ({
getValidate({
config,
errorMessages,
name,
headName,
mainSchema,
serviceFieldName,
setValidationCache,
Expand All @@ -37,7 +37,7 @@ const SchemaRendererServiceFieldComponent: React.FC<SchemaRendererProps> = ({
[
config,
errorMessages,
name,
headName,
mainSchema,
serviceFieldName,
setValidationCache,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export interface GetAjvErrorMessageParams {
export type GetValidateParams<Schema extends JsonSchema> = {
config: SchemaRendererConfig;
errorMessages?: ErrorMessages;
name: string;
headName: string;
mainSchema: Schema;
serviceFieldName: string;
setValidationCache: SetValidationCacheMutator;
Expand Down
79 changes: 55 additions & 24 deletions src/lib/unstable/core/SchemaRendererServiceField/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import Ajv from 'ajv';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';
import isString from 'lodash/isString';
import mapValues from 'lodash/mapValues';
import omit from 'lodash/omit';
import set from 'lodash/set';

import {ARRAY_AND_OBJECT_ERRORS, EMPTY_OBJECT, JsonSchemaType} from '../constants';
import type {ValidationState, ValidationWaiter} from '../mutators';
import {ARRAY_AND_OBJECT_ERRORS, EMPTY_OBJECT, JsonSchemaType, OBJECT_ERROR} from '../constants';
import type {ErrorsState, ValidationState, ValidationWaiter} from '../mutators';
import type {FieldValue, JsonSchema, ObjectValue, SyncValidateError, Validator} from '../types';
import {parseFinalFormPath} from '../utils';
import {getSchemaByFinalFormPath, parseFinalFormPath} from '../utils';

import type {
EntityParametersError,
Expand Down Expand Up @@ -73,6 +74,10 @@ const parseSchemaPath = (schemaPath: string): string[] => {
};

const parseInstancePath = (instancePath: string): string[] => {
if (!instancePath.length) {
return [];
}

return instancePath
.slice('/'.length)
.split('/')
Expand All @@ -85,7 +90,11 @@ const getSchemaBySchemaPath = (
): JsonSchema | undefined => {
const pathArr = parseSchemaPath(schemaPath);

return pathArr.length ? get(mainSchema, pathArr) : mainSchema;
if (!pathArr.length) {
return mainSchema;
}

return get(mainSchema, pathArr);
};

const getSchemaByInstancePath = (
Expand Down Expand Up @@ -148,7 +157,7 @@ const getAjvErrorMessage = ({
export const getValidate = <Schema extends JsonSchema>({
config,
errorMessages,
name,
headName,
mainSchema,
serviceFieldName,
setValidationCache,
Expand All @@ -157,15 +166,17 @@ export const getValidate = <Schema extends JsonSchema>({
const ajvValidate = getAjvValidate({config, mainSchema});

return (_value, allValues, meta) => {
ajvValidate(get(allValues, name));
ajvValidate(get(allValues, headName));

if (!ajvValidate.errors?.length) {
return false;
}

const result = {[ARRAY_AND_OBJECT_ERRORS]: {}};

const waiters: Record<string, ValidationWaiter> = {};
const ajvErrors: {instancePath: string; error: SyncValidateError}[] = [];
const entityParametersErrors: {instancePath: string; error: SyncValidateError}[] = [];
const ajvErrors: {path: string[]; error: SyncValidateError}[] = [];
const entityParametersErrors: {path: string[]; error: SyncValidateError}[] = [];

ajvValidate.errors.forEach((ajvOrEntityParametersError) => {
if (ajvOrEntityParametersError.keyword === 'entityParameters') {
Expand All @@ -180,7 +191,10 @@ export const getValidate = <Schema extends JsonSchema>({

if (cacheItem?.result) {
entityParametersErrors.push({
instancePath: entityParametersError.instancePath,
path: [
...parseFinalFormPath(headName),
...parseInstancePath(entityParametersError.instancePath),
],
error: cacheItem.result,
});
} else if (!waiter || !isEqual(entityParametersError.params, waiter)) {
Expand All @@ -205,7 +219,10 @@ export const getValidate = <Schema extends JsonSchema>({
});
} else {
entityParametersErrors.push({
instancePath: entityParametersError.instancePath,
path: [
...parseFinalFormPath(headName),
...parseInstancePath(entityParametersError.instancePath),
],
error: errorOrPromise,
});
}
Expand All @@ -225,7 +242,10 @@ export const getValidate = <Schema extends JsonSchema>({
}

ajvErrors.push({
instancePath,
path: [
...parseFinalFormPath(headName),
...parseInstancePath(ajvError.instancePath),
],
error: getAjvErrorMessage({
ajvErrorMessage: ajvError.message,
errorMessages,
Expand All @@ -242,25 +262,36 @@ export const getValidate = <Schema extends JsonSchema>({
setValidationWaiters({name: serviceFieldName, waiters});
}

const result = {[ARRAY_AND_OBJECT_ERRORS]: {}};
const errorsState: ErrorsState | undefined = meta?.data;
const externalRegularErrors: {path: string[]; error: SyncValidateError}[] = Object.values(
mapValues(errorsState?.regularErrors, (value, key) => ({
path: parseFinalFormPath(key),
error: value,
})),
);
const externalPriorityErrors: {path: string[]; error: SyncValidateError}[] = Object.values(
mapValues(errorsState?.priorityErrors, (value, key) => ({
path: parseFinalFormPath(key),
error: value,
})),
);

[...ajvErrors, ...entityParametersErrors].forEach((item) => {
[
...externalRegularErrors,
...ajvErrors,
...entityParametersErrors,
...externalPriorityErrors,
].forEach((item) => {
if (item.error) {
const itemSchema = getSchemaByInstancePath(item.instancePath, mainSchema);
const itemSchema = getSchemaByFinalFormPath(item.path, headName, mainSchema);

if (itemSchema) {
const arrayOrObjectSchema =
itemSchema.type === JsonSchemaType.Array ||
itemSchema.type === JsonSchemaType.Object;

const path = [
...parseFinalFormPath(name),
...parseInstancePath(item.instancePath),
];
const arraySchema = itemSchema.type === JsonSchemaType.Array;
const objectSchema = itemSchema.type === JsonSchemaType.Object;

set(
arrayOrObjectSchema ? result[ARRAY_AND_OBJECT_ERRORS] : result,
path,
arraySchema || objectSchema ? result[ARRAY_AND_OBJECT_ERRORS] : result,
objectSchema ? [...item.path, OBJECT_ERROR] : item.path,
item.error,
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib/unstable/core/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ export enum JsonSchemaType {
export const EMPTY_OBJECT = {};

export const ARRAY_AND_OBJECT_ERRORS = 'ARRAY_AND_OBJECT_ERRORS';

export const OBJECT_ERROR = 'OBJECT_ERROR';
1 change: 1 addition & 0 deletions src/lib/unstable/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export {JsonSchemaType, SchemaRendererMode} from './constants';
export {schemaRendererMutators} from './mutators';
export * from './types';
export * from './useSchemaRendererField';
export * from './useSetErrors';
4 changes: 4 additions & 0 deletions src/lib/unstable/core/mutators/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import {setValidationCache, setValidationWaiters} from './async-validation';
import {removeErrors, setErrors} from './set-errors';

export * from './async-validation/types';
export * from './set-errors/types';

export const schemaRendererMutators = {
removeErrors,
setErrors,
setValidationCache,
setValidationWaiters,
};
2 changes: 2 additions & 0 deletions src/lib/unstable/core/mutators/set-errors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './set-errors';
export * from './types';
40 changes: 40 additions & 0 deletions src/lib/unstable/core/mutators/set-errors/set-errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import omit from 'lodash/omit';

import type {ErrorsState, RemoveErrorsFunction, SetErrorsFunction} from './types';

export const setErrors: SetErrorsFunction = (
[{serviceFieldName, priorityErrors, regularErrors}],
mutableState,
) => {
const errorsState = mutableState.fields[serviceFieldName]?.data as ErrorsState | undefined;

if (errorsState && (priorityErrors || regularErrors)) {
errorsState.priorityErrors = {
...errorsState.priorityErrors,
...(priorityErrors || {}),
};
errorsState.regularErrors = {
...errorsState.regularErrors,
...(regularErrors || {}),
};
}
};

export const removeErrors: RemoveErrorsFunction = (
[{removeFunctionOrNames, serviceFieldName}],
mutableState,
) => {
const errorsState = mutableState.fields[serviceFieldName]?.data as ErrorsState | undefined;

if (errorsState && removeFunctionOrNames) {
const {priorityErrors, regularErrors} = Array.isArray(removeFunctionOrNames)
? {
priorityErrors: omit(errorsState.priorityErrors, removeFunctionOrNames),
regularErrors: omit(errorsState.regularErrors, removeFunctionOrNames),
}
: removeFunctionOrNames(errorsState);

errorsState.priorityErrors = priorityErrors;
errorsState.regularErrors = regularErrors;
}
};
41 changes: 41 additions & 0 deletions src/lib/unstable/core/mutators/set-errors/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type {MutableState, Tools} from 'final-form';

export interface ErrorsState {
priorityErrors?: {
[key: string]: string | undefined;
};
regularErrors?: {
[key: string]: string | undefined;
};
}

export interface SetErrorsParams {
priorityErrors?: {
[key: string]: string | undefined;
};
serviceFieldName: string;
regularErrors?: {
[key: string]: string | undefined;
};
}

export type SetErrorsFunction<FormValues = object, InitialFormValues = Partial<FormValues>> = (
args: [SetErrorsParams],
state: MutableState<FormValues, InitialFormValues>,
tools: Tools<FormValues, InitialFormValues>,
) => void;

export type SetErrorsMutator = (params: SetErrorsParams) => void;

export interface RemoveErrorsParams {
removeFunctionOrNames: string[] | ((state: ErrorsState) => ErrorsState);
serviceFieldName: string;
}

export type RemoveErrorsFunction<FormValues = object, InitialFormValues = Partial<FormValues>> = (
args: [RemoveErrorsParams],
state: MutableState<FormValues, InitialFormValues>,
tools: Tools<FormValues, InitialFormValues>,
) => void;

export type RemoveErrorsMutator = (params: RemoveErrorsParams) => void;
Loading