From d9ca6451d7917637d5ee54d8220cb62bccd8b839 Mon Sep 17 00:00:00 2001 From: soupette Date: Fri, 22 Nov 2019 11:18:49 +0100 Subject: [PATCH] Add dynamic to edit a field --- .../src/components/SelectWrapper/index.js | 2 +- .../containers/DataManagerProvider/index.js | 2 +- .../containers/DataManagerProvider/reducer.js | 63 ++++++++++-------- .../admin/src/containers/FormModal/index.js | 64 +++++++++++++------ .../admin/src/containers/FormModal/reducer.js | 16 ++++- .../src/containers/FormModal/utils/forms.js | 51 +++++++++++---- 6 files changed, 135 insertions(+), 63 deletions(-) diff --git a/packages/strapi-plugin-content-manager/admin/src/components/SelectWrapper/index.js b/packages/strapi-plugin-content-manager/admin/src/components/SelectWrapper/index.js index 21b0529c69..3ec0eeb3e6 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/SelectWrapper/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/SelectWrapper/index.js @@ -3,7 +3,7 @@ import React, { useState, useEffect, useRef, memo } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { Link, useLocation } from 'react-router-dom'; -import { get, isArray, isEmpty } from 'lodash'; +import { get, cloneDeep, isArray, isEmpty } from 'lodash'; import { request } from 'strapi-helper-plugin'; import pluginId from '../../pluginId'; import useDataManager from '../../hooks/useDataManager'; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js index 602ef963ca..d33b8f2b01 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/DataManagerProvider/index.js @@ -138,7 +138,7 @@ const DataManagerProvider = ({ children }) => { return ; } - console.log({ modifiedData }); + console.log({ modifiedData, contentTypes }); return ( { return fromJS(rest); } ) - .updateIn(['modifiedData', 'schema', 'attributes'], obj => { - const type = get(rest, 'type'); - const target = get(rest, 'target', null); - const nature = get(rest, 'nature', null); - const currentUid = state.getIn(['modifiedData', 'uid']); + .updateIn( + ['modifiedData', ...pathToDataToEdit, 'schema', 'attributes'], + obj => { + const type = get(rest, 'type', 'relation'); + const target = get(rest, 'target', null); + const nature = get(rest, 'nature', null); + const currentUid = state.getIn([ + 'modifiedData', + ...pathToDataToEdit, + 'uid', + ]); - // When the user in creating a relation with the same content type we need to create another attribute - // that is the opposite of the created one - if ( - type === 'relation' && - nature !== 'oneWay' && - nature !== 'manyWay' && - target === currentUid - ) { - const oppositeAttribute = { - nature, - target, - type, - unique: rest.unique, - required: rest.required, - dominant: nature === 'manyToMany' ? !rest.dominant : null, - targetAttribute: name, - }; + // When the user in creating a relation with the same content type we need to create another attribute + // that is the opposite of the created one + if ( + type === 'relation' && + nature !== 'oneWay' && + nature !== 'manyWay' && + target === currentUid + ) { + const oppositeAttribute = { + nature, + target, + type, + unique: rest.unique, + required: rest.required, + dominant: nature === 'manyToMany' ? !rest.dominant : null, + targetAttribute: name, + }; - return obj.update(rest.targetAttribute, () => { - return fromJS(oppositeAttribute); - }); + return obj.update(rest.targetAttribute, () => { + return fromJS(oppositeAttribute); + }); + } + + return obj; } - - return obj; - }); + ); } case 'GET_DATA_SUCCEEDED': return state diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js index 853fa4ce96..990bdf9778 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/index.js @@ -14,7 +14,7 @@ import { import { Inputs } from '@buffetjs/custom'; import { useHistory, useLocation } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; -import { get, isEmpty, toString, upperFirst } from 'lodash'; +import { get, has, isEmpty, set, toString, upperFirst } from 'lodash'; import pluginId from '../../pluginId'; import useQuery from '../../hooks/useQuery'; import useDataManager from '../../hooks/useDataManager'; @@ -44,6 +44,7 @@ const FormModal = () => { targetUid: null, attributeType: null, headerDisplayName: null, + pathToSchema: [], }; const [state, setState] = useState(initialStateData); const [reducerState, dispatch] = useReducer(reducer, initialState, init); @@ -59,34 +60,60 @@ const FormModal = () => { modifiedData: allDataSchema, sortedContentTypesList, } = useDataManager(); - const { formErrors, modifiedData } = reducerState.toJS(); + const { formErrors, initialData, modifiedData } = reducerState.toJS(); useEffect(() => { if (!isEmpty(search)) { // Return 'null' if there isn't any attributeType search params - //TODO need targetUID - // TODO change target by targetUID & add displayName instead of target const attributeType = query.get('attributeType'); const modalType = query.get('modalType'); const actionType = query.get('actionType'); + const attributeName = query.get('attributeName'); + const settingType = query.get('settingType'); + const forTarget = query.get('forTarget'); + const targetUid = query.get('targetUid'); + const headerDisplayName = query.get('headerDisplayName'); + const pathToSchema = + forTarget === 'contentType' || forTarget === 'component' + ? [forTarget] + : [forTarget, targetUid]; setState({ - attributeName: query.get('attributeName'), + attributeName, actionType, modalType, - settingType: query.get('settingType'), - forTarget: query.get('forTarget'), - targetUid: query.get('targetUid'), - headerDisplayName: query.get('headerDisplayName'), + settingType, + forTarget, + targetUid, + headerDisplayName, attributeType, + pathToSchema, }); // Set the predefined data structure to create an attribute if ( attributeType && attributeType !== 'null' && + // This condition is added to prevent the reducer state to be cleared when navigating from the base tab to tha advanced one state.modalType !== 'attribute' ) { + const attributeToEditNotFormatted = get( + allDataSchema, + [...pathToSchema, 'schema', 'attributes', attributeName], + {} + ); + const attributeToEdit = { + ...attributeToEditNotFormatted, + name: attributeName, + }; + + if ( + attributeType === 'relation' && + !has(attributeToEdit, ['targetAttribute']) + ) { + set(attributeToEdit, ['targetAttribute'], '-'); + } + dispatch({ type: 'SET_ATTRIBUTE_DATA_SCHEMA', attributeType, @@ -96,6 +123,8 @@ const FormModal = () => { 'error' ), targetUid: get(sortedContentTypesList, ['0', 'uid'], 'error'), + isEditing: actionType === 'edit', + modifiedDataToSetForEditing: attributeToEdit, }); } } @@ -122,8 +151,6 @@ const FormModal = () => { headerId = null; } - console.log({ allDataSchema }); - const checkFormValidity = async () => { let schema; @@ -134,15 +161,16 @@ const FormModal = () => { // && state.forTarget !== 'components' && // state.forTarget !== 'component' ) { - const pathToSchemaAttributes = - state.forTarget === 'contentType' || state.forTarget === 'component' - ? [state.forTarget] - : [state.forTarget, state.targetUid]; + const type = + state.attributeType === 'relation' ? 'relation' : modifiedData.type; schema = forms[state.modalType].schema( - get(allDataSchema, pathToSchemaAttributes, {}), - modifiedData.type, - modifiedData + get(allDataSchema, state.pathToSchema, {}), + type, + modifiedData, + state.actionType === 'edit', + state.attributeName, + initialData ); } else { // TODO validate component schema diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/reducer.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/reducer.js index d54201595b..7ada2a6551 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/reducer.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/reducer.js @@ -4,6 +4,7 @@ import pluralize from 'pluralize'; const initialState = fromJS({ formErrors: {}, modifiedData: {}, + initialData: {}, }); export const shouldPluralizeTargetAttribute = nature => @@ -75,7 +76,20 @@ const reducer = (state, action) => { case 'RESET_PROPS': return initialState; case 'SET_ATTRIBUTE_DATA_SCHEMA': { - const { attributeType, nameToSetForRelation, targetUid } = action; + const { + attributeType, + isEditing, + modifiedDataToSetForEditing, + nameToSetForRelation, + targetUid, + } = action; + + if (isEditing) { + return state + .update('modifiedData', () => fromJS(modifiedDataToSetForEditing)) + .update('initialData', () => fromJS(modifiedDataToSetForEditing)); + } + let dataToSet; if (attributeType === 'text') { diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/forms.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/forms.js index de37fb39c1..9aa3cdf8dc 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/forms.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/FormModal/utils/forms.js @@ -17,7 +17,7 @@ yup.addMethod(yup.mixed, 'defined', function() { yup.addMethod(yup.string, 'unique', function( message, - allReadyTakenValues, + alreadyTakenAttributes, validator ) { return this.test('unique', message, function(string) { @@ -25,7 +25,7 @@ yup.addMethod(yup.string, 'unique', function( return false; } - return !allReadyTakenValues.includes( + return !alreadyTakenAttributes.includes( typeof validator === 'function' ? validator(string) : string ); }); @@ -46,18 +46,42 @@ const ATTRIBUTES_THAT_DONT_HAVE_MIN_MAX_SETTINGS = [ const forms = { attribute: { - schema(currentSchema, attributeType, dataToValidate) { - const allreadyTakenAttributes = Object.keys( + schema( + currentSchema, + attributeType, + dataToValidate, + isEditing, + attributeToEditName, + initialData + ) { + const alreadyTakenAttributes = Object.keys( get(currentSchema, ['schema', 'attributes'], {}) - ); - const targetAttributeAllreadyTakenValue = dataToValidate.name - ? [...allreadyTakenAttributes, dataToValidate.name] - : allreadyTakenAttributes; + ).filter(attribute => { + if (isEditing) { + return attribute !== attributeToEditName; + } + + return true; + }); + + let targetAttributeAlreadyTakenValue = dataToValidate.name + ? [...alreadyTakenAttributes, dataToValidate.name] + : alreadyTakenAttributes; + + if ( + isEditing && + attributeType === 'relation' && + dataToValidate.target === currentSchema.uid + ) { + targetAttributeAlreadyTakenValue = targetAttributeAlreadyTakenValue.filter( + attribute => attribute !== initialData.targetAttribute + ); + } const commonShape = { name: yup .string() - .unique(errorsTrads.unique, allreadyTakenAttributes) + .unique(errorsTrads.unique, alreadyTakenAttributes) .required(errorsTrads.required), type: yup.string().required(errorsTrads.required), default: yup.string().nullable(), @@ -144,13 +168,12 @@ const forms = { return yup.object().shape({ name: yup .string() - .unique(errorsTrads.unique, allreadyTakenAttributes) + .unique(errorsTrads.unique, alreadyTakenAttributes) .required(errorsTrads.required), targetAttribute: yup .string() - .unique(errorsTrads.unique, targetAttributeAllreadyTakenValue) + .unique(errorsTrads.unique, targetAttributeAlreadyTakenValue) .required(errorsTrads.required), - type: yup.string().required(errorsTrads.required), target: yup.string().required(errorsTrads.required), nature: yup.string().required(), dominant: yup.boolean().nullable(), @@ -586,11 +609,11 @@ const forms = { }, }, contentType: { - schema(allReadyTakenValues) { + schema(alreadyTakenAttributes) { return yup.object().shape({ name: yup .string() - .unique(errorsTrads.unique, allReadyTakenValues, createUid) + .unique(errorsTrads.unique, alreadyTakenAttributes, createUid) .required(errorsTrads.required), collectionName: yup.string(), });