18F/e-QIP-prototype

View on GitHub
src/components/Section/Identification/ApplicantSSN/ApplicantSSN.jsx

Summary

Maintainability
A
0 mins
Test Coverage

import React from 'react'

import { i18n } from 'config'
import { validSSN } from 'validators/helpers'

import { Field, SSN, Show } from 'components/Form'
import { IDENTIFICATION, IDENTIFICATION_SSN } from 'config/formSections/identification'

import connectSubsection from 'components/Section/shared/SubsectionConnector'
import Subsection from 'components/Section/shared/Subsection'


const sectionConfig = {
  key: IDENTIFICATION_SSN.key,
  section: IDENTIFICATION.name,
  store: IDENTIFICATION.store,
  subsection: IDENTIFICATION_SSN.name,
  storeKey: IDENTIFICATION_SSN.storeKey,
}

export class ApplicantSSN extends Subsection {
  constructor(props) {
    super(props)

    const {
      section, subsection, store, storeKey,
    } = sectionConfig

    this.section = section
    this.subsection = subsection
    this.store = store
    this.storeKey = storeKey

    this.state = {
      uid: `${subsection.name}-${super.guid()}`,
      verification: {},
      error: false,
      verified: props.verified || false,
    }
  }

  update = (queue) => {
    this.props.onUpdate(this.storeKey, {
      ssn: this.props.ssn,
      verified: this.props.verified,
      ...queue,
    })
  }

  updateSSN = (values) => {
    this.setState({
      verified: false,
    })

    this.update({
      ssn: values,
      verified: false,
      error: false,
    })
  }

  updateVerification = (values) => {
    const verified = (
      this.props.ssn.first === values.first
      && this.props.ssn.middle === values.middle
      && this.props.ssn.last === values.last
      && validSSN(values)
    )

    this.setState(prevState => ({
      verified,
      verification: verified ? {} : values,
      error: verified ? false : prevState.error,
    }), () => {
      // Update the properties so they can be reflected
      // within this component.
      this.update({ verified })

      // If everything checks out there are no errors and
      // we are going to forcefully flush them.
      if (verified) {
        this.handleError(values, [])
        this.verificationError(values, [])
      }
    })
  }

  verificationError = (value, arr) => {
    // If the verification SSN is valid then we run our validation
    // function(s). However, if it is not then we set the `valid` property
    // to a neutral state. This allows the error notifications to be properly
    // toggled within the various subscribing components.
    const { verification } = this.state
    const local = (
      this.props.onError(
        value,
        this.constructor.errors.map(err => (
          {
            code: err.code,
            valid: validSSN(verification)
              ? err.func(verification, this.props)
              : null,
            uid: this.state.uid,
          }
        ))
      ) || []
    )

    // Set the error state value so we can apply a CSS class to
    // the entire SSN.
    this.setState({ error: local.some(x => x.valid === false) })
    return this.handleError(value, arr.concat(local))
  }

  render() {
    const klass = `section-content applicant-ssn ${this.props.className
      || ''}`.trim()
    const klassVerify = `applicant-ssn-verification ${
      this.state.error ? 'usa-input-error' : ''
    }`.trim()
    const verify = (
      validSSN(this.props.ssn)
      && !this.props.verified
      && !this.props.ssn.notApplicable
    )

    return (
      <div
        className={klass}
        data-section={IDENTIFICATION.key}
        data-subsection={IDENTIFICATION_SSN.key}
      >
        <h1 className="section-header">{i18n.t('identification.destination.ssn')}</h1>
        <Field
          title={i18n.t('identification.ssn.title')}
          titleSize="h4"
          help="identification.ssn.help"
          scrollIntoView={this.props.scrollIntoView}
        >
          <SSN
            name="ssn"
            {...this.props.ssn}
            className="applicant-ssn-initial"
            onUpdate={this.updateSSN}
            onError={this.handleError}
            required={!this.props.ssn.notApplicable && this.props.required}
          />
        </Field>

        <Show when={verify}>
          <Field
            title={i18n.t('identification.ssn.heading.verify')}
            scrollIntoView={this.props.scrollIntoView}
            titleSize="h4"
          >
            <SSN
              name="verification"
              {...this.state.verification}
              hideNotApplicable={true}
              className={klassVerify}
              onUpdate={this.updateVerification}
              onError={this.verificationError}
              required={verify && this.props.required}
            />
          </Field>
        </Show>
        <Show when={this.props.verified}>
          <Field
            title={i18n.t('identification.ssn.heading.verified')}
            optional={true}
            scrollIntoView={this.props.scrollIntoView}
            titleSize="h4"
          />
        </Show>
      </div>
    )
  }
}

ApplicantSSN.defaultProps = {
  ssn: {},
  verified: false,
  onUpdate: () => {},
  onError: (value, arr) => arr,
  dispatch: () => {},
  required: false,
}

ApplicantSSN.errors = [
  {
    code: 'ssn.mismatch',
    func: (value, props) => {
      if (!value) {
        return null
      }

      return (
        props.ssn.first === value.first
        && props.ssn.middle === value.middle
        && props.ssn.last === value.last
      )
    },
  },
]

export default connectSubsection(ApplicantSSN, sectionConfig)