Source: components/FormField.js

import React from 'react'
import { useField } from 'formik'
import PropTypes from 'prop-types'
import {
  TextField,
  FormControl,
  InputLabel,
  Select,
  FormHelperText,
  MenuItem,
  FormLabel,
  RadioGroup,
  Radio,
  FormControlLabel
} from '@material-ui/core'

/**
 * Formik text input field. Uses material ui TextField component.
 * Used inside Formik and Form that are imported form 'formik'.
 * @exports components/FormikTextField
 * @param {Object} props
 * @param {string} props.label - Field label
 * @param {string} props.name - Field name
 * @param {("text"|"password")} props.type - Text field types that should be used
 * @param {string} [props.placeholder] - Field placeholder value
 * @param {boolean} [props.disabled] - If true text field is disabled
 * @param {("filled"|"outlined"|"standard")} [props.variant=standard] - All available text field styles
 * @example
 * <Formik>
 *  <Form>
 *    <FormikTextField label="Email" name="email" type="text" />
 *  </Form>
 * </Formik>
 */
export const FormikTextField = ({ label, ...props }) => {
  const [field, meta] = useField(props)
  const errorText = (meta.touched && meta.error && !props.disabled) ? meta.error : ''

  return (
    <TextField
      {...field}
      {...props}
      label={label}
      error={!!errorText}
      helperText={errorText}
      style={{ minHeight: '5rem' }}
    />
  )
}

FormikTextField.propTypes = {
  label: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  type: PropTypes.oneOf(['text', 'password']).isRequired,
  placeholder: PropTypes.string,
  disabled: PropTypes.bool,
  variant: PropTypes.oneOf(['filled', 'outlined', 'standard'])
}

/**
 * Formik select input field. Uses material ui.
 * Used inside Formik and Form that are imported from 'formik'.
 * @exports components/FormikSelectField
 * @param {Object} props
 * @param {string} props.label - Field label
 * @param {string} props.name - Field name
 * @param {Object[]} props.options - All available fields shown in dropdown menu
 * @param {(string|number)} props.options.value - Option value (value that is sent to API)
 * @param {string} [props.options.label] - Option label (shown to user)
 * @example
 * <Formik>
 *  <Form>
 *    <FormikSelectField
 *      label="Role"
 *      name="role"
 *      options={[{ value: 'worker', label: 'Worker' }, ... ]}
 *    />
 *  </Form>
 * </Formik>
 */
export const FormikSelectField = ({ options, label, ...props }) => {
  const [field, meta] = useField(props)
  const errorText = meta.touched && meta.error ? meta.error : ''

  return (
    <FormControl
      style={{ minHeight: '5rem', minWidth: 120 }}
      error={!!errorText}>
      <InputLabel id={props.id || props.name}>{label}</InputLabel>
      <Select {...field} >
        {options.map(option => (
          <MenuItem key={option.value} value={option.value}>
            {option.label || option.value}
          </MenuItem>
        ))}
      </Select>
      <FormHelperText>{errorText}</FormHelperText>
    </FormControl>
  )
}

FormikSelectField.propTypes = {
  label: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  options: PropTypes.arrayOf(PropTypes.shape({
    value: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]).isRequired,
    label: PropTypes.string
  })).isRequired
}

/**
 * Formik radio input field. Uses material ui.
 * Used inside Formik and Form that are imported from 'formik'.
 * @exports components/FormikRadioField
 * @param {Object} props
 * @param {string} props.label - Field label
 * @param {string} props.name - Field name
 * @param {Object[]} props.options - All available fields shown in dropdown menu
 * @param {(string|number)} props.options.value - Option value (value that is sent to API)
 * @param {string} [props.options.label] - Option label (shown to user)
 * @example
 * <Formik>
 *  <Form>
 *    <FormikRadioField
        label="Role"
        name="role"
        options={[{ value: 'worker', label: 'Worker' }, ... ]}
      />
 *  </Form>
 * </Formik>
 */
export const FormikRadioField = ({ options, label, ...props }) => {
  const [field, meta] = useField(props)
  const errorText = meta.touched && meta.error ? meta.error : ''

  return (
    <FormControl
      style={{ minHeight: '6.5rem' }}
      error={!!errorText}>
      <FormLabel id={props.id || props.name}>{label}</FormLabel>
      <RadioGroup
        style={{ display: 'flex', flexFlow: 'row wrap', justifyContent: 'space-around' }}
        {...field}>
        {options.map(option => (
          <FormControlLabel
            key={option.value}
            value={option.value}
            control={<Radio color="primary" />}
            label={option.label}
            labelPlacement="bottom"
          />
        ))}
      </RadioGroup>
      <FormHelperText style={{ minHeight: '19px' }}>{errorText}</FormHelperText>
    </FormControl>
  )
}

FormikRadioField.propTypes = {
  label: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  options: PropTypes.arrayOf(PropTypes.shape({
    value: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]).isRequired,
    label: PropTypes.string
  })).isRequired
}