diff --git a/package.json b/package.json index ca5067a..c5bb570 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@indec/form-builder", - "version": "1.6.0", + "version": "1.7.0", "description": "Form builder", "main": "index.js", "private": false, diff --git a/src/components/Checkbox/Checkbox.js b/src/components/Checkbox/Checkbox.js index 70647a8..8f090aa 100644 --- a/src/components/Checkbox/Checkbox.js +++ b/src/components/Checkbox/Checkbox.js @@ -37,7 +37,7 @@ function Checkbox({ {getSelectedOptions(options, field.value)} ) : ( - + {options.map((option, index) => ( subQuestions.map((subQuestion, index) => ({ +}) => subQuestions.map(subQuestion => ({ ...subQuestion, - name: `${sectionName}.${sectionIndex}.${questionName}.specifications.${index}.answer.value` + name: `${sectionName}.${sectionIndex}.${questionName}.answer.specifications.${subQuestion.name}.answer.value` })); const getComponent = (section, sectionIndex, questionIndex, readOnlyMode, warnings, values) => { @@ -26,11 +26,11 @@ const getComponent = (section, sectionIndex, questionIndex, readOnlyMode, warnin return null; } let QuestionComponent; - const questionName = `${section.name}.${sectionIndex}.${question.name}.answer.value`; + const questionName = `${section.name}.${sectionIndex}.${question.name}.answer`; const isRequired = question.validations.some(validation => validation.type === 'required'); const label = `${question.number} - ${question.label}`; const isMultiple = question.multiple; - const subQuestions = question.subQuestions.length > 0 + const subQuestions = question.subQuestions && question.subQuestions.length > 0 ? mapSubQuestions({ sectionName: section.name, sectionIndex, questionName: question.name, subQuestions: question.subQuestions }) diff --git a/src/components/QuestionBuilder/Wrapper.js b/src/components/QuestionBuilder/Wrapper.js index d2e5c77..330e720 100644 --- a/src/components/QuestionBuilder/Wrapper.js +++ b/src/components/QuestionBuilder/Wrapper.js @@ -40,13 +40,18 @@ function Wrapper({ /> ); } else { - Component = ; + Component = ; } if (subQuestions.length > 0 && options.length > 0) { Component = ( <> {Component} - + ); } @@ -57,7 +62,11 @@ Wrapper.propTypes = { isMultiple: PropTypes.bool.isRequired, name: PropTypes.string.isRequired, values: valuesPropTypes.isRequired, - subQuestions: PropTypes.arrayOf(subQuestionPropTypes).isRequired + subQuestions: PropTypes.arrayOf(subQuestionPropTypes) +}; + +Wrapper.defaultProps = { + subQuestions: [] }; export default Wrapper; diff --git a/src/components/Radio/Radio.js b/src/components/Radio/Radio.js index 3ad86e3..5fb3149 100644 --- a/src/components/Radio/Radio.js +++ b/src/components/Radio/Radio.js @@ -20,7 +20,7 @@ function Radio({ {readOnlyMode ? ( {getSelectedOptionLabel(options, field.value)} ) : ( - + {options.map(option => ( } label={option.label} /> ))} diff --git a/src/components/SubQuestions/SubQuestions.js b/src/components/SubQuestions/SubQuestions.js index 20dc855..6627c21 100644 --- a/src/components/SubQuestions/SubQuestions.js +++ b/src/components/SubQuestions/SubQuestions.js @@ -1,31 +1,30 @@ import PropTypes from 'prop-types'; import {FastField} from 'formik'; +import Box from '@mui/material/Box'; import castArray from '@/utils/castArray'; import subQuestionPropTypes from '@/utils/propTypes/subQuestion'; import valuesPropTypes from '@/utils/propTypes/values'; function SubQuestions({values, subQuestions, readOnlyMode, Component}) { - const specifications = values.specifications.filter( - specification => castArray(values.answer.value).includes(specification.optionId.toString()) + const selectedQuestions = subQuestions.filter( + subQuestion => castArray(values.answer.value).includes(subQuestion.optionId.toString()) ); return ( <> - {specifications.map(specification => { - const subQuestion = subQuestions.find( - currentSubQuestion => currentSubQuestion.optionId === specification.optionId - ); + {selectedQuestions.map(subQuestion => { const isRequired = subQuestion.validations.some(validation => validation.type === 'required'); return ( - + + + ); })} diff --git a/src/utils/buildQuestions.js b/src/utils/buildQuestions.js index 788567f..2080c56 100644 --- a/src/utils/buildQuestions.js +++ b/src/utils/buildQuestions.js @@ -21,6 +21,15 @@ const getAnswerValueType = question => { return answer; }; +const getSubQuestions = subQuestions => subQuestions.reduce((accumulator, currentValue, currentIndex) => { + accumulator[currentValue.name] = { + id: currentIndex + 1, + optionId: currentValue.optionId, + answer: {value: ''} + }; + return accumulator; +}, {}); + const buildQuestions = section => { const values = {[section.name]: {id: 1}}; if (section.interruption.interruptible) { @@ -32,14 +41,13 @@ const buildQuestions = section => { if (question.multiple) { values[section.name][question.name] = {id, answer: [{id: 1, value: getAnswerValueType(question)}]}; } - if (question.subQuestions.length > 0) { + if (question.subQuestions && question.subQuestions.length > 0) { values[section.name][question.name] = { ...values[section.name][question.name], - specifications: question.subQuestions.map( - (subQuestion, index) => ({ - id: index + 1, optionId: subQuestion.optionId, answer: {value: ''} - }) - ) + answer: { + ...values[section.name][question.name].answer, + specifications: getSubQuestions(question.subQuestions) + } }; } }); diff --git a/src/utils/buildYupSchema.js b/src/utils/buildYupSchema.js index 739c198..95cd492 100644 --- a/src/utils/buildYupSchema.js +++ b/src/utils/buildYupSchema.js @@ -2,6 +2,7 @@ import * as Yup from 'yup'; import dateTypes from '@/constants/dateTypes'; import questionTypes from '@/constants/questionTypes'; +import castArray from '@/utils/castArray'; const getValidatorType = (type, options, {isRequired, message, metadata}) => { let validator; @@ -69,10 +70,43 @@ const handleValidations = ({validator, validations, type, opts}) => { return newValidator; }; +const buildSubQuestionsValidations = (subQuestions, opts) => subQuestions.reduce((acc, currentValue) => { + let subQuestionValidator = Yup.string(); + const subQuestionValidations = currentValue.validations; + subQuestionValidator = handleValidations({ + validator: subQuestionValidator, + validations: subQuestionValidations, + type: currentValue.type, + opts + }); + acc[currentValue.name] = Yup.object({answer: Yup.object({value: subQuestionValidator})}); + return acc; +}, {}); + +const buildAnswerObj = ({values, subQuestions, validator, multiple, opts}) => { + const defaultSchema = multiple ? Yup.array().of( + Yup.object({ + id: Yup.number().required(), + value: validator + }) + ) : Yup.object({value: validator}); + if (subQuestions.length > 0) { + const selectedQuestions = buildSubQuestionsValidations(subQuestions.filter( + subQuestion => castArray(values.value).includes(subQuestion.optionId.toString()) + ), opts); + return defaultSchema.concat( + Yup.object({ + specifications: Yup.object(selectedQuestions) + }) + ); + } + return defaultSchema; +}; + export default function buildYupSchema(schema, config, opts = {}) { const schemaWithValidations = schema; const { - name, type, validations, options, metadata, multiple + name, type, validations, options, metadata, multiple, subQuestions = [] } = config; const requiredField = validations.find(validation => validation.type === 'required'); let validator = getValidatorType( @@ -90,9 +124,7 @@ export default function buildYupSchema(schema, config, opts = {}) { validator = handleValidations({validator, validations, type, opts}); schemaWithValidations[name] = Yup.object({ id: Yup.number().required(), - answer: Yup.object({ - value: multiple ? Yup.array().of(Yup.object({id: Yup.number(), value: validator})) : validator - }) + answer: Yup.lazy(values => buildAnswerObj({values, type, subQuestions, validator, multiple, opts})) }); return schemaWithValidations; } diff --git a/src/utils/propTypes/values.js b/src/utils/propTypes/values.js index 4be4a87..db07ff3 100644 --- a/src/utils/propTypes/values.js +++ b/src/utils/propTypes/values.js @@ -20,10 +20,9 @@ export default PropTypes.shape({ }) ) ]), - specifications: PropTypes.arrayOf( - PropTypes.shape({ - optionId: PropTypes.number, - name: PropTypes.string - }) - ) + specifications: PropTypes.shape({ + optionId: PropTypes.number, + name: PropTypes.string, + answer: PropTypes.shape({value: PropTypes.string}) + }) });