diff --git a/packages/web/src/components/FlowStep/index.jsx b/packages/web/src/components/FlowStep/index.jsx
index 853c24bb..0dee4bbe 100644
--- a/packages/web/src/components/FlowStep/index.jsx
+++ b/packages/web/src/components/FlowStep/index.jsx
@@ -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 = ;
const errorIcon = ;
-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);
diff --git a/packages/web/src/components/FlowStep/validation.js b/packages/web/src/components/FlowStep/validation.js
new file mode 100644
index 00000000..134a4f62
--- /dev/null
+++ b/packages/web/src/components/FlowStep/validation.js
@@ -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);
+}