From 4ac8094c824ae921b5dd1c8b0942f0468cf4390f Mon Sep 17 00:00:00 2001 From: Maximiliano forlenza Date: Mon, 3 Jul 2023 10:19:03 -0300 Subject: [PATCH] refactor: change readonly mode view --- package.json | 2 +- src/components/Checkbox/Checkbox.js | 45 ++++----- src/components/Checkbox/Checkbox.stories.js | 8 +- src/components/Checkbox/Checkbox.test.js | 21 ++-- src/components/FormBuilder/FormBuilder.js | 35 ++++--- .../FormBuilder/FormBuilder.stories.js | 33 +++++++ src/components/FormBuilder/Modals/Modals.js | 9 +- .../FormBuilder/Modals/Modals.stories.js | 37 +------ .../FormBuilder/Modals/Modals.test.js | 6 +- .../SectionHeader/SectionHeader.js | 40 ++++---- .../SectionHeader/SectionHeader.stories.js | 1 + .../SectionHeader/SectionHeader.test.js | 39 ++++---- .../NavigationButtons/NavigationButtons.js | 53 +++++----- .../NavigationButtons.test.js | 18 ++++ src/components/QuestionBuilder/Wrapper.js | 10 +- src/components/Radio/Radio.js | 26 ++--- src/components/Radio/Radio.stories.js | 7 +- src/components/Radio/Radio.test.js | 19 ++-- src/components/RadioTable/RadioTable.js | 73 +++++++------- .../RadioTable/RadioTable.stories.js | 12 ++- src/components/RadioTable/RadioTable.test.js | 30 +++++- .../RadioTable/ReadOnly/ReadOnly.js | 43 -------- .../RadioTable/ReadOnly/ReadOnly.test.js | 98 ------------------- src/components/RadioTable/ReadOnly/index.js | 3 - src/components/Select/Select.js | 11 +-- src/components/Select/Select.stories.js | 7 +- src/components/SubQuestions/SubQuestions.js | 4 +- src/components/TextField/TextField.js | 61 ++++++------ src/components/TextField/TextField.stories.js | 7 +- src/components/TextField/TextField.test.js | 25 +++-- src/constants/modals.js | 1 - src/utils/buildYupSchema.js | 2 +- src/utils/propTypes/option.js | 2 +- 33 files changed, 355 insertions(+), 433 deletions(-) delete mode 100644 src/components/RadioTable/ReadOnly/ReadOnly.js delete mode 100644 src/components/RadioTable/ReadOnly/ReadOnly.test.js delete mode 100644 src/components/RadioTable/ReadOnly/index.js diff --git a/package.json b/package.json index 5d1c97e..d9083af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@indec/form-builder", - "version": "1.8.0", + "version": "1.9.0", "description": "Form builder", "main": "index.js", "private": false, diff --git a/src/components/Checkbox/Checkbox.js b/src/components/Checkbox/Checkbox.js index 82e4615..18629de 100644 --- a/src/components/Checkbox/Checkbox.js +++ b/src/components/Checkbox/Checkbox.js @@ -3,20 +3,13 @@ import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; import MuiCheckbox from '@mui/material/Checkbox'; import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; import FieldMessage from '@/components/FieldMessage'; import InputLabel from '@/components/InputLabel'; -import defaultMessages from '@/constants/defaultMessages'; import formikField from '@/utils/propTypes/formikField'; import formikForm from '@/utils/propTypes/formikForm'; import optionPropTypes from '@/utils/propTypes/option'; -const getSelectedOptions = (options, selectedValues) => selectedValues.reduce((accumulator, currentValue) => { - const option = options.find(currentOption => currentOption.value === currentValue); - return option ? [...accumulator, option.label] : accumulator; -}, []).join(', ') || defaultMessages.UNANSWERED; - const handleChecked = (e, selectedValue, {name, value}, setFieldValue) => { const isChecked = e.target.checked; const values = isChecked @@ -31,27 +24,23 @@ function Checkbox({ return ( - {readOnlyMode ? ( - - {getSelectedOptions(options, field.value)} - - ) : ( - - {options.map((option, index) => ( - handleChecked(e, option.value, field, form.setFieldValue)} - /> - )} - label={option.label} - /> - ))} - - )} + + {options.map((option, index) => ( + handleChecked(e, option.value, field, form.setFieldValue)} + /> + )} + label={option.label} + disabled={readOnlyMode} + /> + ))} + ); diff --git a/src/components/Checkbox/Checkbox.stories.js b/src/components/Checkbox/Checkbox.stories.js index 115eada..1cbf2d8 100644 --- a/src/components/Checkbox/Checkbox.stories.js +++ b/src/components/Checkbox/Checkbox.stories.js @@ -83,12 +83,13 @@ const section = { function Template(args) { const {errorSchema: validateSchema, warningSchema} = getSchemas(section); - const {withErrors, withWarnings, ...props} = args; + const {withErrors, withWarnings, initialValues, ...props} = args; return ( {}} + enableReinitialize > {({values, submitForm}) => { const warnings = withWarnings ? getWarnings(warningSchema, values) || {} : {}; @@ -128,7 +129,8 @@ WithReadOnlyMode.args = { required: false, name: 'S1.0.S1P1.answer.value', warnings: {}, - options + options, + initialValues: ['2'] }; export const WithErrors = Template.bind({}); diff --git a/src/components/Checkbox/Checkbox.test.js b/src/components/Checkbox/Checkbox.test.js index dca948b..ec4f663 100644 --- a/src/components/Checkbox/Checkbox.test.js +++ b/src/components/Checkbox/Checkbox.test.js @@ -122,9 +122,18 @@ describe('', () => { props.field.value = ['2', '3']; }); - it('should display the selected options', () => { + it('should be checked the selected options', () => { const {container} = getComponent(); - expect(getByText(container, 'Option 2, Option 3')).toBeInTheDocument(); + const firstSelectedOption = getByTestId(container, 'checkbox-1'); + const secondSelectedOption = getByTestId(container, 'checkbox-2'); + expect(firstSelectedOption.querySelector('input').checked).toBe(true); + expect(secondSelectedOption.querySelector('input').checked).toBe(true); + }); + + it('should not be checked the other options', () => { + const {container} = getComponent(); + const unselectedOption = getByTestId(container, 'checkbox-0'); + expect(unselectedOption.querySelector('input').checked).toBe(false); }); }); @@ -133,16 +142,16 @@ describe('', () => { props.field.value = []; }); - it('should display `Sin respuesta.`', () => { + it('should not display `Sin respuesta.`', () => { const {container} = getComponent(); - expect(getByText(container, 'Sin respuesta.')).toBeInTheDocument(); + expect(queryByText(container, 'Sin respuesta.')).toBeNull(); }); }); - it('should not render the list of checkboxes', () => { + it('should render the list of checkboxes', () => { const {container} = getComponent(); props.options.forEach(option => { - expect(queryByText(container, option.label)).toBeNull(); + expect(getByText(container, option.label)).toBeInTheDocument(); }); }); }); diff --git a/src/components/FormBuilder/FormBuilder.js b/src/components/FormBuilder/FormBuilder.js index 01e8d8d..b4d7133 100644 --- a/src/components/FormBuilder/FormBuilder.js +++ b/src/components/FormBuilder/FormBuilder.js @@ -22,11 +22,11 @@ function FormBuilder({ page, onSubmit, onPrevious, - isSurvey, components, - initialValues + initialValues, + isReadOnly }) { - const [readOnlyMode, setReadOnlyMode] = useState(false); + const [readOnlyMode, setReadOnlyMode] = useState(isReadOnly); const [showSurvey, setShowSurvey] = useState(false); const [selectedSectionId, setSelectedSelectionId] = useState(); const [openModal, setOpenModal] = useState(); @@ -35,7 +35,7 @@ function FormBuilder({ const handleShowSurvey = (sectionId, readOnly) => { setShowSurvey(sectionId); - setReadOnlyMode(readOnly ? sectionId : false); + setReadOnlyMode(readOnly); }; const handleOpenModal = (modal, sectionId) => { @@ -55,7 +55,7 @@ function FormBuilder({ const newValues = values; const lastSection = getLastId(values[section.name]); newValues[section.name].push({...formInitialValues[section.name][0], id: lastSection + 1}); - return setValues(newValues); + setValues(newValues); }; return ( @@ -79,26 +79,36 @@ function FormBuilder({ { components.SectionHeader - ? + ? ( + handleShowSurvey(currentSection.id, true)} + onEdit={() => handleShowSurvey(currentSection.id, false)} + onDelete={() => handleOpenModal(modals.CONFIRM_DELETE_SECTION_MODAL, currentSection.id)} + sectionsLength={values[section.name].length} + section={section} + values={currentSection} + isReadOnly={isReadOnly} + /> + ) : ( handleOpenModal(modals.PREVIEW_MODAL, currentSection.id)} + onView={() => handleShowSurvey(currentSection.id, true)} onEdit={() => handleShowSurvey(currentSection.id, false)} onDelete={() => handleOpenModal(modals.CONFIRM_DELETE_SECTION_MODAL, currentSection.id)} sectionsLength={values[section.name].length} section={section} values={currentSection} - isSurvey={isSurvey} + isReadOnly={isReadOnly} /> ) } - {(!isSurvey || showSurvey === currentSection.id) && ( + {showSurvey === currentSection.id && ( @@ -140,6 +150,7 @@ function FormBuilder({ ? () => handleOpenModal(modals.INTERRUPTION_MODAL, section.id) : undefined } + readOnlyMode={isReadOnly} /> ) } @@ -156,7 +167,7 @@ FormBuilder.propTypes = { section: sectionPropTypes.isRequired, page: PropTypes.number.isRequired, isLastSection: PropTypes.bool, - isSurvey: PropTypes.bool, + isReadOnly: PropTypes.bool, components: PropTypes.shape({ SectionHeader: PropTypes.node, NavigationButtons: PropTypes.node @@ -170,7 +181,7 @@ FormBuilder.defaultProps = { FormBuilder.defaultProps = { isLastSection: false, - isSurvey: true, + isReadOnly: false, components: {} }; diff --git a/src/components/FormBuilder/FormBuilder.stories.js b/src/components/FormBuilder/FormBuilder.stories.js index a41cadd..47dd764 100644 --- a/src/components/FormBuilder/FormBuilder.stories.js +++ b/src/components/FormBuilder/FormBuilder.stories.js @@ -408,3 +408,36 @@ WithCustomNavigationButtons.args = { NavigationButtons: props => } }; + +export const WithReadOnlyMode = Template.bind({}); +WithReadOnlyMode.args = { + initialValues: { + S1: [ + { + id: 1, + S1P1: { + id: 1, + answer: {value: 'test'} + }, + S1P2: { + id: 2, + answer: {value: 26} + }, + S1P3: { + id: 3, + answer: [ + { + id: 1, + value: 123456 + }, + { + id: 2, + value: 12345678 + } + ] + } + } + ] + }, + isReadOnly: true +}; diff --git a/src/components/FormBuilder/Modals/Modals.js b/src/components/FormBuilder/Modals/Modals.js index 68dbe29..eef7e04 100644 --- a/src/components/FormBuilder/Modals/Modals.js +++ b/src/components/FormBuilder/Modals/Modals.js @@ -4,13 +4,10 @@ import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Modal from '@/components/Modal'; -import QuestionBuilder from '@/components/QuestionBuilder'; import Radio from '@/components/Radio'; import modals from '@/constants/modals'; -const getChildren = (modal, { - values, index, section, options, label, name -}) => { +const getChildren = (modal, {options, label, name}) => { switch (modal) { case modals.CONFIRM_DELETE_SECTION_MODAL: return ( @@ -21,10 +18,6 @@ const getChildren = (modal, { ¿Esta seguro que desea borrar esta sección? ); - case modals.PREVIEW_MODAL: - return ( - - ); case modals.INTERRUPTION_MODAL: return ( diff --git a/src/components/FormBuilder/Modals/Modals.stories.js b/src/components/FormBuilder/Modals/Modals.stories.js index 28965c3..6ddbfc5 100644 --- a/src/components/FormBuilder/Modals/Modals.stories.js +++ b/src/components/FormBuilder/Modals/Modals.stories.js @@ -26,42 +26,9 @@ function Template(args) { ); } -export const Preview = Template.bind({}); -Preview.args = { - modal: 1, - index: 0, - section: { - id: 1, - name: 'S1', - label: 'Sección 1', - multiple: false, - questions: [ - { - id: 1, - label: 'Ingrese su nombre', - name: 'S1P1', - number: '1', - type: 1, - options: [], - validations: [], - userVarName: 's1p1' - } - ], - headers: [ - { - id: 1, - question: 1 - } - ] - }, - values: { - S1: {id: 1, answer: ''} - } -}; - export const Interruption = Template.bind({}); Interruption.args = { - modal: 2, + modal: 1, label: 'Select an option', options: [ { @@ -77,4 +44,4 @@ Interruption.args = { }; export const ConfirmDeleteSection = Template.bind({}); -ConfirmDeleteSection.args = {modal: 3}; +ConfirmDeleteSection.args = {modal: 2}; diff --git a/src/components/FormBuilder/Modals/Modals.test.js b/src/components/FormBuilder/Modals/Modals.test.js index a0a6aef..2f2cde1 100644 --- a/src/components/FormBuilder/Modals/Modals.test.js +++ b/src/components/FormBuilder/Modals/Modals.test.js @@ -1,4 +1,4 @@ -import {getByTestId, getByText} from '@testing-library/react'; +import {getByTestId, getByText, queryByTestId} from '@testing-library/react'; import Modals from './Modals'; @@ -49,9 +49,9 @@ describe('', () => { }; }); - it('should render QuestionBuilder component', () => { + it('should not render QuestionBuilder component', () => { const {baseElement} = getComponent(); - expect(getByTestId(baseElement, 'question-builder')).toBeInTheDocument(); + expect(queryByTestId(baseElement, 'question-builder')).toBeNull(); }); }); diff --git a/src/components/FormBuilder/SectionHeader/SectionHeader.js b/src/components/FormBuilder/SectionHeader/SectionHeader.js index 3011be3..cd84ef5 100644 --- a/src/components/FormBuilder/SectionHeader/SectionHeader.js +++ b/src/components/FormBuilder/SectionHeader/SectionHeader.js @@ -33,7 +33,7 @@ const getHeaders = (questions, values, headers) => { }; function SectionHeader({ - section, sectionsLength, onView, onEdit, onDelete, values, isSurvey + section, sectionsLength, onView, onEdit, onDelete, values, isReadOnly }) { return ( {section.introduction} )} - {isSurvey ? {getHeaders(section.questions, values, section.headers)} : null} + {getHeaders(section.questions, values, section.headers)} - {isSurvey ? ( - - - - - - - - {section.multiple && sectionsLength > 1 && ( - - + + + + + {!isReadOnly && ( + <> + + - )} - - ) : null} + {section.multiple && sectionsLength > 1 && ( + + + + )} + + )} + ); } @@ -78,7 +80,11 @@ SectionHeader.propTypes = { sectionsLength: PropTypes.number.isRequired, section: sectionPropTypes.isRequired, values: PropTypes.shape({}).isRequired, - isSurvey: PropTypes.bool.isRequired + isReadOnly: PropTypes.bool +}; + +SectionHeader.defaultProps = { + isReadOnly: false }; export default SectionHeader; diff --git a/src/components/FormBuilder/SectionHeader/SectionHeader.stories.js b/src/components/FormBuilder/SectionHeader/SectionHeader.stories.js index 04b2111..668a5bd 100644 --- a/src/components/FormBuilder/SectionHeader/SectionHeader.stories.js +++ b/src/components/FormBuilder/SectionHeader/SectionHeader.stories.js @@ -53,6 +53,7 @@ function Template(args) { onEdit={() => {}} onDelete={() => {}} sectionsLength={1} + isReadOnly={false} {...args} /> ); diff --git a/src/components/FormBuilder/SectionHeader/SectionHeader.test.js b/src/components/FormBuilder/SectionHeader/SectionHeader.test.js index baa1d1d..77fa485 100644 --- a/src/components/FormBuilder/SectionHeader/SectionHeader.test.js +++ b/src/components/FormBuilder/SectionHeader/SectionHeader.test.js @@ -77,26 +77,26 @@ describe('', () => { }); }); - describe('when `props.isSurvey` is `true`', () => { - beforeEach(() => { - props.isSurvey = true; - }); + it('should render a button to activate read only mode', () => { + const {container} = getComponent(); + expect(getByTestId(container, 'read-only-button')).toBeInTheDocument(); + }); - it('should render a button to activate read only mode', () => { + describe('when the button to active read only mode is clicked', () => { + beforeEach(() => { const {container} = getComponent(); - expect(getByTestId(container, 'read-only-button')).toBeInTheDocument(); + const button = getByTestId(container, 'read-only-button'); + fireEvent.click(button); }); - describe('when the button to active read only mode is clicked', () => { - beforeEach(() => { - const {container} = getComponent(); - const button = getByTestId(container, 'read-only-button'); - fireEvent.click(button); - }); + it('should fire `props.onView`', () => { + expect(props.onView).toHaveBeenCalledTimes(1); + }); + }); - it('should fire `props.onView`', () => { - expect(props.onView).toHaveBeenCalledTimes(1); - }); + describe('when `props.isReadOnly` is `false`', () => { + beforeEach(() => { + props.isReadOnly = false; }); it('should render a button to edit survey', () => { @@ -173,14 +173,9 @@ describe('', () => { }); }); - describe('when `props.isSurvey` is `false`', () => { + describe('when `props.isReadOnly` is `true`', () => { beforeEach(() => { - props.isSurvey = false; - }); - - it('should not render a button to activate read only mode', () => { - const {container} = getComponent(); - expect(queryByTestId(container, 'read-only-button')).toBeNull(); + props.isReadOnly = true; }); it('should not render a button to edit survey', () => { diff --git a/src/components/NavigationButtons/NavigationButtons.js b/src/components/NavigationButtons/NavigationButtons.js index c8e0dca..6085723 100644 --- a/src/components/NavigationButtons/NavigationButtons.js +++ b/src/components/NavigationButtons/NavigationButtons.js @@ -14,7 +14,8 @@ function NavigationButtons({ onAddNew, addButtonLabel, isLastSection, - onInterrupt + onInterrupt, + readOnlyMode }) { return ( @@ -27,29 +28,31 @@ function NavigationButtons({ > Anterior - - {onAddNew && ( - - )} - {onInterrupt && ( - - )} - + {!readOnlyMode && ( + + {onAddNew && ( + + )} + {onInterrupt && ( + + )} + + )}