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    
ui-component-library / src / forms / form / FormState.tsx
Size: Mime:
/* eslint-disable brace-style */
import { Serializable } from 'exotic'
import { isObj, isFunction, isFalse } from 'exotic'
import { curry } from 'chain-able-boost'
import {
  extendObservable,
  action,
  observable,
  computed,
  decorate,
} from 'xmobx/mobx'
import { isValid, errorMessage } from '../deps'
import { isAmexCard, isValidCreditCard } from '../deps/isValidCreditCard'
import { isValidYear } from '../deps/isValidExpiryDate'
import { InputState } from '../input/InputState'
import {
  toSerialized,
  fromInputToSpread,
  fromInputToSerializable,
  fromInputToSerializedKeyValue,
  toInputState,
} from './deps'
import { GetInputValueType, ObserverFormProps } from './typings'

// const curriedDecorate = curry('_', decorate)
function curryDecorate(types: Object) {
  return function(Klass: any) {
    decorate(Klass, types)
    return Klass
  }
}

const decorations = {
  setInputsList: action.bound,
  setFormReference: action.bound,
  setProps: action.bound,
}

// @todo + finish at the end removing validation duplication
// @curryDecorate(decorations)

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

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

  /**
   * @example [].map(FormState.toInputState)
   */
  static toInputState = toInputState

  constructor(stateData: Object = {}) {
    // super(stateData)
    // extendObservable(this, {
    //   // I think this has an issue - name
    //   // name: '',
    //   hasAllValidInputs: false,
    //   inputsList: [],
    // })

    /**
     * @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 {String} name name of the input for this form
   * @return {InputState}
   */
  get = (name): InputState => {
    /**
     * @todo convert this to a .find
     */
    let result
    const isSameName = (inputState: InputState) => {
      console.info('[forms] input state get - recursing: ', inputState)

      if (inputState.type === 'groupElements') {
        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 =========

  /**
   * @type {Computed}
   */
  toJSON(): Serializable {
    const formData = {}

    const getInputValue: GetInputValueType = input => {
      // ignore labels
      if (input.type === 'label') {
        return
      }

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

      // 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 `getInputValue` as a longhand `.reduce`
    this.inputsList.forEach(getInputValue)

    return formData
  }

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

  formValidation = () => {
    const isValidInput = item => {
      console.log('itemDetails', item)
      const { type, isHidden, isEnabled, validationType } = item
      if (isEnabled && type !== 'label' && validationType !== 'none') {
        if (type === 'groupElements') {
          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
      }
    } else {
      const validationResult = isValid(value, validationType)
      if (!validationResult) {
        return false
      }
    }
    return true
  }

  validateCreditCard = item => {
    const creditCard = this.get('cardNumber')
    const securityCode = this.get('SecurityCode')
    if (creditCard && creditCard.getValue()) {
      if (securityCode && securityCode.getValue()) {
        const isamexCard = isAmexCard(creditCard.getValue())
        if (isamexCard && securityCode.getValue().length !== 4) {
          return false
        } else if (!isamexCard && securityCode.getValue().length === 4) {
          return false
        }
      }
      return isValidCreditCard(creditCard.getValue())
    }
    return false
  }

  validateExpiryDate = item => {
    const expiryMonth = this.get('expirationyear')
    const expiryYear = this.get('expirationyear')
    if (expiryMonth.getValue() && expiryYear.getValue()) {
      if (isValidYear(expiryYear.getValue(), expiryMonth.getValue())) {
        return true
      } else {
        return false
      }
    }
    return false
  }

  @action.bound
  validateConfirmPassword(item) {
    // if (isObj(item) === false) {
    //   console.error('@ganesh - it is being passed a string')
    //   return false
    // }

    const password = this.get('password')
    const passwordValue = password.getValue()
    item.isValidInput = true
    if (passwordValue === '' || passwordValue !== item.value) {
      item.isValidInput = false
      item.errorMessage = errorMessage(item.errorMessageFor)
      return false
    }
    return true
  }

  // @todo remove userContainer logic and make it generic

  // doesMatchesUserName = item => {
  //   const email = this.get('email')
  //   const userData = userContainer.username || userContainer.email
  //   const emailValue = userData ? userData : (email ? email.getValue() : '')
  //   if (item.value !== '' && emailValue === item.value) {
  //     item.isValidInput = false
  //     item.errorMessage = errorMessage('passwordSameAsEmail')
  //     return true
  //   }
  //   return false
  // }
}

export { FormState }
export default FormState