text: // @flow
  import * as React from 'react'
  import { useDispatch, useSelector } from 'react-redux'
  import {
    DropdownField,
    FormGroup,
    PipetteSelect,
    OutlineButton,
    type Mount,
  } from '@opentrons/components'
  import { getLabwareDefURI, getLabwareDisplayName } from '@opentrons/shared-data'
  import isEmpty from 'lodash/isEmpty'
  import reduce from 'lodash/reduce'
  import { i18n } from '../../../localization'
  import { createCustomTiprackDef } from '../../../labware-defs/actions'
  import { getLabwareDefsByURI } from '../../../labware-defs/selectors'
  import { PipetteDiagram } from './PipetteDiagram'
  
  import styles from './FilePipettesModal.css'
  import formStyles from '../../forms/forms.css'
  
  import type { FormPipettesByMount } from '../../../step-forms'
  
  export type Props = {|
    initialTabIndex?: number,
    values: FormPipettesByMount,
    // TODO 2020-3-20 use formik typing here after we update the def in flow-typed
    errors:
      | null
      | string
      | {
          left?: {
            tiprackDefURI: string,
          },
          right?: {
            tiprackDefURI: string,
          },
        },
    touched:
      | null
      | boolean
      | {
          left?: {
            tiprackDefURI: boolean,
          },
          right?: {
            tiprackDefURI: boolean,
          },
        },
    onFieldChange: (event: SyntheticInputEvent<HTMLSelectElement>) => mixed,
    onSetFieldValue: (field: string, value: string | null) => void,
    onSetFieldTouched: (field: string, touched: boolean) => void,
    onBlur: (event: SyntheticFocusEvent<HTMLSelectElement>) => mixed,
  |}
  
  // TODO(mc, 2019-10-14): delete this typedef when gen2 ff is removed
  type PipetteSelectProps = {| mount: Mount, tabIndex: number |}
  
  export function PipetteFields(props: Props): React.Node {
    const {
      values,
      onFieldChange,
      onSetFieldValue,
      onSetFieldTouched,
      onBlur,
      errors,
      touched,
    } = props
  
    const dispatch = useDispatch()
  
    const allLabware = useSelector(getLabwareDefsByURI)
  
    const tiprackOptions = reduce(
      allLabware,
      (acc, def: $Values<typeof allLabware>) => {
        if (def.metadata.displayCategory !== 'tipRack') return acc
        return [
          ...acc,
          {
            name: getLabwareDisplayName(def),
            value: getLabwareDefURI(def),
          },
        ]
      },
      []
    )
  
    const initialTabIndex = props.initialTabIndex || 1
  
    const renderPipetteSelect = (props: PipetteSelectProps) => {
      const { tabIndex, mount } = props
      const pipetteName = values[mount].pipetteName
  
      return (
        <PipetteSelect
          enableNoneOption
          tabIndex={tabIndex}
          pipetteName={pipetteName != null ? pipetteName : null}
          onPipetteChange={pipetteName => {
            const nameAccessor = `pipettesByMount.${mount}.pipetteName`
            const value = pipetteName
            const targetToClear = `pipettesByMount.${mount}.tiprackDefURI`
            // this select does not return an event so we have to manually set the field val
            onSetFieldValue(nameAccessor, value)
            onSetFieldValue(targetToClear, null)
            onSetFieldTouched(targetToClear, false)
          }}
          id={`PipetteSelect_${mount}`}
        />
      )
    }
  
    return (
      <React.Fragment>
        <div className={styles.mount_fields_row}>
          <div className={styles.mount_column}>
            <FormGroup
              key="leftPipetteModel"
              label={i18n.t('modal.pipette_fields.left_pipette')}
              className={formStyles.stacked_row}
            >
              {renderPipetteSelect({
                mount: 'left',
                tabIndex: initialTabIndex + 1,
              })}
            </FormGroup>
            <FormGroup
              disabled={isEmpty(values.left.pipetteName)}
              key={'leftTiprackModel'}
              label={i18n.t('modal.pipette_fields.left_tiprack')}
              className={formStyles.stacked_row}
            >
              <DropdownField
                error={
                  // TODO JF 2020-3-19 allow dropdowns to take error
                  // components from formik so we avoid manually doing this
                  touched &&
                  typeof touched !== 'boolean' &&
                  touched.left &&
                  touched.left.tiprackDefURI &&
                  errors !== null &&
                  typeof errors !== 'string' &&
                  errors.left
                    ? errors.left.tiprackDefURI
                    : null
                }
                tabIndex={initialTabIndex + 2}
                disabled={isEmpty(values.left.pipetteName)}
                options={tiprackOptions}
                value={values.left.tiprackDefURI}
                name="pipettesByMount.left.tiprackDefURI"
                onChange={onFieldChange}
                onBlur={onBlur}
              />
            </FormGroup>
          </div>
          <PipetteDiagram
            leftPipette={values.left.pipetteName}
            rightPipette={values.right.pipetteName}
          />
          <div className={styles.mount_column}>
            <FormGroup
              key="rightPipetteModel"
              label={i18n.t('modal.pipette_fields.right_pipette')}
              className={formStyles.stacked_row}
            >
              {renderPipetteSelect({
                mount: 'right',
                tabIndex: initialTabIndex + 3,
              })}
            </FormGroup>
            <FormGroup
              disabled={isEmpty(values.right.pipetteName)}
              key={'rightTiprackModel'}
              label={i18n.t('modal.pipette_fields.right_tiprack')}
              className={formStyles.stacked_row}
            >
              <DropdownField
                error={
                  // TODO JF 2020-3-19 allow dropdowns to take error
                  // components from formik so we avoid manually doing this
                  touched &&
                  typeof touched !== 'boolean' &&
                  touched.right &&
                  touched.right.tiprackDefURI &&
                  errors !== null &&
                  typeof errors !== 'string' &&
                  errors.right
                    ? errors.right.tiprackDefURI
                    : null
                }
                tabIndex={initialTabIndex + 4}
                disabled={isEmpty(values.right.pipetteName)}
                options={tiprackOptions}
                value={values.right.tiprackDefURI}
                name="pipettesByMount.right.tiprackDefURI"
                onChange={onFieldChange}
                onBlur={onBlur}
              />
            </FormGroup>
          </div>
        </div>
        <div>
          <OutlineButton Component="label" className={styles.upload_button}>
            {i18n.t('button.upload_custom_tip_rack')}
            <input
              type="file"
              onChange={e => dispatch(createCustomTiprackDef(e))}
            />
          </OutlineButton>
        </div>
      </React.Fragment>
    )
  }
  