all files / src/ generateFields.js

98.92% Statements 92/93
96.63% Branches 86/89
100% Functions 15/15
96.67% Lines 29/30
28 statements, 3 functions, 23 branches Ignored     
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72     67×   15× 15× 15×     15× 15× 15× 12×     12× 12×         15× 15× 10×         52×   18× 18× 11×             34× 34× 34× 17×           29×                                  
const sameContents = (arrayA, arrayB) => arrayA === arrayB || (arrayA.length === arrayB.length && arrayA.every((valueA, index) => valueA === arrayB[ index ]))
 
const generate = (field, tree = {}, values = {}, path = '', createField, createArray) => {
  const [ all, key, rest ] = /([^.]+)\.?(.+)?/.exec(field) // eslint-disable-line no-unused-vars
  if (/.+\[\]/.test(key)) {
    // is array key
    const noBrackets = key.substring(0, key.length - 2)
    const valuesArray = values[ noBrackets ]
    Iif (valuesArray && !Array.isArray(valuesArray)) {
      throw new Error(`Expected array value for ${path}${field}, but found ${typeof array}: ${JSON.stringify(valuesArray)}`)
    }
    const treeArray = tree[ noBrackets ] || []
    const results = []
    if (valuesArray) {
      if (rest) {
        valuesArray.forEach((value, index) => {
          results[ index ] = generate(rest, treeArray[ index ], value, `${path}${noBrackets}[${index}].`, createField, createArray)
        })
      } else {
        valuesArray.forEach((value, index) => {
          const fieldKey = `${path}${noBrackets}[${index}]`
          results[ index ] = !treeArray[ index ] || treeArray[ index ].key !== fieldKey ?
            createField(fieldKey) : treeArray[ index ]
        })
      }
    }
    const arrayKey = `${path}${noBrackets}`
    if (!tree[ noBrackets ] || !sameContents(treeArray, results) || treeArray.key !== arrayKey) {
      return {
        ...tree,
        [noBrackets]: createArray(arrayKey, results)
      }
    }
  } else if (rest) {
    // need to recurse
    const result = generate(rest, tree[ key ], values[ key ], `${path}${key}.`, createField, createArray)
    if (!tree[ key ] || tree[ key ] !== result) {
      return {
        ...tree,
        [key]: result
      }
    }
  } else {
    // value key
    const fieldKey = `${path}${key}`
    const existing = tree[ key ]
    if (!existing || existing.key !== fieldKey) {
      return {
        ...tree,
        [key]: createField(fieldKey)
      }
    }
  }
  return tree
}
 
/**
 * Generates a tree of field objects
 *
 * @param fields The fields array passed to redux-form
 * @param tree The existing field tree
 * @param values The form values from the Redux store
 * @param createField A callback to create a field object
 * @param createArray A callback to create a field array
 */
const generateFields = (fields = [], tree = {}, values, createField, createArray) =>
  fields.reduce((accumulator, field) =>
      generate(field, accumulator, values, undefined, createField, createArray),
    tree)
 
export default generateFields