diff --git a/packages/strapi-plugin-content-manager/admin/src/components/FieldComponent/index.js b/packages/strapi-plugin-content-manager/admin/src/components/FieldComponent/index.js index ac85e1dee2..05348f8b10 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/FieldComponent/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/FieldComponent/index.js @@ -20,6 +20,7 @@ const FieldComponent = ({ icon, isFromDynamicZone, isRepeatable, + isNested, label, max, min, @@ -87,6 +88,7 @@ const FieldComponent = ({ componentUid={componentUid} fields={displayedFields} isFromDynamicZone={isFromDynamicZone} + isNested={isNested} max={max} min={min} name={name} @@ -102,6 +104,7 @@ FieldComponent.defaultProps = { icon: 'smile', isFromDynamicZone: false, isRepeatable: false, + isNested: false, max: Infinity, min: -Infinity, }; @@ -112,6 +115,7 @@ FieldComponent.propTypes = { icon: PropTypes.string, isFromDynamicZone: PropTypes.bool, isRepeatable: PropTypes.bool, + isNested: PropTypes.bool, label: PropTypes.string.isRequired, max: PropTypes.number, min: PropTypes.number, diff --git a/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/BannerWrapper.js b/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/BannerWrapper.js index 31f7d1386d..0a703baebd 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/BannerWrapper.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/BannerWrapper.js @@ -150,7 +150,7 @@ const BannerWrapper = styled.button` } if (hasErrors) { - fill = '#FAA684'; + fill = '#F64D0A'; trashFill = '#F64D0A'; } diff --git a/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/DraggedItem.js b/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/DraggedItem.js index e3abb8c14f..8831082a9f 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/DraggedItem.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/DraggedItem.js @@ -35,6 +35,7 @@ const DraggedItem = ({ modifiedData, moveComponentField, removeRepeatableField, + triggerFormValidation, } = useDataManager(); const { setIsDraggingComponent, unsetIsDraggingComponent } = useEditView(); const mainField = get(schema, ['settings', 'mainField'], 'id'); @@ -127,6 +128,8 @@ const DraggedItem = ({ end: () => { // Enable the relations select to fire requests unsetIsDraggingComponent(); + // Update the errors + triggerFormValidation(); }, collect: monitor => ({ isDragging: monitor.isDragging(), @@ -171,50 +174,53 @@ const DraggedItem = ({ ref={refs} /> - - {fields.map((fieldRow, key) => { - return ( -
- {fieldRow.map(field => { - const currentField = getField(field.name); - const isComponent = - get(currentField, 'type', '') === 'component'; - const keys = `${componentFieldName}.${field.name}`; + {!isDragging && ( + + {fields.map((fieldRow, key) => { + return ( +
+ {fieldRow.map(field => { + const currentField = getField(field.name); + const isComponent = + get(currentField, 'type', '') === 'component'; + const keys = `${componentFieldName}.${field.name}`; - if (isComponent) { - const componentUid = currentField.component; - const metas = getMeta(field.name); + if (isComponent) { + const componentUid = currentField.component; + const metas = getMeta(field.name); + + return ( + + ); + } return ( - +
+ {}} + onBlur={hasErrors ? checkFormErrors : null} + /> +
); - } - - return ( -
- {}} - onBlur={hasErrors ? checkFormErrors : null} - /> -
- ); - })} -
- ); - })} -
+ })} +
+ ); + })} +
+ )}
); diff --git a/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/index.js b/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/index.js index a256399126..2c12b8d5eb 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/RepeatableComponent/index.js @@ -1,7 +1,7 @@ import React, { useReducer } from 'react'; import { useDrop } from 'react-dnd'; import PropTypes from 'prop-types'; -import { get } from 'lodash'; +import { get, take } from 'lodash'; import { FormattedMessage } from 'react-intl'; import { ErrorMessage } from '@buffetjs/styles'; import pluginId from '../../pluginId'; @@ -18,6 +18,7 @@ const RepeatableComponent = ({ componentValue, componentValueLength, fields, + isNested, max, min, name, @@ -28,7 +29,7 @@ const RepeatableComponent = ({ const componentErrorKeys = Object.keys(formErrors) .filter(errorKey => { - return errorKey.split('.')[0] === name; + return take(errorKey.split('.'), isNested ? 3 : 1).join('.') === name; }) .map(errorKey => { return errorKey @@ -75,7 +76,7 @@ const RepeatableComponent = ({ const doesPreviousFieldContainErrorsAndIsOpen = componentErrorKeys.includes(`${name}.${index - 1}`) && index !== 0 && - collapses[index - 1].isOpen === false; + get(collapses, [index - 1, 'isOpen'], false) === false; const hasErrors = componentErrorKeys.includes(componentFieldName); return ( @@ -88,8 +89,8 @@ const RepeatableComponent = ({ hasErrors={hasErrors} hasMinError={hasMinError} isFirst={index === 0} - isOpen={collapses[index].isOpen} - key={collapses[index]._temp__id} + isOpen={get(collapses, [index, 'isOpen'], false)} + key={get(collapses, [index, '_temp__id'], null)} onClickToggle={() => { // Close all other collapses and open the selected one toggleCollapses(index); @@ -107,6 +108,7 @@ const RepeatableComponent = ({ hoverIndex, }); }} + parentName={name} schema={schema} toggleCollapses={toggleCollapses} /> @@ -162,6 +164,7 @@ RepeatableComponent.defaultProps = { componentValue: null, componentValueLength: 0, fields: [], + isNested: false, max: Infinity, min: -Infinity, }; @@ -171,6 +174,7 @@ RepeatableComponent.propTypes = { componentValue: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), componentValueLength: PropTypes.number, fields: PropTypes.array, + isNested: PropTypes.bool, max: PropTypes.number, min: PropTypes.number, name: PropTypes.string.isRequired, diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/EditViewDataManagerProvider/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/EditViewDataManagerProvider/index.js index 50b8eb79bf..c3cb9cd836 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/EditViewDataManagerProvider/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/EditViewDataManagerProvider/index.js @@ -411,6 +411,11 @@ const EditViewDataManagerProvider = ({ setIsSubmitting, shouldShowLoadingState, slug, + triggerFormValidation: () => { + dispatch({ + type: 'TRIGGER_FORM_VALIDATION', + }); + }, }} > {showLoader ? ( diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/EditViewDataManagerProvider/reducer.js b/packages/strapi-plugin-content-manager/admin/src/containers/EditViewDataManagerProvider/reducer.js index db8b0e0d12..b343e9b76d 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/EditViewDataManagerProvider/reducer.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/EditViewDataManagerProvider/reducer.js @@ -187,7 +187,17 @@ const reducer = (state, action) => { case 'REMOVE_REPEATABLE_FIELD': { const componentPathToRemove = ['modifiedData', ...action.keys]; - return state.deleteIn(componentPathToRemove); + return state + .update('shouldCheckErrors', v => { + const hasErrors = state.get('formErrors').keySeq().size > 0; + + if (hasErrors) { + return !v; + } + + return v; + }) + .deleteIn(componentPathToRemove); } case 'REMOVE_RELATION': @@ -223,6 +233,16 @@ const reducer = (state, action) => { case 'SUBMIT_SUCCESS': case 'DELETE_SUCCEEDED': return state.update('initialData', () => state.get('modifiedData')); + case 'TRIGGER_FORM_VALIDATION': + return state.update('shouldCheckErrors', v => { + const hasErrors = state.get('formErrors').keySeq().size > 0; + + if (hasErrors) { + return !v; + } + + return v; + }); default: return state; }