feat: add dynamic fields to validation schema and debug problem with dependsOn field
This commit is contained in:
@@ -11,9 +11,6 @@ import IconButton from '@mui/material/IconButton';
|
||||
import ErrorIcon from '@mui/icons-material/Error';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
|
||||
import { EditorContext } from 'contexts/Editor';
|
||||
import { StepExecutionsProvider } from 'contexts/StepExecutions';
|
||||
import TestSubstep from 'components/TestSubstep';
|
||||
@@ -33,77 +30,17 @@ import {
|
||||
Header,
|
||||
Wrapper,
|
||||
} from './style';
|
||||
import isEmpty from 'helpers/isEmpty';
|
||||
import { StepPropType } from 'propTypes/propTypes';
|
||||
import useTriggers from 'hooks/useTriggers';
|
||||
import useActions from 'hooks/useActions';
|
||||
import useTriggerSubsteps from 'hooks/useTriggerSubsteps';
|
||||
import useActionSubsteps from 'hooks/useActionSubsteps';
|
||||
import useStepWithTestExecutions from 'hooks/useStepWithTestExecutions';
|
||||
import { generateValidationSchema } from './validation';
|
||||
|
||||
const validIcon = <CheckCircleIcon color="success" />;
|
||||
const errorIcon = <ErrorIcon color="error" />;
|
||||
|
||||
function generateValidationSchema(substeps) {
|
||||
const fieldValidations = substeps?.reduce(
|
||||
(allValidations, { arguments: args }) => {
|
||||
if (!args || !Array.isArray(args)) return allValidations;
|
||||
const substepArgumentValidations = {};
|
||||
for (const arg of args) {
|
||||
const { key, required } = arg;
|
||||
// base validation for the field if not exists
|
||||
if (!substepArgumentValidations[key]) {
|
||||
substepArgumentValidations[key] = yup.mixed();
|
||||
}
|
||||
if (
|
||||
typeof substepArgumentValidations[key] === 'object' &&
|
||||
(arg.type === 'string' || arg.type === 'dropdown')
|
||||
) {
|
||||
// if the field is required, add the required validation
|
||||
if (required) {
|
||||
substepArgumentValidations[key] = substepArgumentValidations[key]
|
||||
.required(`${key} is required.`)
|
||||
.test(
|
||||
'empty-check',
|
||||
`${key} must be not empty`,
|
||||
(value) => !isEmpty(value),
|
||||
);
|
||||
}
|
||||
// if the field depends on another field, add the dependsOn required validation
|
||||
if (Array.isArray(arg.dependsOn) && arg.dependsOn.length > 0) {
|
||||
for (const dependsOnKey of arg.dependsOn) {
|
||||
const missingDependencyValueMessage = `We're having trouble loading '${key}' data as required field '${dependsOnKey}' is missing.`;
|
||||
// TODO: make `dependsOnKey` agnostic to the field. However, nested validation schema is not supported.
|
||||
// So the fields under the `parameters` key are subject to their siblings only and thus, `parameters.` is removed.
|
||||
substepArgumentValidations[key] = substepArgumentValidations[
|
||||
key
|
||||
].when(`${dependsOnKey.replace('parameters.', '')}`, {
|
||||
is: (value) => Boolean(value) === false,
|
||||
then: (schema) =>
|
||||
schema
|
||||
.notOneOf([''], missingDependencyValueMessage)
|
||||
.required(missingDependencyValueMessage),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...allValidations,
|
||||
...substepArgumentValidations,
|
||||
};
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const validationSchema = yup.object({
|
||||
parameters: yup.object(fieldValidations),
|
||||
});
|
||||
|
||||
return yupResolver(validationSchema);
|
||||
}
|
||||
|
||||
function FlowStep(props) {
|
||||
const { collapsed, onChange, onContinue, flowId } = props;
|
||||
const editorContext = React.useContext(EditorContext);
|
||||
|
114
packages/web/src/components/FlowStep/validation.js
Normal file
114
packages/web/src/components/FlowStep/validation.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import * as yup from 'yup';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import isEmpty from 'helpers/isEmpty';
|
||||
|
||||
function addRequiredValidation({ required, schema, key }) {
|
||||
// if the field is required, add the required validation
|
||||
if (required) {
|
||||
return schema
|
||||
.required(`${key} is required.`)
|
||||
.test(
|
||||
'empty-check',
|
||||
`${key} must be not empty`,
|
||||
(value) => !isEmpty(value),
|
||||
);
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
function addDependsOnValidation({ schema, dependsOn, key, args }) {
|
||||
// if the field depends on another field, add the dependsOn required validation
|
||||
if (Array.isArray(dependsOn) && dependsOn.length > 0) {
|
||||
for (const dependsOnKey of dependsOn) {
|
||||
const dependsOnKeyShort = dependsOnKey.replace('parameters.', '');
|
||||
const dependsOnField = args.find(({ key }) => key === dependsOnKeyShort);
|
||||
|
||||
if (dependsOnField?.required) {
|
||||
const missingDependencyValueMessage = `We're having trouble loading '${key}' data as required field '${dependsOnKey}' is missing.`;
|
||||
|
||||
// TODO: make `dependsOnKey` agnostic to the field. However, nested validation schema is not supported.
|
||||
// So the fields under the `parameters` key are subject to their siblings only and thus, `parameters.` is removed.
|
||||
return schema.when(dependsOnKeyShort, {
|
||||
is: (dependsOnValue) => Boolean(dependsOnValue) === false,
|
||||
then: (schema) =>
|
||||
schema
|
||||
.notOneOf([''], missingDependencyValueMessage)
|
||||
.required(missingDependencyValueMessage),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
export function generateValidationSchema(substeps) {
|
||||
const fieldValidations = substeps?.reduce(
|
||||
(allValidations, { arguments: args }) => {
|
||||
if (!args || !Array.isArray(args)) return allValidations;
|
||||
|
||||
const substepArgumentValidations = {};
|
||||
|
||||
for (const arg of args) {
|
||||
const { key, required } = arg;
|
||||
|
||||
// base validation for the field if not exists
|
||||
if (!substepArgumentValidations[key]) {
|
||||
substepArgumentValidations[key] = yup.mixed();
|
||||
}
|
||||
|
||||
if (arg.type === 'dynamic') {
|
||||
const fieldsSchema = {};
|
||||
|
||||
for (const field of arg.fields) {
|
||||
fieldsSchema[field.key] = yup.mixed();
|
||||
|
||||
fieldsSchema[field.key] = addRequiredValidation({
|
||||
required: field.required,
|
||||
schema: fieldsSchema[field.key],
|
||||
key: field.key,
|
||||
});
|
||||
|
||||
fieldsSchema[field.key] = addDependsOnValidation({
|
||||
schema: fieldsSchema[field.key],
|
||||
dependsOn: field.dependsOn,
|
||||
key: field.key,
|
||||
args,
|
||||
});
|
||||
}
|
||||
|
||||
substepArgumentValidations[key] = yup
|
||||
.array()
|
||||
.of(yup.object(fieldsSchema));
|
||||
} else if (
|
||||
typeof substepArgumentValidations[key] === 'object' &&
|
||||
(arg.type === 'string' || arg.type === 'dropdown')
|
||||
) {
|
||||
substepArgumentValidations[key] = addRequiredValidation({
|
||||
required,
|
||||
schema: substepArgumentValidations[key],
|
||||
key,
|
||||
});
|
||||
|
||||
substepArgumentValidations[key] = addDependsOnValidation({
|
||||
schema: substepArgumentValidations[key],
|
||||
dependsOn: arg.dependsOn,
|
||||
key,
|
||||
args,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...allValidations,
|
||||
...substepArgumentValidations,
|
||||
};
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const validationSchema = yup.object({
|
||||
parameters: yup.object(fieldValidations),
|
||||
});
|
||||
|
||||
return yupResolver(validationSchema);
|
||||
}
|
Reference in New Issue
Block a user