18F/e-QIP-prototype

View on GitHub
src/selectors/validation.js

Summary

Maintainability
A
25 mins
Test Coverage
/* eslint import/prefer-default-export: 0 */
import { createSelector } from 'reselect'

import { REVIEW_AND_SUBMIT_SUBMIT, REVIEW_AND_SUBMIT_PRINT } from 'constants/sections'

import { formIsLocked } from 'validators'
import { sectionIsValid } from 'helpers/validation'
import { reduceSubsections } from 'helpers/navigation'

import { nestedFormSectionsSelector } from 'selectors/navigation'

/**
 * Recursive function that loops through sections and their subsections, checks
 * section validity from Redux, and adds the isValid boolean to the section
 * object.
 */
const getFormSectionStatuses = (sections = [], store = '', state = {}) => {
  const { form } = state
  const newSections = sections
    .filter(s => s.subsections || s.storeKey)
    .map((s) => {
      if (s.subsections) {
        const parentStore = store || s.store
        return {
          ...s,
          subsections: getFormSectionStatuses(s.subsections, parentStore, state),
        }
      }

      const { key } = s
      const isValid = !!(form[key] && form[key].complete === true)

      return {
        ...s,
        isValid,
      }
    })

  return newSections
}

const getFormStatus = (state) => {
  const formSections = nestedFormSectionsSelector(state, true)
  const formSectionStatuses = getFormSectionStatuses(formSections, '', state)
  return formSectionStatuses
}

export const formStatusSelector = createSelector(
  getFormStatus,
  formSections => ({
    formSections,
    formIsValid: sectionIsValid(formSections),
  })
)

const formHasErrors = (state) => {
  const formStatus = formStatusSelector(state)
  const { formIsValid } = formStatus
  return !formIsValid
}

const getSectionLocked = (state, props) => {
  const { section } = props
  const formLocked = formIsLocked(state.application)

  // Special cases
  switch (section.key) {
    case REVIEW_AND_SUBMIT_SUBMIT: {
      return formLocked || formHasErrors(state)
    }

    case REVIEW_AND_SUBMIT_PRINT:
      return !formLocked

    default:
      return formLocked
  }
}

const getSectionErrors = (state, props) => {
  const { application } = state
  const { Errors } = application

  const { topSection, section } = props

  const sectionErrors = topSection
    ? Errors[topSection]
    : Errors[section.name]

  if (!sectionErrors) { return [] }

  if (!section.subsections) {
    return sectionErrors.filter(e => e.subsection === section.name)
  }

  const flatSections = reduceSubsections(section.subsections)

  return sectionErrors.filter(s => (
    flatSections.find(i => i.name === s.subsection)
  ))
}

const getSectionCompleted = (state, props) => {
  const { form } = state
  const { section } = props

  if (section.subsections) {
    // Check complete status of each subsection
    const flatSections = reduceSubsections(section.subsections)
    const sectionsWithData = flatSections.filter(s => !!s.storeKey)
    return sectionsWithData.length > 0 && sectionsWithData.every((s) => {
      const sectionData = form && form[s.key]
      return sectionData && sectionData.complete === true
    })
  }

  const sectionData = form && form[section.key]
  if (!sectionData) return false
  return sectionData.complete === true
}

export const sectionIsLockedSelector = createSelector(
  getSectionLocked,
  locked => ({ locked })
)

export const sectionHasErrorsSelector = createSelector(
  getSectionErrors,
  (errors = []) => ({
    errors: errors.some(e => e.valid === false),
  })
)

export const sectionIsValidSelector = createSelector(
  getSectionCompleted,
  completed => ({ completed })
)