import { Meta } from '@storybook/addon-docs';

<Meta title="Patterns/Formik Form/Codesandbox" />

# Formik Form

Formik Form demonstrates a pattern for integrating the input elements found in Palmetto Components with [Formik](https://formik.org/). The form is fully functional and even mock submits. Manipulate the inputs in the form and watch the state object below it update with the new values in real-time. Attempt to submit the form without filling out required inputs, and observe the error states for these inputs.

```js codesandbox=palmetto-components
import React from 'react';
import { Field, Form, Formik } from 'formik';
import {
  Box,
  Button,
  DateInput,
  FormikCheckboxInput,
  FormikRadioGroup,
  FormikSelectInput,
  FormikSelectInputNative,
  FormikTextareaInput,
  FormikTextInput,
  FormikTimePickerNative,
  FormikToggle,
} from '@palmetto/palmetto-components';

export default () => {
  const flavorOptions = [
    { value: 'chocolate', label: 'Chocolate' },
    { value: 'strawberry', label: 'Strawberry' },
    { value: 'vanilla', label: 'Vanilla' },
  ];
  const colorOptions = [
    { value: 'red', label: 'red' },
    { value: 'orange', label: 'orange' },
    { value: 'yellow', label: 'yellow' },
    { value: 'green', label: 'green' },
    { value: 'blue', label: 'blue' },
    { value: 'indigo', label: 'indigo' },
    { value: 'violet', label: 'violet' },
  ];
  const sizeOptions = [
    { id: 'small', value: 'small', label: 'Small' },
    { id: 'medium', value: 'medium', label: 'Medium' },
    { id: 'large', value: 'large', label: 'Large' },
  ];
  const handleValidation = values => {
    const errors = {};
    if (!values.firstName) {
      errors.firstName = 'required';
    }
    if (!values.lastName) {
      errors.lastName = 'required';
    }
    if (!values.areTermsChecked) {
      errors.areTermsChecked = 'required';
    }
    if (!values.areTermsChecked2) {
      errors.areTermsChecked2 = 'required';
    }
    if (!values.email) {
      errors.email = 'required';
    } else if (
      !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
    ) {
      errors.email = 'Invalid email address';
    }
    if (!values.flavor) {
      errors.flavor = 'required';
    }
    if (!values.sizes) {
      errors.sizes = 'required';
    }
    if (!values.timePicker) {
      errors.timePicker = 'required';
    }
    if (!values.dateInput) {
      errors.dateInput = 'required';
    }
    return errors;
  };
  const handleSubmit = (values, { setSubmitting }) => {
    // Make API calls here
    setTimeout(() => {
      setSubmitting(false);
      alert(
        // eslint-disable-line no-alert
        `Submitted Successfully ->  ${JSON.stringify(values, null, 2)}`,
      );
    }, 2000);
  };
  const formatOutput = (values, isSubmitting) => {
    return { ...values, isSubmitting };
  };
  return (
    <Box maxWidth="500px" margin="lg auto">
      <Formik
        initialValues={{
          firstName: '',
          lastName: '',
          email: '',
          phoneInputMask: '',
          phoneInputMaskWithUnformattedValue: '',
          areTermsChecked: false,
          areTermsChecked2: false,
          flavor: null,
          flavor2: null,
          colors: [],
          sizes: null,
          timePicker: null,
          timePickerNative: null,
          dateInput: '',
        }}
        validate={handleValidation}
        onSubmit={handleSubmit}
      >
        {({ isSubmitting, values, setFieldValue, handleBlur, errors }) => (
          <Form>
            <Box gap="lg">
              <Field
                type="text"
                label="First Name"
                name="firstName"
                id="firstName"
                component={FormikTextInput}
                // With a custom onChange.
                // We preserve Formik's convention and relegate state form management back to the user.
                onChange={event => {
                  setFieldValue('firstName', event.target.value);
                }}
                isRequired
              />
              <Field
                type="text"
                label="Last Name"
                name="lastName"
                id="lastName"
                component={FormikTextInput}
                isRequired
              />
              <Field
                label="Email"
                name="email"
                id="email"
                component={FormikTextInput}
                isRequired
              />
              <Field
                label="Phone Input Mask Returning a Formatted Value"
                name="phoneInputMask"
                id="phoneInputMask"
                component={FormikTextInput}
                inputMask="phone"
                type="tel"
              />
              <Field
                label="Phone Input Mask Returning an Unformatted Value"
                name="phoneInputMaskWithUnformattedValue"
                id="phoneInputMaskWithUnformattedValue"
                component={FormikTextInput}
                inputMask="phone"
                type="tel"
                onChange={event => {
                  setFieldValue(
                    'phoneInputMaskWithUnformattedValue',
                    event.target.rawValue,
                  );
                }}
              />
              <Field
                label="Flavor with Custom onChange"
                name="flavor"
                id="flavor"
                options={flavorOptions}
                component={FormikSelectInputNative}
                // With a custom onChange.
                // We preserve Formik's convention and relegate form state management back to the user.
                onChange={event => {
                  setFieldValue('flavor', event.target.value);
                }}
                isRequired
              />
              <Field
                label="Flavor without custom onChange"
                name="flavor2"
                id="flavor2"
                options={flavorOptions}
                component={FormikSelectInputNative}
              />
              <Field
                label="Multi-select Colors"
                name="colors"
                id="colors"
                isMulti
                options={colorOptions}
                component={FormikSelectInput}
              />
              <Field
                label="Describe your perfect meal"
                name="meal"
                id="meal"
                component={FormikTextareaInput}
              />
              <Field
                title="Sizes"
                name="sizes"
                id="sizes"
                options={sizeOptions}
                component={FormikRadioGroup}
                isRequired
              />
              <Field
                label="Select Time With Native Select"
                name="timePicker"
                id="timePicker"
                component={FormikTimePickerNative}
                isRequired
              />
              <Field
                label="Terms and Conditions"
                name="areTermsChecked"
                id="areTermsChecked"
                component={FormikCheckboxInput}
                isRequired
              />
              <Field
                label="Terms and Conditions Toggle"
                name="areTermsChecked2"
                id="areTermsChecked2"
                component={FormikToggle}
                isRequired
              />
              <DateInput
                datePickerProps={{
                  onChange: date => {
                    setFieldValue('dateInput', date);
                  },
                  selected: values.dateInput,
                }}
                textInputProps={{
                  label: 'Date Input',
                  name: 'dateInput',
                  id: 'dateInput',
                  error: errors.dateInput,
                  isRequired: true,
                  onClear: () => {
                    setFieldValue('dateInput', null);
                  },
                  onBlur: handleBlur,
                }}
              />
              <Button
                type="submit"
                className="m-bottom-md"
                isLoading={isSubmitting}
              >
                Submit
              </Button>
              <pre>
                {JSON.stringify(
                  formatOutput(values, isSubmitting),
                  undefined,
                  2,
                )}
              </pre>
              <pre>{JSON.stringify(formatOutput(errors), undefined, 2)}</pre>
            </Box>
          </Form>
        )}
      </Formik>
    </Box>
  );
};
```
