fix: cleanData can now check the original location of the relation for correct comparison

This commit is contained in:
Josh 2023-01-10 17:43:29 +00:00
parent 8d6a247b14
commit a9d481673d
5 changed files with 60 additions and 33 deletions

View File

@ -21,7 +21,7 @@ import {
getAPIInnerErrors,
} from '@strapi/helper-plugin';
import { getTrad, removeKeyInObject } from '../../utils';
import { getTrad } from '../../utils';
import selectCrudReducer from '../../sharedReducers/crudReducer/selectors';
@ -361,11 +361,9 @@ const EditViewDataManagerProvider = ({
const createFormData = useCallback(
(modifiedData, initialData) => {
// First we need to remove the added keys needed for the dnd
const preparedData = removeKeyInObject(cloneDeep(modifiedData), '__temp_key__');
// Then we need to apply our helper
const cleanedData = cleanData(
{ browserState: preparedData, serverState: initialData },
{ browserState: modifiedData, serverState: initialData },
currentContentTypeLayout,
allLayoutData.components
);

View File

@ -1,6 +1,7 @@
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import isObject from 'lodash/isObject';
import { getInitialDataPathUsingTempKeys } from '../../../utils/paths';
/* eslint-disable indent */
@ -12,6 +13,8 @@ import isObject from 'lodash/isObject';
* @returns
*/
const cleanData = ({ browserState, serverState }, currentSchema, componentsSchema) => {
const rootServerState = serverState;
const rootBrowserState = browserState;
const getType = (schema, attrName) => get(schema, ['attributes', attrName, 'type'], '');
const getOtherInfos = (schema, arr) => get(schema, ['attributes', ...arr], '');
@ -20,10 +23,12 @@ const cleanData = ({ browserState, serverState }, currentSchema, componentsSchem
* @param {object} browserState the modifiedData from REDUX
* @param {object} serverState the initialData from REDUX
* @param {*} schema
* @param {string} path
* @returns
*/
const recursiveCleanData = (browserState, serverState, schema) => {
const recursiveCleanData = (browserState, serverState, schema, pathToParent) => {
return Object.keys(browserState).reduce((acc, current) => {
const path = pathToParent ? `${pathToParent}.${current}` : current;
const attrType = getType(schema, current);
// This is the field value
@ -61,7 +66,8 @@ const cleanData = ({ browserState, serverState }, currentSchema, componentsSchem
const subCleanedData = recursiveCleanData(
data,
(oldValue ?? [])[index],
componentsSchema[component]
componentsSchema[component],
`${path}.${index}`
);
return subCleanedData;
@ -69,20 +75,25 @@ const cleanData = ({ browserState, serverState }, currentSchema, componentsSchem
: value;
} else {
cleanedData = value
? recursiveCleanData(value, oldValue, componentsSchema[component])
? recursiveCleanData(value, oldValue, componentsSchema[component], path)
: value;
}
break;
case 'relation': {
const trueInitialDataPath = getInitialDataPathUsingTempKeys(
rootServerState,
rootBrowserState
)(path).join('.');
/**
* Because of how repeatable components work when you dig into them the server
* will have no object to compare too therefore no relation array will be setup
* because the component has not been initialised, therefore we can safely assume
* it needs to be added and provide a default empty array.
*/
let actualOldValue = oldValue ?? [];
let actualOldValue = get(rootServerState, trueInitialDataPath, []);
const valuesWithPositions = value.map((relation, index, allRelations) => {
const nextRelation = allRelations[index + 1];
@ -141,7 +152,8 @@ const cleanData = ({ browserState, serverState }, currentSchema, componentsSchem
const subCleanedData = recursiveCleanData(
componentData,
(oldValue ?? [])[index],
componentsSchema[componentData.__component]
componentsSchema[componentData.__component],
`${path}.${index}`
);
return subCleanedData;
@ -157,7 +169,7 @@ const cleanData = ({ browserState, serverState }, currentSchema, componentsSchem
}, {});
};
return recursiveCleanData(browserState, serverState, currentSchema);
return recursiveCleanData(browserState, serverState, currentSchema, '');
};
// TODO: check which parts are still needed: I suspect the

View File

@ -15,6 +15,7 @@ import { getTrad } from '../../utils';
import { PUBLICATION_STATES, RELATIONS_TO_DISPLAY, SEARCH_RESULTS_TO_DISPLAY } from './constants';
import { connect, select, normalizeSearchResults, diffRelations, normalizeRelation } from './utils';
import { getInitialDataPathUsingTempKeys } from '../../utils/paths';
export const RelationInputDataManager = ({
error,
@ -50,28 +51,7 @@ export const RelationInputDataManager = ({
const nameSplit = name.split('.');
const initialDataPath = nameSplit.reduce((acc, currentValue, index) => {
const initialDataParent = get(initialData, acc);
const modifiedDataTempKey = get(modifiedData, [
...nameSplit.slice(0, index),
currentValue,
'__temp_key__',
]);
if (Array.isArray(initialDataParent) && typeof modifiedDataTempKey === 'number') {
const initialDataIndex = initialDataParent.findIndex(
(entry) => entry.__temp_key__ === modifiedDataTempKey
);
acc.push(initialDataIndex.toString());
return acc;
}
acc.push(currentValue);
return acc;
}, []);
const initialDataPath = getInitialDataPathUsingTempKeys(initialData, modifiedData)(name);
const relationsFromModifiedData = get(modifiedData, name, []);

View File

@ -5,7 +5,7 @@ const getMaxTempKey = (arr) => {
const maxTempKey = Math.max.apply(
Math,
arr.map((o) => o.__temp_key__ ?? o.id)
arr.map((o) => o.__temp_key__ ?? 0)
);
return Number.isNaN(maxTempKey) ? -1 : maxTempKey;

View File

@ -0,0 +1,37 @@
/**
* This file is for all helpers related to `paths` in the CM.
*/
import { get } from 'lodash';
/**
* This is typically used in circumstances where there are re-orderable pieces e.g. Dynamic Zones
* or Repeatable fields. It finds the _original_ location of the initial data using `__temp_key__` values
* which are added to the fields in the `INIT_FORM` reducer to give array data a stable (when you add
* a new item they wont have a server ID).
*/
export const getInitialDataPathUsingTempKeys = (initialData, modifiedData) => (currentPath) => {
const splitPath = currentPath.split('.');
return splitPath.reduce((acc, currentValue, index) => {
const initialDataParent = get(initialData, acc);
const modifiedDataTempKey = get(modifiedData, [
...splitPath.slice(0, index),
currentValue,
'__temp_key__',
]);
if (Array.isArray(initialDataParent) && typeof modifiedDataTempKey === 'number') {
const initialDataIndex = initialDataParent.findIndex(
(entry) => entry.__temp_key__ === modifiedDataTempKey
);
acc.push(initialDataIndex.toString());
return acc;
}
acc.push(currentValue);
return acc;
}, []);
};