Search code examples
reactjssemantic-uiformik

How to validate Formik FieldArray which uses Semantic dropdown with allow additions


I have a formik fieldarray where every row would have 3 dropdowns. And I would like to have a validation schema which works this way:

If email is selected in the first dropdown, then the value given in the 3rd dropdown(it has allowAdditions) to be validated if a valid email id is given or not.

If a <, <=, >, >= operator is selected in the second dropdown,then the value given in the 3rd dropdown(it has allowAdditions) to be validated if it has a number only.

Before achieveing this, I tried to give a simple validation to validate if the length od the 1st dropdown is greater than 6. Though the validation worked, no warning message is shown as it comes with normal FormInput in formik. Not sure if this is because it is a dropdown.

Validation:

  const validationSchema = object().shape({
    rows: array().of(
      object().shape({
        value: string().min(4, "too short").required("Required")
      })
    )
  });

Formik:

<Formik
      initialValues={{ rows: initialValues }}
      onSubmit={(values) => {
        // transform the rows to add the condition key for each row object
        const output = values.rows.map((row, index) => {
          if (index === 0) {
            return { ...row, condition: "if" };
          } else {
            return { ...row, condition: "and" };
          }
        });

        console.log(output);
      }}
      validationSchema={validationSchema}
    >
      {({ handleSubmit, values, setFieldValue }) => (
        <Form onSubmit={handleSubmit} className={"rulesetForm"}>
          <pre>{JSON.stringify(values, null, 2)}</pre>
          <FieldArray
            name="rows"
            render={({ push, remove }) => {
              return (
                values.rows.length > 0 &&
                values.rows.map((row, index) => {
                  const mainFieldOptions = getMainFieldOptions(
                    values.rows,
                    index
                  );
                  return (
                    <Grid key={`mainfield-operation-value-${index}`}>
                      <Grid.Row className={"rulesetGrid fluid"}>
                        {index === 0 ? (
                          <p className="condition"> If</p>
                        ) : (
                          <p className="condition"> And</p>
                        )}

                        <Dropdown
                          name={`rows.${index}.mainField`}
                          className={"dropdown fieldDropdown"}
                          widths={2}
                          placeholder="Field"
                          fluid
                          selection
                          options={mainFieldOptions}
                          value={row.mainField || ""}
                          onChange={(e, { value }) => {
                            setFieldValue(`rows.${index}.mainField`, value);
                          }}
                        />

                        <Dropdown
                          name={`rows.${index}.operation`}
                          className={"dropdown operationDropdown"}
                          widths={2}
                          placeholder="Operation"
                          fluid
                          selection
                          options={operation}
                          value={row.operation}
                          onChange={(e, { value }) =>
                            setFieldValue(`rows.${index}.operation`, value)
                          }
                        />
                        <Dropdown
                          name={`rows.${index}.value`}
                          className={"dropdown valueDropdown"}
                          widths={1}
                          placeholder="Value"
                          fluid
                          search
                          allowAdditions
                          selection
                          multiple
                          options={dropDownOptions}
                          value={row.value}
                          onAddItem={handleAddition}
                          onChange={(e, { value }) =>
                            setFieldValue(`rows.${index}.value`, value)
                          }
                        />

                        {values.rows.length - 1 === index && (
                          <Icon
                            className={"  plus icon plusIcon"}
                            onClick={() => push(initialValues[0])}
                          />
                        )}
                        {values.rows.length !== 1 && (
                          <Icon
                            className={"minus crossIcon"}
                            onClick={() => remove(index)}
                          />
                        )}
                      </Grid.Row>
                    </Grid>
                  );
                })
              );
            }}
          />
          <div>
            <div style={{ marginTop: "1rem" }}>
              <Button
                floated="right"
                type="submit"
                variant="contained"
                primary={true}
              >
                Submit
              </Button>
            </div>
          </div>
        </Form>
      )}
    </Formik>

Here is the sandbox link: https://codesandbox.io/s/semantic-ui-example-forked-lmqi9?file=/example.js:1826-6181

Any help is really really appreciated and would mean a lot. Thanks in advance


Solution

  • You need to use the Yup When along with Test

    Working Sandbox

    CodeSandbox