Merge branch 'v4/ds-migration' of github.com:strapi/strapi into v4/ds-migration

This commit is contained in:
Alexandre Bodin 2021-09-28 10:41:43 +02:00
commit e0184b62b1
11 changed files with 407 additions and 253 deletions

View File

@ -304,6 +304,14 @@ const EditViewDataManagerProvider = ({
console.error(err); console.error(err);
errors = getYupInnerErrors(err); errors = getYupInnerErrors(err);
toggleNotification({
type: 'warning',
message: {
id: getTrad('containers.EditView.notification.errors'),
defaultMessage: 'The form contains some errors',
},
});
} }
dispatch({ dispatch({
@ -311,7 +319,16 @@ const EditViewDataManagerProvider = ({
errors, errors,
}); });
}, },
[createFormData, isCreatingEntry, modifiedData, onPost, onPut, trackerProperty, yupSchema] [
createFormData,
isCreatingEntry,
modifiedData,
onPost,
onPut,
toggleNotification,
trackerProperty,
yupSchema,
]
); );
const handlePublish = useCallback(async () => { const handlePublish = useCallback(async () => {

View File

@ -295,6 +295,14 @@ const createYupSchemaAttribute = (type, validations, options) => {
return value !== null; return value !== null;
} }
if (type === 'date' || type === 'datetime') {
if (typeof value === 'string') {
return !isEmpty(value);
}
return !isEmpty(value.toString());
}
return !isEmpty(value); return !isEmpty(value);
}); });
} }

View File

@ -17,6 +17,7 @@ const Label = ({ intlLabel, id, labelAction, name, numberOfEntries, showNumberOf
const label = intlLabel?.id ? formatMessage(intlLabel) : ''; const label = intlLabel?.id ? formatMessage(intlLabel) : '';
return ( return (
<Box paddingBottom={1}>
<Row> <Row>
<Text textColor="neutral800" htmlFor={id || name} small bold as="label"> <Text textColor="neutral800" htmlFor={id || name} small bold as="label">
{label} {label}
@ -24,6 +25,7 @@ const Label = ({ intlLabel, id, labelAction, name, numberOfEntries, showNumberOf
</Text> </Text>
{labelAction && <LabelAction paddingLeft={1}>{labelAction}</LabelAction>} {labelAction && <LabelAction paddingLeft={1}>{labelAction}</LabelAction>}
</Row> </Row>
</Box>
); );
}; };

View File

@ -59,7 +59,6 @@ const FieldComponent = ({
return ( return (
<Box> <Box>
<Stack size={1}>
<Row justifyContent="space-between"> <Row justifyContent="space-between">
{intlLabel && ( {intlLabel && (
<Label <Label
@ -78,12 +77,14 @@ const FieldComponent = ({
defaultMessage: 'Reset Entry', defaultMessage: 'Reset Entry',
})} })}
icon={<DeleteIcon />} icon={<DeleteIcon />}
noBorder
onClick={() => { onClick={() => {
removeComponentFromField(name, componentUid); removeComponentFromField(name, componentUid);
}} }}
/> />
)} )}
</Row> </Row>
<Stack size={1}>
{!isRepeatable && !isInitialized && ( {!isRepeatable && !isInitialized && (
<ComponentInitializer <ComponentInitializer
isReadOnly={isReadOnly} isReadOnly={isReadOnly}
@ -94,6 +95,7 @@ const FieldComponent = ({
<NonRepeatableComponent <NonRepeatableComponent
componentUid={componentUid} componentUid={componentUid}
isFromDynamicZone={isFromDynamicZone} isFromDynamicZone={isFromDynamicZone}
isNested={isNested}
name={name} name={name}
/> />
)} )}

View File

@ -18,7 +18,7 @@ const CominSoonInput = ({ description, intlLabel, labelAction, error, name }) =>
) )
: name; : name;
const hint = description const hint = description?.id
? formatMessage( ? formatMessage(
{ id: description.id, defaultMessage: description.defaultMessage }, { id: description.id, defaultMessage: description.defaultMessage },
{ ...description.values } { ...description.values }

View File

@ -10,7 +10,7 @@ import { useContentTypeLayout } from '../../hooks';
import FieldComponent from '../FieldComponent'; import FieldComponent from '../FieldComponent';
import Inputs from '../Inputs'; import Inputs from '../Inputs';
const NonRepeatableComponent = ({ componentUid, name }) => { const NonRepeatableComponent = ({ componentUid, isFromDynamicZone, isNested, name }) => {
const { getComponentLayout } = useContentTypeLayout(); const { getComponentLayout } = useContentTypeLayout();
const componentLayoutData = useMemo(() => getComponentLayout(componentUid), [ const componentLayoutData = useMemo(() => getComponentLayout(componentUid), [
componentUid, componentUid,
@ -19,7 +19,15 @@ const NonRepeatableComponent = ({ componentUid, name }) => {
const fields = componentLayoutData.layouts.edit; const fields = componentLayoutData.layouts.edit;
return ( return (
<Box background="neutral100" paddingLeft={6} paddingRight={6} paddingTop={6} paddingBottom={6}> <Box
background={isFromDynamicZone ? 'neutral0' : 'neutral100'}
paddingLeft={6}
paddingRight={6}
paddingTop={6}
paddingBottom={6}
hasRadius={isNested}
borderColor={isNested ? 'neutral200' : ''}
>
<Stack size={6}> <Stack size={6}>
{fields.map((fieldRow, key) => { {fields.map((fieldRow, key) => {
return ( return (
@ -39,6 +47,7 @@ const NonRepeatableComponent = ({ componentUid, name }) => {
id: metadatas.label, id: metadatas.label,
defaultMessage: metadatas.label, defaultMessage: metadatas.label,
}} }}
isNested
isRepeatable={fieldSchema.repeatable} isRepeatable={fieldSchema.repeatable}
max={fieldSchema.max} max={fieldSchema.max}
min={fieldSchema.min} min={fieldSchema.min}
@ -67,8 +76,15 @@ const NonRepeatableComponent = ({ componentUid, name }) => {
); );
}; };
NonRepeatableComponent.defaultProps = {
isFromDynamicZone: false,
isNested: false,
};
NonRepeatableComponent.propTypes = { NonRepeatableComponent.propTypes = {
componentUid: PropTypes.string.isRequired, componentUid: PropTypes.string.isRequired,
isFromDynamicZone: PropTypes.bool,
isNested: PropTypes.bool,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
}; };

View File

@ -1,27 +1,37 @@
import React, { useCallback, useState, useEffect, useMemo, memo } from 'react'; import React, { useCallback, useState, useEffect, useMemo, memo } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormattedMessage, useIntl } from 'react-intl';
import { Link, useLocation } from 'react-router-dom';
import { findIndex, get, isArray, isEmpty, set } from 'lodash';
import { import {
DropdownIndicator, // FormattedMessage,
LabelIconWrapper, useIntl,
} from 'react-intl';
// import { Link, useLocation } from 'react-router-dom';
// import { findIndex, get, isArray, isEmpty, set } from 'lodash';
import get from 'lodash/get';
import isArray from 'lodash/isArray';
import {
// DropdownIndicator,
NotAllowedInput, NotAllowedInput,
useCMEditViewDataManager, useCMEditViewDataManager,
useQueryParams, // useQueryParams,
} from '@strapi/helper-plugin'; } from '@strapi/helper-plugin';
import { Flex, Text, Padded } from '@buffetjs/core'; // import { Flex, Text, Padded } from '@buffetjs/core';
import { stringify } from 'qs'; // import { stringify } from 'qs';
import axios from 'axios'; import axios from 'axios';
import { axiosInstance } from '../../../core/utils'; import { axiosInstance } from '../../../core/utils';
import { getTrad } from '../../utils'; // import { getTrad } from '../../utils';
import SelectOne from '../SelectOne'; import ComingSoonInput from '../Inputs/ComingSoonInput';
import SelectMany from '../SelectMany'; // import SelectOne from '../SelectOne';
import ClearIndicator from './ClearIndicator'; // import SelectMany from '../SelectMany';
import IndicatorSeparator from './IndicatorSeparator'; // import ClearIndicator from './ClearIndicator';
import Option from './Option'; // import IndicatorSeparator from './IndicatorSeparator';
import { A, BaselineAlignment } from './components'; // import Option from './Option';
import { connect, select, styles } from './utils'; // import { A, BaselineAlignment } from './components';
import {
connect,
select,
// styles
} from './utils';
const initialPaginationState = { const initialPaginationState = {
_contains: '', _contains: '',
@ -29,77 +39,89 @@ const initialPaginationState = {
_start: 0, _start: 0,
}; };
const buildParams = (query, paramsToKeep) => { // const buildParams = (query, paramsToKeep) => {
if (!paramsToKeep) { // if (!paramsToKeep) {
return {}; // return {};
} // }
return paramsToKeep.reduce((acc, current) => { // return paramsToKeep.reduce((acc, current) => {
const value = get(query, current, null); // const value = get(query, current, null);
if (value) { // if (value) {
set(acc, current, value); // set(acc, current, value);
} // }
return acc; // return acc;
}, {}); // }, {});
}; // };
function SelectWrapper({ function SelectWrapper({
description, description,
editable, // editable,
label, labelAction,
labelIcon, intlLabel,
isCreatingEntry, isCreatingEntry,
isFieldAllowed, isFieldAllowed,
isFieldReadable, isFieldReadable,
mainField, mainField,
name, name,
relationType, relationType,
targetModel, // targetModel,
placeholder, // placeholder,
queryInfos, queryInfos,
}) { }) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const [{ query }] = useQueryParams(); // const [{ query }] = useQueryParams();
// Disable the input in case of a polymorphic relation // Disable the input in case of a polymorphic relation
const isMorph = useMemo(() => relationType.toLowerCase().includes('morph'), [relationType]); const isMorph = useMemo(() => relationType.toLowerCase().includes('morph'), [relationType]);
const { const {
addRelation, // addRelation,
modifiedData, modifiedData,
moveRelation, // moveRelation,
onChange, // onChange,
onRemoveRelation, // onRemoveRelation,
} = useCMEditViewDataManager(); } = useCMEditViewDataManager();
const { pathname } = useLocation(); // const { pathname } = useLocation();
const value = get(modifiedData, name, null); const value = get(modifiedData, name, null);
const [state, setState] = useState(initialPaginationState); const [
const [options, setOptions] = useState([]); state,
const [isLoading, setIsLoading] = useState(false); // setState
const [isOpen, setIsOpen] = useState(false); ] = useState(initialPaginationState);
const [
// options,
setOptions,
] = useState([]);
const [
// isLoading,
setIsLoading,
] = useState(false);
const [
isOpen,
// setIsOpen
] = useState(false);
const filteredOptions = useMemo(() => { // const filteredOptions = useMemo(() => {
return options.filter(option => { // return options.filter(option => {
if (!isEmpty(value)) { // if (!isEmpty(value)) {
// SelectMany // // SelectMany
if (Array.isArray(value)) { // if (Array.isArray(value)) {
return findIndex(value, o => o.id === option.value.id) === -1; // return findIndex(value, o => o.id === option.value.id) === -1;
} // }
// SelectOne // // SelectOne
return get(value, 'id', '') !== option.value.id; // return get(value, 'id', '') !== option.value.id;
} // }
return true; // return true;
}); // });
}, [options, value]); // }, [options, value]);
const { const {
endPoint, endPoint,
containsKey, containsKey,
defaultParams, defaultParams,
shouldDisplayRelationLink, // shouldDisplayRelationLink,
paramsToKeep, // paramsToKeep,
} = queryInfos; } = queryInfos;
const isSingle = ['oneWay', 'oneToOne', 'manyToOne', 'oneToManyMorph', 'oneToOneMorph'].includes( const isSingle = ['oneWay', 'oneToOne', 'manyToOne', 'oneToManyMorph', 'oneToOneMorph'].includes(
@ -170,15 +192,17 @@ function SelectWrapper({
} }
}, },
[ [
isMorph,
isFieldAllowed,
state._limit,
state._contains,
defaultParams,
containsKey, containsKey,
defaultParams,
endPoint, endPoint,
idsToOmit, idsToOmit,
isFieldAllowed,
isMorph,
mainField.name, mainField.name,
setIsLoading,
setOptions,
state._contains,
state._limit,
] ]
); );
@ -193,180 +217,198 @@ function SelectWrapper({
return () => source.cancel('Operation canceled by the user.'); return () => source.cancel('Operation canceled by the user.');
}, [getData, isOpen]); }, [getData, isOpen]);
const handleInputChange = (inputValue, { action }) => { // const handleInputChange = (inputValue, { action }) => {
if (action === 'input-change') { // if (action === 'input-change') {
setState(prevState => { // setState(prevState => {
if (prevState._contains === inputValue) { // if (prevState._contains === inputValue) {
return prevState; // return prevState;
} // }
return { ...prevState, _contains: inputValue, _start: 0 }; // return { ...prevState, _contains: inputValue, _start: 0 };
}); // });
} // }
return inputValue; // return inputValue;
}; // };
const handleMenuScrollToBottom = () => { // const handleMenuScrollToBottom = () => {
setState(prevState => ({ ...prevState, _limit: prevState._limit + 20 })); // setState(prevState => ({ ...prevState, _limit: prevState._limit + 20 }));
}; // };
const handleMenuClose = () => { // const handleMenuClose = () => {
setState(initialPaginationState); // setState(initialPaginationState);
setIsOpen(false); // setIsOpen(false);
}; // };
const handleChange = value => { // const handleChange = value => {
onChange({ target: { name, value: value ? value.value : value } }); // onChange({ target: { name, value: value ? value.value : value } });
}; // };
const handleAddRelation = value => { // const handleAddRelation = value => {
if (!isEmpty(value)) { // if (!isEmpty(value)) {
addRelation({ target: { name, value } }); // addRelation({ target: { name, value } });
} // }
}; // };
const handleMenuOpen = () => { // const handleMenuOpen = () => {
setIsOpen(true); // setIsOpen(true);
}; // };
const to = `/content-manager/collectionType/${targetModel}/${value ? value.id : null}`; // const to = `/content-manager/collectionType/${targetModel}/${value ? value.id : null}`;
const searchToPersist = stringify(buildParams(query, paramsToKeep), { encode: false }); // const searchToPersist = stringify(buildParams(query, paramsToKeep), { encode: false });
const link = useMemo(() => { // const link = useMemo(() => {
if (!value) { // if (!value) {
return null; // return null;
} // }
if (!shouldDisplayRelationLink) { // if (!shouldDisplayRelationLink) {
return null; // return null;
} // }
return ( // return (
<Link to={{ pathname: to, state: { from: pathname }, search: searchToPersist }}> // <Link to={{ pathname: to, state: { from: pathname }, search: searchToPersist }}>
<FormattedMessage id="content-manager.containers.Edit.seeDetails"> // <FormattedMessage id="content-manager.containers.Edit.seeDetails">
{msg => <A color="mediumBlue">{msg}</A>} // {msg => <A color="mediumBlue">{msg}</A>}
</FormattedMessage> // </FormattedMessage>
</Link> // </Link>
); // );
}, [shouldDisplayRelationLink, pathname, to, value, searchToPersist]); // }, [shouldDisplayRelationLink, pathname, to, value, searchToPersist]);
const Component = isSingle ? SelectOne : SelectMany; // const Component = isSingle ? SelectOne : SelectMany;
const associationsLength = isArray(value) ? value.length : 0; const associationsLength = isArray(value) ? value.length : 0;
const isDisabled = useMemo(() => { // const isDisabled = useMemo(() => {
if (isMorph) { // if (isMorph) {
return true; // return true;
} // }
if (!isCreatingEntry) { // if (!isCreatingEntry) {
return (!isFieldAllowed && isFieldReadable) || !editable; // return (!isFieldAllowed && isFieldReadable) || !editable;
} // }
return !editable; // return !editable;
}, [isMorph, isCreatingEntry, editable, isFieldAllowed, isFieldReadable]); // }, [isMorph, isCreatingEntry, editable, isFieldAllowed, isFieldReadable]);
const labelIconformatted = labelIcon const multipleLabel = intlLabel.id
? { icon: labelIcon.icon, title: formatMessage(labelIcon.title) } ? formatMessage({ id: intlLabel.id, defaultMessage: intlLabel.defaultMessage })
: labelIcon; : name;
const formattedLabel = isSingle
? intlLabel
: {
// Custom trad id in order to add the label count
id: 'relations-label',
defaultMessage: '{label} ({count})',
values: { label: multipleLabel, count: associationsLength },
};
if (!isFieldAllowed && isCreatingEntry) { if (!isFieldAllowed && isCreatingEntry) {
return <NotAllowedInput label={label} labelIcon={labelIconformatted} />; return <NotAllowedInput intlLabel={intlLabel} labelAction={labelAction} />;
} }
if (!isCreatingEntry && !isFieldAllowed && !isFieldReadable) { if (!isCreatingEntry && !isFieldAllowed && !isFieldReadable) {
return <NotAllowedInput label={label} labelIcon={labelIconformatted} />; return <NotAllowedInput intlLabel={intlLabel} labelAction={labelAction} />;
} }
return ( return (
<Padded> <ComingSoonInput
<BaselineAlignment /> intlLabel={formattedLabel}
<Flex justifyContent="space-between"> labelAction={labelAction}
<Flex> description={description}
<Text fontWeight="semiBold">
<span>
{label}
{!isSingle && ` (${associationsLength})`}
</span>
</Text>
{labelIconformatted && (
<div style={{ lineHeight: '13px' }}>
<LabelIconWrapper title={labelIconformatted.title}>
{labelIconformatted.icon}
</LabelIconWrapper>
</div>
)}
</Flex>
{isSingle && link}
</Flex>
{!isEmpty(description) && (
<Padded top size="xs">
<BaselineAlignment />
<Text fontSize="sm" color="grey" lineHeight="12px" ellipsis>
{description}
</Text>
</Padded>
)}
<Padded top size="sm">
<BaselineAlignment />
<Component
addRelation={handleAddRelation}
components={{ ClearIndicator, DropdownIndicator, IndicatorSeparator, Option }}
displayNavigationLink={shouldDisplayRelationLink}
id={name}
isDisabled={isDisabled}
isLoading={isLoading}
isClearable
mainField={mainField}
move={moveRelation}
name={name} name={name}
options={filteredOptions}
onChange={handleChange}
onInputChange={handleInputChange}
onMenuClose={handleMenuClose}
onMenuOpen={handleMenuOpen}
onMenuScrollToBottom={handleMenuScrollToBottom}
onRemove={onRemoveRelation}
placeholder={
isEmpty(placeholder) ? (
<FormattedMessage id={getTrad('containers.Edit.addAnItem')} />
) : (
placeholder
)
}
searchToPersist={searchToPersist}
styles={styles}
targetModel={targetModel}
value={value}
/> />
</Padded>
<div style={{ marginBottom: 28 }} />
</Padded>
); );
// return (
// <Padded>
// <BaselineAlignment />
// <Flex justifyContent="space-between">
// <Flex>
// <Text fontWeight="semiBold">
// <span>
// {label}
// {!isSingle && ` (${associationsLength})`}
// </span>
// </Text>
// {labelIconformatted && (
// <div style={{ lineHeight: '13px' }}>
// <LabelIconWrapper title={labelIconformatted.title}>
// {labelIconformatted.icon}
// </LabelIconWrapper>
// </div>
// )}
// </Flex>
// {isSingle && link}
// </Flex>
// {!isEmpty(description) && (
// <Padded top size="xs">
// <BaselineAlignment />
// <Text fontSize="sm" color="grey" lineHeight="12px" ellipsis>
// {description}
// </Text>
// </Padded>
// )}
// <Padded top size="sm">
// <BaselineAlignment />
// <Component
// addRelation={handleAddRelation}
// components={{ ClearIndicator, DropdownIndicator, IndicatorSeparator, Option }}
// displayNavigationLink={shouldDisplayRelationLink}
// id={name}
// isDisabled={isDisabled}
// isLoading={isLoading}
// isClearable
// mainField={mainField}
// move={moveRelation}
// name={name}
// options={filteredOptions}
// onChange={handleChange}
// onInputChange={handleInputChange}
// onMenuClose={handleMenuClose}
// onMenuOpen={handleMenuOpen}
// onMenuScrollToBottom={handleMenuScrollToBottom}
// onRemove={onRemoveRelation}
// placeholder={
// isEmpty(placeholder) ? (
// <FormattedMessage id={getTrad('containers.Edit.addAnItem')} />
// ) : (
// placeholder
// )
// }
// searchToPersist={searchToPersist}
// styles={styles}
// targetModel={targetModel}
// value={value}
// />
// </Padded>
// <div style={{ marginBottom: 28 }} />
// </Padded>
// );
} }
SelectWrapper.defaultProps = { SelectWrapper.defaultProps = {
editable: true, // editable: true,
description: '', description: '',
label: '', labelAction: null,
labelIcon: null,
isFieldAllowed: true, isFieldAllowed: true,
placeholder: '', // placeholder: null,
}; };
SelectWrapper.propTypes = { SelectWrapper.propTypes = {
editable: PropTypes.bool, // editable: PropTypes.bool,
description: PropTypes.string, description: PropTypes.shape({
label: PropTypes.string,
labelIcon: PropTypes.shape({
icon: PropTypes.node.isRequired,
title: PropTypes.shape({
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
defaultMessage: PropTypes.string, defaultMessage: PropTypes.string.isRequired,
}), values: PropTypes.object,
}), }),
intlLabel: PropTypes.shape({
id: PropTypes.string.isRequired,
defaultMessage: PropTypes.string.isRequired,
values: PropTypes.object,
}).isRequired,
labelAction: PropTypes.element,
isCreatingEntry: PropTypes.bool.isRequired, isCreatingEntry: PropTypes.bool.isRequired,
isFieldAllowed: PropTypes.bool, isFieldAllowed: PropTypes.bool,
isFieldReadable: PropTypes.bool.isRequired, isFieldReadable: PropTypes.bool.isRequired,
@ -377,9 +419,13 @@ SelectWrapper.propTypes = {
}).isRequired, }).isRequired,
}).isRequired, }).isRequired,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
placeholder: PropTypes.string, // placeholder: PropTypes.shape({
// id: PropTypes.string.isRequired,
// defaultMessage: PropTypes.string.isRequired,
// values: PropTypes.object,
// }),
relationType: PropTypes.string.isRequired, relationType: PropTypes.string.isRequired,
targetModel: PropTypes.string.isRequired, // targetModel: PropTypes.string.isRequired,
queryInfos: PropTypes.shape({ queryInfos: PropTypes.shape({
containsKey: PropTypes.string.isRequired, containsKey: PropTypes.string.isRequired,
defaultParams: PropTypes.object, defaultParams: PropTypes.object,

View File

@ -5,10 +5,12 @@ import { CheckPermissions, useTracking } from '@strapi/helper-plugin';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { ContentLayout } from '@strapi/parts/Layout'; import { ContentLayout } from '@strapi/parts/Layout';
import { Box } from '@strapi/parts/Box'; import { Box } from '@strapi/parts/Box';
import { Divider } from '@strapi/parts/Divider';
import { Grid, GridItem } from '@strapi/parts/Grid'; import { Grid, GridItem } from '@strapi/parts/Grid';
import { LinkButton } from '@strapi/parts/LinkButton'; import { LinkButton } from '@strapi/parts/LinkButton';
import { Main } from '@strapi/parts/Main'; import { Main } from '@strapi/parts/Main';
import { Stack } from '@strapi/parts/Stack'; import { Stack } from '@strapi/parts/Stack';
import { TableLabel } from '@strapi/parts/Text';
import ConfigureIcon from '@strapi/icons/ConfigureIcon'; import ConfigureIcon from '@strapi/icons/ConfigureIcon';
import EditIcon from '@strapi/icons/EditIcon'; import EditIcon from '@strapi/icons/EditIcon';
import { InjectionZone } from '../../../shared/components'; import { InjectionZone } from '../../../shared/components';
@ -18,7 +20,7 @@ import DynamicZone from '../../components/DynamicZone';
// import FormWrapper from '../../components/FormWrapper'; // import FormWrapper from '../../components/FormWrapper';
import FieldComponent from '../../components/FieldComponent'; import FieldComponent from '../../components/FieldComponent';
import Inputs from '../../components/Inputs'; import Inputs from '../../components/Inputs';
// import SelectWrapper from '../../components/SelectWrapper'; import SelectWrapper from '../../components/SelectWrapper';
import CollectionTypeFormWrapper from '../../components/CollectionTypeFormWrapper'; import CollectionTypeFormWrapper from '../../components/CollectionTypeFormWrapper';
import EditViewDataManagerProvider from '../../components/EditViewDataManagerProvider'; import EditViewDataManagerProvider from '../../components/EditViewDataManagerProvider';
import SingleTypeFormWrapper from '../../components/SingleTypeFormWrapper'; import SingleTypeFormWrapper from '../../components/SingleTypeFormWrapper';
@ -88,6 +90,9 @@ const EditView = ({
); );
}, [currentContentTypeLayoutData]); }, [currentContentTypeLayoutData]);
const relationsLayout = currentContentTypeLayoutData.layouts.editRelations;
const displayedRelationsLength = relationsLayout.length;
return ( return (
<DataManagementWrapper allLayoutData={layout} slug={slug} id={id} origin={origin}> <DataManagementWrapper allLayoutData={layout} slug={slug} id={id} origin={origin}>
{({ {({
@ -241,6 +246,66 @@ const EditView = ({
<Informations /> <Informations />
<InjectionZone area="contentManager.editView.informations" /> <InjectionZone area="contentManager.editView.informations" />
</Box> </Box>
{displayedRelationsLength > 0 && (
<Box
as="aside"
aria-labelledby="additional-informations"
background="neutral0"
borderColor="neutral150"
hasRadius
paddingBottom={4}
paddingLeft={4}
paddingRight={4}
paddingTop={6}
>
<TableLabel textColor="neutral600">
{formatMessage(
{
id: getTrad('containers.Edit.relations'),
defaultMessage:
'{number, plural, =0 {relations} one {relation} other {relations}}',
},
{ number: displayedRelationsLength }
)}
</TableLabel>
<Box paddingTop={2} paddingBottom={6}>
<Divider />
</Box>
<Stack size={4}>
{relationsLayout.map(
({ name, fieldSchema, labelAction, metadatas, queryInfos }) => {
return (
<SelectWrapper
{...fieldSchema}
{...metadatas}
key={name}
description={{
id: metadatas.description,
defaultMessage: metadatas.description,
}}
intlLabel={{
id: metadatas.label,
defaultMessage: metadatas.label,
}}
labelAction={labelAction}
name={name}
relationsType={fieldSchema.relationType}
queryInfos={queryInfos}
placeholder={
metadatas.placeholder
? {
id: metadatas.placeholder,
defaultMessage: metadatas.placeholder,
}
: null
}
/>
);
}
)}
</Stack>
</Box>
)}
<Box as="aside" aria-labelledby="links"> <Box as="aside" aria-labelledby="links">
<Stack size={2}> <Stack size={2}>
{slug !== 'strapi::administrator' && ( {slug !== 'strapi::administrator' && (

View File

@ -110,7 +110,6 @@ const CMEditViewLocalePicker = ({
<Option <Option
value={value.value} value={value.value}
disabled disabled
hidden
startIcon={hasDraftAndPublishEnabled ? <Bullet status={currentLocaleStatus} /> : null} startIcon={hasDraftAndPublishEnabled ? <Bullet status={currentLocaleStatus} /> : null}
> >
{value.label} {value.label}

View File

@ -1,8 +1,7 @@
import React from 'react'; import React from 'react';
import get from 'lodash/get'; import get from 'lodash/get';
import I18N from '@strapi/icons/I18N'; import I18N from '@strapi/icons/I18N';
// FIXME import StrikedWorld from '@strapi/icons/StrikedWorld';
import HelpIcon from '@strapi/icons/HelpIcon';
import LabelAction from '../components/LabelAction'; import LabelAction from '../components/LabelAction';
import { getTrad } from '../utils'; import { getTrad } from '../utils';
@ -45,7 +44,7 @@ const enhanceEditLayout = layout =>
? 'This value is unique for the selected locale' ? 'This value is unique for the selected locale'
: 'This value is common to all locales', : 'This value is common to all locales',
}, },
icon: hasI18nEnabled ? <I18N aria-hidden /> : <HelpIcon aria-hidden />, icon: hasI18nEnabled ? <I18N aria-hidden /> : <StrikedWorld aria-hidden />,
}; };
acc.push({ ...field, labelAction: <LabelAction {...labelActionProps} /> }); acc.push({ ...field, labelAction: <LabelAction {...labelActionProps} /> });

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import I18N from '@strapi/icons/I18N'; import I18N from '@strapi/icons/I18N';
// FIXME
import HelpIcon from '@strapi/icons/HelpIcon'; import StrikedWorld from '@strapi/icons/StrikedWorld';
import LabelAction from '../../components/LabelAction'; import LabelAction from '../../components/LabelAction';
import { getTrad } from '../../utils'; import { getTrad } from '../../utils';
import mutateEditViewLayout, { import mutateEditViewLayout, {
@ -450,7 +450,7 @@ describe('i18n | contentManagerHooks | mutateEditViewLayout', () => {
labelAction: ( labelAction: (
<LabelAction <LabelAction
title={{ id: notLocalizedTrad, defaultMessage: notLocalizedTradDefaultMessage }} title={{ id: notLocalizedTrad, defaultMessage: notLocalizedTradDefaultMessage }}
icon={<HelpIcon aria-hidden />} icon={<StrikedWorld aria-hidden />}
/> />
), ),
}, },