mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 19:04:38 +00:00
Merge pull request #14881 from strapi/chore/refactor-edit-view
[Content manager] Refactor edit view
This commit is contained in:
commit
94cd474940
@ -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;
|
||||
@ -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';
|
||||
|
||||
@ -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 };
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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([]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -30,7 +30,7 @@ const EditViewLayoutManager = ({ layout, ...rest }) => {
|
||||
return <LoadingIndicatorPage />;
|
||||
}
|
||||
|
||||
return <Permissions {...rest} layout={currentLayout} userPermissions={permissions} />;
|
||||
return <Permissions {...rest} userPermissions={permissions} />;
|
||||
};
|
||||
|
||||
EditViewLayoutManager.propTypes = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user