18F/e-QIP-prototype

View on GitHub
src/components/Section/Citizenship/UsPassport/index.jsx

Summary

Maintainability
A
2 hrs
Test Coverage
import React from 'react'
import { i18n } from 'config'
import {
  Field,
  Show,
  Text,
  Suggestions,
  Name,
  DateControl,
  Branch,
} from 'components/Form'
import { FOREIGN } from 'config/formSections/foreign'
import { CITIZENSHIP, CITIZENSHIP_US_PASSPORT } from 'config/formSections/citizenship'
import Subsection from 'components/Section/shared/Subsection'
import connectSubsection from 'components/Section/shared/SubsectionConnector'
import { extractDate } from '../../History/dateranges'


const sectionConfig = {
  key: CITIZENSHIP_US_PASSPORT.key,
  section: CITIZENSHIP.name,
  store: FOREIGN.store,
  subsection: CITIZENSHIP_US_PASSPORT.name,
  storeKey: CITIZENSHIP_US_PASSPORT.storeKey,
}

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

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

    this.section = section
    this.subsection = subsection
    // TODO: Temporary solution to limit risk of changing Redux, backend, and XML changes
    this.store = store
    this.storeKey = storeKey

    this.number = null
    this.state = {
      showSuggestionsModal: this.showSuggestions(),
    }
  }

  update = (queue, fn) => {
    this.props.onUpdate(this.storeKey, {
      Name: this.props.Name,
      Number: this.props.Number,
      Card: this.props.Card,
      Issued: this.props.Issued,
      Expiration: this.props.Expiration,
      Comments: this.props.Comments,
      HasPassports: this.props.HasPassports,
      suggestedNames: this.props.suggestedNames,
      ...queue,
    })

    if (fn) {
      fn()
    }
  }

  /**
   * Handle the change event.
   */
  updateCard = (values) => {
    this.update(
      {
        Card: values,
      },
      () => {
        // This allows us to force a blur/validation using
        // the new regular expression
        this.number.refs.text.refs.input.focus()
        this.number.refs.text.refs.input.blur()
      },
    )
  }

  /**
   * Handle when the yes/no option has been changed
   */
  updateBranch = (values) => {
    this.update({
      HasPassports: values,
      Name: values.value === 'Yes' ? this.props.Name : {},
      Number: values.value === 'Yes' ? this.props.Number : '',
      Issued: values.value === 'Yes' ? this.props.Issued : {},
      Expiration: values.value === 'Yes' ? this.props.Expiration : {},
    })
  }

  updateName = (values) => {
    this.update({
      Name: values,
    })
  }

  updateNumber = (values) => {
    this.update({
      Number: values,
    })
  }

  updateIssued = (values) => {
    this.update({
      Issued: values,
    })
  }

  updateExpiration = (values) => {
    this.update({
      Expiration: values,
    })
  }

  renderSuggestion = (suggestion = {}) => {
    const name = `${suggestion.first || ''} ${suggestion.middle || ''} ${suggestion.last || ''} ${suggestion.suffix || ''}`.trim()
    return <span>{name}</span>
  }

  onSuggestion = (suggestion) => {
    this.update({
      Name: suggestion,
    })

    this.setState({ showSuggestionsModal: false })
  }

  onDismiss = () => {
    this.setState({ showSuggestionsModal: false })
  }

  showSuggestions = () => {
    // If we have a name already, don't show
    if (this.props.Name && this.props.Name.first && this.props.Name.last) {
      return false
    }

    // If we have suggestions, show them
    return !!this.props.suggestedNames.length
  }

  passportBeforeCutoff = () => {
    if (this.props.Issued) {
      const cutoffDate = new Date('1/1/1990 00:00')
      const issueDate = extractDate(this.props.Issued)

      if (issueDate && issueDate < cutoffDate) {
        return true
      }
    }

    return false
  }

  render() {
    const { showSuggestionsModal } = this.state
    const numberLength = this.passportBeforeCutoff() ? '255' : '9'
    const numberRegEx = this.passportBeforeCutoff()
      ? '^[a-zA-Z0-9]*$'
      : this.props.reBook

    return (
      <div
        className="section-content passport"
        data-section={CITIZENSHIP.key}
        data-subsection={CITIZENSHIP_US_PASSPORT.key}
      >
        <h1 className="section-header">{i18n.t('citizenship.usPassport.title')}</h1>
        <h3>{i18n.t('citizenship.usPassport.info.text')}</h3>
        <Branch
          name="has_passport"
          label={i18n.t('citizenship.usPassport.question.title')}
          labelSize="h4"
          {...this.props.HasPassports}
          warning={true}
          onUpdate={this.updateBranch}
          onError={this.handleError}
          required={this.props.required}
          scrollIntoView={this.props.scrollIntoView}
        />
        <Show when={this.props.HasPassports.value === 'Yes'}>
          <div>
            <Field
              title={i18n.t('citizenship.usPassport.name')}
              titleSize="h4"
              optional={true}
              className="no-margin-bottom"
            />
            <Suggestions
              show={showSuggestionsModal}
              suggestions={this.props.suggestedNames}
              renderSuggestion={this.renderSuggestion}
              withSuggestions={true}
              suggestionTitle={i18n.t('suggestions.name.title')}
              suggestionParagraph={i18n.m('suggestions.name.para')}
              suggestionLabel={i18n.t('suggestions.name.label')}
              suggestionDismissLabel={i18n.t('suggestions.name.dismiss')}
              suggestionUseLabel={i18n.t('suggestions.name.use')}
              onSuggestion={this.onSuggestion}
              onDismiss={this.onDismiss}
            />
            <Field
              optional={true}
              filterErrors={Name.requiredErrorsOnly}
              scrollIntoView={this.props.scrollIntoView}
            >
              <Name
                name="name"
                {...this.props.Name}
                onUpdate={this.updateName}
                onError={this.handleError}
                required={this.props.required}
                scrollIntoView={this.props.scrollIntoView}
              />
            </Field>

            <Field
              title={i18n.t('citizenship.usPassport.issued')}
              help={i18n.t('citizenship.usPassport.help.issued.message')}
              adjustFor="labels"
              shrink={true}
              scrollIntoView={this.props.scrollIntoView}
            >
              <DateControl
                name="issued"
                className="passport-issued"
                {...this.props.Issued}
                minDateEqualTo={true}
                onUpdate={this.updateIssued}
                onError={this.handleError}
                required={this.props.required}
              />
            </Field>

            <Field
              title={i18n.t('citizenship.usPassport.expiration')}
              help={i18n.t('citizenship.usPassport.help.expiration.message')}
              adjustFor="labels"
              shrink={true}
              scrollIntoView={this.props.scrollIntoView}
            >
              <DateControl
                name="expiration"
                className="passport-expiration"
                {...this.props.Expiration}
                minDate={this.props.Issued}
                minDateEqualTo={true}
                noMaxDate={true}
                prefix="passportInformation"
                onUpdate={this.updateExpiration}
                onError={this.handleError}
                required={this.props.required}
              />
            </Field>

            <Field
              title={i18n.t('citizenship.usPassport.label.bookNumber')}
              help={i18n.t('citizenship.usPassport.help.number.message')}
              errorPrefix="passport"
              adjustFor="buttons"
              shrink={true}
              scrollIntoView={this.props.scrollIntoView}
            >
              <div>
                <Text
                  name="number"
                  {...this.props.Number}
                  pattern={numberRegEx}
                  maxlength={numberLength}
                  className="number passport-number"
                  ref={(el) => { this.number = el }}
                  prefix="passport"
                  onUpdate={this.updateNumber}
                  onError={this.handleError}
                  required={this.props.required}
                />
              </div>
            </Field>
          </div>
        </Show>
      </div>
    )
  }
}

// Regular expressions were based on the information provided by
// U.S. Citizenship and Immigration Services (USCIS) at:
//
// https://e-verify-uscis.gov/esp/help/EvHelpPassportandPassportCardNbr.htm
UsPassport.defaultProps = {
  Name: {},
  Number: {},
  Card: { value: 'Book' },
  Issued: {},
  Expiration: {},
  Comments: {},
  HasPassports: {},
  suggestedNames: [],
  reBook: '^[a-zA-Z0-9]{9}$',
  onUpdate: () => {},
  onError: (value, arr) => arr,
  section: 'foreign',
  subsection: 'passport',
  dispatch: () => {},
  errors: [],
}

export default connectSubsection(UsPassport, sectionConfig)