Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/ui / src / forms / form / FormState.tsx
Size: Mime:
/* eslint-disable brace-style */
import { Serializable } from '@skava/typings'
import { isArray } from 'exotic'
import { action, observable } from 'xmobx/mobx'
import { isValid, errorMessage, isValidPassword } from '../deps'
import { isAmexCard, isValidCreditCard } from '../deps/isValidCreditCard'
import { isValidExpiryDate } from '../deps/isValidExpiryDate'
import { InputState } from '../input/InputState'
import { InputType, InputStateType } from '../input/typings'
import { fromInputToSerializedKeyValue, toInputState } from './deps'
import { GetInputValueType, ObserverFormProps } from './typings'

class FormState {
  props: ObserverFormProps
  form: Element
  @observable
  hasAllValidInputs = false
  @observable
  inputsList: Array<InputState> = []

  static init(state: InputState) {
    return new FormState(state)
  }

  toSerialized: () => Serializable

  constructor(stateData: Object = {}) {
    /**
     * @todo when setting inputs list, copy...
     * ORIGINAL_INPUTS_LIST
     */
    this.inputsList = observable([])

    // no need to add another method, is an alias for specific usage
    this.toSerialized = this.toJSON.bind(this)
  }

  /**
   * @note added identifier check too
   * @param name name of the input for this form
   */
  get = (name: string): InputState | undefined => {
    /**
     * @todo convert this to a .find
     */
    let result
    const isSameName = (inputState: InputState) => {
      console.info('[forms] input state get - recursing: ', inputState)

      /**
       * @todo @james - this actually helps to loop through the elementsList inside groupElements,
       * will calling it recursively "result" variable has the element state, though it doesn't return anything.
       */
      if (inputState.type === 'groupElements') {
        // console.error('[form state] MISSING serialized')
        inputState.elementList.forEach(isSameName)
      } else if (inputState.name === name || inputState.identifier === name) {
        result = inputState
      }
    }

    this.inputsList.forEach(isSameName)

    return result
  }

  @action
  setInputsList(list: Array<InputState>) {
    this.inputsList = list
    return this
  }
  @action.bound
  setFormReference(dom: HTMLFormElement) {
    this.form = dom
  }
  @action.bound
  setProps(props: ObserverFormProps) {
    this.props = props
  }

  // ========= protected read =========

  toJSON(): Serializable {
    const formData = {}

    const assignInputValue = (
      input: InputStateType,
      index?: number
    ): Serializable => {
      // ignore labels
      if (input.type === 'label') {
        return undefined
      }

      // title says it all
      const { key, value } = fromInputToSerializedKeyValue(
        input,
        assignInputValue
      )

      // special place for radios
      if (input.type === 'radio') {
        if (value) {
          formData['selectedItem'] = key
        }
      } else {
        // otherwise, default
        formData[key] = value

        /**
         * @todo @fixme bad serialization - fixed in ui-component-library
         */
        if (formData['0'] === value) {
          delete formData['0']
        }
      }
    }

    // this is using `assignInputValue` as a longhand `.reduce`
    this.inputsList.forEach(assignInputValue)

    return formData
  }

  // ========= @todo - remove this duplication =========

  formValidation = () => {
    const isValidInput = (item: InputStateType, index?: number): boolean => {
      console.log('itemDetails', item)
      const { type, isHidden, isEnabled, validationType } = item
      if (isEnabled && type !== 'label' && validationType !== 'none') {
        if (type === 'groupElements' && isArray(item.elementList)) {
          return item.elementList.every(isValidInput)
        } else {
          return this.isValidElement(item)
        }
      } else {
        return true
      }
    }

    console.log('current input list', this.inputsList)
    const hasAllValidInputs = this.inputsList.every(isValidInput)
    console.log('hasAllValidInputs', hasAllValidInputs)
    return hasAllValidInputs
  }

  /**
   * @todo @gnanaprabhu why is this duplicated
   */
  isValidElement = item => {
    const { validationType, value } = item

    if (validationType === 'creditCard' || validationType === 'securityCode') {
      return this.validateCreditCard(item)
    }

    if (validationType === 'month' || validationType === 'year') {
      return this.validateExpiryDate(item)
    }

    if (validationType === 'confirmPassword') {
      if (this.validateConfirmPassword(item) === false) {
        return false
      }
    }
    if (validationType === 'newPassword') {
      if (this.validateNewPassword(item) === false) {
        return false
      }
    } else {
      const validationResult = isValid(value, validationType)
      if (!validationResult) {
        return false
      }
    }
    return true
  }

  creditCardNumberNullCheck = (props?: any) => {
    if (props && props.item && props.item.value && props.item.value.cardnumber) {
      return props.item.value.cardnumber
    }
    return ''
  }

  securityCodeNullCheck = (item?: any) => {
    if (item.validationType === 'securityCode') {
      return item.value
    }
    return ''
  }
  validateCardType = (isamexCard: boolean, securityCodeValue, validateCard: boolean) => {
    if (isamexCard && securityCodeValue.length !== 4) {
      return false
    } else if (!isamexCard && securityCodeValue.length !== 3) {
      return false
    }
    if (validateCard) {
      return true
    }
  }
  validateCreditCard = (item?: any) => {
    const validationType = item.validationType
    const creditCard = this.get('cardNumber')
    const creditCardValue = creditCard && creditCard.getValue()
    const securityCode = this.get('SecurityCode')
    const securityCodeValue = securityCode ? securityCode.getValue() : this.securityCodeNullCheck(item)
    if (creditCardValue) {
      if (securityCodeValue) {
        const isamexCard = isAmexCard(creditCardValue)
        this.validateCardType(isamexCard, securityCodeValue, false)
      } else if (validationType === 'securityCode') {
        return false
      }
      return isValidCreditCard(creditCardValue, securityCodeValue)
    } else {
      //@todo @component - Need to change the toAmexCard - to validate partially
      const isamexCard = this.props.item.value.cardtype === 'AMERICAN_EXPRESS'
      return this.validateCardType(isamexCard, securityCodeValue, true)
    }
    return false
  }

  validateExpiryDate = (item: any) => {
    const expiryMonth = this.get('expirationMonth') as InputState
    const expiryYear = this.get('expirationYear') as InputState
    const objValid = {
      validationType: item.validationType,
      expiryYear,
      expiryMonth,
      expiryMonthParsed: expiryMonth.getValue(),
      expiryYearParsed: expiryYear.getValue(),
    }
    return isValidExpiryDate(objValid)
  }

  @action.bound
  validateForSamePassword(item, name, shouldBeSamePassword) {
    // if (isObj(item) === false) {
    //   console.error('@ganesh - it is being passed a string')
    //   return false
    // }
    const { value } = item

    if (!isValidPassword(value)) {
      item.setIsValidInput(false)
      item.errorMessage = errorMessage('password')
      return false
    }

    const password = this.get(name) as InputState
    const passwordValue = password.getValue()
    item.setIsValidInput(true)
    let isValid = passwordValue === ''
    isValid = shouldBeSamePassword
      ? passwordValue !== item.value
      : passwordValue === item.value

    if (isValid) {
      item.setIsValidInput(false)
      item.errorMessage = errorMessage(item.errorMessageFor)
      return false
    }
    return true
  }

  @action.bound
  validateConfirmPassword(item) {
    return this.validateForSamePassword(item, 'password', true)
  }

  @action.bound
  validateNewPassword(item) {
    return this.validateForSamePassword(item, 'oldPassword', false)
  }
}

export { FormState }
export default FormState