Merge pull request #14881 from strapi/chore/refactor-edit-view

[Content manager] Refactor edit view
This commit is contained in:
markkaylor 2022-11-23 21:26:09 +01:00 committed by GitHub
commit 94cd474940
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 133 additions and 124 deletions

View File

@ -0,0 +1,56 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Grid, GridItem } from '@strapi/design-system/Grid';
import Inputs from '../../../components/Inputs';
import FieldComponent from '../../../components/FieldComponent';
const GridRow = ({ columns }) => {
return (
<Grid gap={4}>
{columns.map(({ fieldSchema, labelAction, metadatas, name, size, queryInfos }) => {
const isComponent = fieldSchema.type === 'component';
if (isComponent) {
const { component, max, min, repeatable = false, required = false } = fieldSchema;
return (
<GridItem col={size} s={12} xs={12} key={component}>
<FieldComponent
componentUid={component}
labelAction={labelAction}
isRepeatable={repeatable}
intlLabel={{
id: metadatas.label,
defaultMessage: metadatas.label,
}}
max={max}
min={min}
name={name}
required={required}
/>
</GridItem>
);
}
return (
<GridItem col={size} key={name} s={12} xs={12}>
<Inputs
size={size}
fieldSchema={fieldSchema}
keys={name}
labelAction={labelAction}
metadatas={metadatas}
queryInfos={queryInfos}
/>
</GridItem>
);
})}
</Grid>
);
};
GridRow.propTypes = {
columns: PropTypes.array.isRequired,
};
export default GridRow;

View File

@ -1,6 +1,6 @@
import React, { Suspense, memo, useCallback, useMemo } from 'react';
import React, { Suspense, memo } from 'react';
import PropTypes from 'prop-types';
import get from 'lodash/get';
import { useSelector } from 'react-redux';
import {
CheckPermissions,
LoadingIndicatorPage,
@ -18,8 +18,7 @@ import Pencil from '@strapi/icons/Pencil';
import { InjectionZone } from '../../../shared/components';
import permissions from '../../../permissions';
import DynamicZone from '../../components/DynamicZone';
import FieldComponent from '../../components/FieldComponent';
import Inputs from '../../components/Inputs';
import CollectionTypeFormWrapper from '../../components/CollectionTypeFormWrapper';
import EditViewDataManagerProvider from '../../components/EditViewDataManagerProvider';
import SingleTypeFormWrapper from '../../components/SingleTypeFormWrapper';
@ -27,64 +26,43 @@ import { getTrad } from '../../utils';
import DraftAndPublishBadge from './DraftAndPublishBadge';
import Informations from './Informations';
import Header from './Header';
import { createAttributesLayout, getFieldsActionMatchingPermissions } from './utils';
import { getFieldsActionMatchingPermissions } from './utils';
import DeleteLink from './DeleteLink';
import GridRow from './GridRow';
import { selectCurrentLayout, selectAttributesLayout } from './selectors';
const cmPermissions = permissions.contentManager;
const ctbPermissions = [{ action: 'plugin::content-type-builder.read', subject: null }];
/* eslint-disable react/no-array-index-key */
const EditView = ({
allowedActions,
isSingleType,
goBack,
layout,
slug,
id,
origin,
userPermissions,
}) => {
const EditView = ({ allowedActions, isSingleType, goBack, slug, id, origin, userPermissions }) => {
const { trackUsage } = useTracking();
const { formatMessage } = useIntl();
const { createActionAllowedFields, readActionAllowedFields, updateActionAllowedFields } =
useMemo(() => {
return getFieldsActionMatchingPermissions(userPermissions, slug);
}, [userPermissions, slug]);
getFieldsActionMatchingPermissions(userPermissions, slug);
const configurationPermissions = useMemo(() => {
return isSingleType
? cmPermissions.singleTypesConfigurations
: cmPermissions.collectionTypesConfigurations;
}, [isSingleType]);
const { layout, formattedContentTypeLayout } = useSelector((state) => ({
layout: selectCurrentLayout(state),
formattedContentTypeLayout: selectAttributesLayout(state),
}));
const configurationPermissions = isSingleType
? cmPermissions.singleTypesConfigurations
: cmPermissions.collectionTypesConfigurations;
// // FIXME when changing the routing
const configurationsURL = `/content-manager/${
isSingleType ? 'singleType' : 'collectionType'
}/${slug}/configurations/edit`;
const currentContentTypeLayoutData = get(layout, ['contentType'], {});
const DataManagementWrapper = useMemo(
() => (isSingleType ? SingleTypeFormWrapper : CollectionTypeFormWrapper),
[isSingleType]
);
const DataManagementWrapper = isSingleType ? SingleTypeFormWrapper : CollectionTypeFormWrapper;
// Check if a block is a dynamic zone
const isDynamicZone = useCallback((block) => {
const isDynamicZone = (block) => {
return block.every((subBlock) => {
return subBlock.every((obj) => obj.fieldSchema.type === 'dynamiczone');
});
}, []);
const formattedContentTypeLayout = useMemo(() => {
if (!currentContentTypeLayoutData.layouts) {
return [];
}
return createAttributesLayout(
currentContentTypeLayoutData.layouts.edit,
currentContentTypeLayoutData.attributes
);
}, [currentContentTypeLayoutData]);
};
return (
<DataManagementWrapper allLayoutData={layout} slug={slug} id={id} origin={origin}>
@ -171,65 +149,9 @@ const EditView = ({
borderColor="neutral150"
>
<Stack spacing={6}>
{row.map((grid, gridIndex) => {
return (
<Grid gap={4} key={gridIndex}>
{grid.map(
({
fieldSchema,
labelAction,
metadatas,
name,
size,
queryInfos,
}) => {
const isComponent = fieldSchema.type === 'component';
if (isComponent) {
const {
component,
max,
min,
repeatable = false,
required = false,
} = fieldSchema;
return (
<GridItem col={size} s={12} xs={12} key={component}>
<FieldComponent
componentUid={component}
labelAction={labelAction}
isRepeatable={repeatable}
intlLabel={{
id: metadatas.label,
defaultMessage: metadatas.label,
}}
max={max}
min={min}
name={name}
required={required}
/>
</GridItem>
);
}
return (
<GridItem col={size} key={name} s={12} xs={12}>
<Inputs
size={size}
fieldSchema={fieldSchema}
keys={name}
labelAction={labelAction}
metadatas={metadatas}
queryInfos={queryInfos}
/>
</GridItem>
);
}
)}
</Grid>
);
})}
{row.map((grid, gridRowIndex) => (
<GridRow columns={grid} key={gridRowIndex} />
))}
</Stack>
</Box>
);
@ -328,16 +250,6 @@ EditView.propTypes = {
canCreate: PropTypes.bool.isRequired,
canDelete: PropTypes.bool.isRequired,
}).isRequired,
layout: PropTypes.shape({
components: PropTypes.object.isRequired,
contentType: PropTypes.shape({
uid: PropTypes.string.isRequired,
settings: PropTypes.object.isRequired,
metadatas: PropTypes.object.isRequired,
options: PropTypes.object.isRequired,
attributes: PropTypes.object.isRequired,
}).isRequired,
}).isRequired,
id: PropTypes.string,
isSingleType: PropTypes.bool,
goBack: PropTypes.func.isRequired,
@ -346,7 +258,4 @@ EditView.propTypes = {
userPermissions: PropTypes.array,
};
export { EditView };
export default memo(EditView);
// export default () => 'TODO Edit view';

View File

@ -0,0 +1,10 @@
import { createSelector } from 'reselect';
import { createAttributesLayout } from './utils';
const selectCurrentLayout = (state) => state['content-manager_editViewLayoutManager'].currentLayout;
const selectAttributesLayout = createSelector(selectCurrentLayout, (layout) =>
createAttributesLayout(layout?.contentType ?? {})
);
export { selectCurrentLayout, selectAttributesLayout };

View File

@ -1,14 +1,19 @@
import { get, isEmpty } from 'lodash';
// TODO: refacto this file to avoid eslint issues
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-unused-vars */
const createAttributesLayout = (currentLayout, attributes) => {
const createAttributesLayout = (currentContentTypeLayoutData) => {
if (!currentContentTypeLayoutData.layouts) {
return [];
}
const currentLayout = currentContentTypeLayoutData.layouts.edit;
const attributes = currentContentTypeLayoutData.attributes;
const getType = (name) => get(attributes, [name, 'type'], '');
let currentRowIndex = 0;
const newLayout = [];
for (let row of currentLayout) {
currentLayout.forEach((row) => {
const hasDynamicZone = row.some(({ name }) => getType(name) === 'dynamiczone');
if (!newLayout[currentRowIndex]) {
@ -27,7 +32,7 @@ const createAttributesLayout = (currentLayout, attributes) => {
} else {
newLayout[currentRowIndex].push(row);
}
}
});
return newLayout.filter((arr) => arr.length > 0);
};

View File

@ -27,6 +27,12 @@ describe('Content Manager | EditView | utils | createAttributesLayout', () => {
[{ name: 'postal_code', size: 6 }],
[{ name: 'geolocation', size: 12 }],
];
const currentLayoutData = {
layouts: {
edit: currentLayout,
},
attributes,
};
const expected = [
[
[
@ -38,7 +44,7 @@ describe('Content Manager | EditView | utils | createAttributesLayout', () => {
],
];
expect(createAttributesLayout(currentLayout, attributes)).toEqual(expected);
expect(createAttributesLayout(currentLayoutData)).toEqual(expected);
});
it('Should return an array of size 2 if there is a dynamic zone at the end of the layout', () => {
@ -68,6 +74,12 @@ describe('Content Manager | EditView | utils | createAttributesLayout', () => {
[{ name: 'geolocation', size: 12 }],
[{ name: 'dynamicZone1', size: 12 }],
];
const currentLayoutData = {
layouts: {
edit: currentLayout,
},
attributes,
};
const expected = [
[
[
@ -80,7 +92,7 @@ describe('Content Manager | EditView | utils | createAttributesLayout', () => {
[[{ name: 'dynamicZone1', size: 12 }]],
];
expect(createAttributesLayout(currentLayout, attributes)).toEqual(expected);
expect(createAttributesLayout(currentLayoutData)).toEqual(expected);
});
it('Should return an array of size 2 if there is a dynamic zone at the beginning of the layout', () => {
@ -114,6 +126,12 @@ describe('Content Manager | EditView | utils | createAttributesLayout', () => {
[{ name: 'postal_code', size: 6 }],
[{ name: 'geolocation', size: 12 }],
];
const currentLayoutData = {
layouts: {
edit: currentLayout,
},
attributes,
};
const expected = [
[[{ name: 'dynamicZone1', size: 12 }]],
[
@ -126,7 +144,7 @@ describe('Content Manager | EditView | utils | createAttributesLayout', () => {
],
];
expect(createAttributesLayout(currentLayout, attributes)).toEqual(expected);
expect(createAttributesLayout(currentLayoutData)).toEqual(expected);
});
it('Should return an array of size 5 if there are 3 dynamic zones', () => {
@ -164,6 +182,12 @@ describe('Content Manager | EditView | utils | createAttributesLayout', () => {
[{ name: 'geolocation', size: 12 }],
[{ name: 'dynamicZone3', size: 12 }],
];
const currentLayoutData = {
layouts: {
edit: currentLayout,
},
attributes,
};
const expected = [
[[{ name: 'dynamicZone1', size: 12 }]],
[
@ -177,6 +201,11 @@ describe('Content Manager | EditView | utils | createAttributesLayout', () => {
[[{ name: 'dynamicZone3', size: 12 }]],
];
expect(createAttributesLayout(currentLayout, attributes)).toEqual(expected);
expect(createAttributesLayout(currentLayoutData)).toEqual(expected);
});
it('Should return an empty array when no layouts are found', () => {
const currentLayoutData = {};
expect(createAttributesLayout(currentLayoutData)).toEqual([]);
});
});

View File

@ -30,7 +30,7 @@ const EditViewLayoutManager = ({ layout, ...rest }) => {
return <LoadingIndicatorPage />;
}
return <Permissions {...rest} layout={currentLayout} userPermissions={permissions} />;
return <Permissions {...rest} userPermissions={permissions} />;
};
EditViewLayoutManager.propTypes = {