Merge pull request #10990 from strapi/cm/body-layout

CM init body layout
This commit is contained in:
cyril lopez 2021-09-16 12:56:18 +02:00 committed by GitHub
commit 942cdf8fc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1287 additions and 483 deletions

View File

@ -3,6 +3,7 @@ import { cloneDeep, get, isEmpty, isEqual, set } from 'lodash';
import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';
import { Prompt, Redirect } from 'react-router-dom';
import { Main } from '@strapi/parts/Main';
import {
LoadingIndicatorPage,
ContentManagerEditViewDataManagerContext,
@ -426,14 +427,6 @@ const EditViewDataManagerProvider = ({
});
}, []);
// const overlayBlockerParams = useMemo(
// () => ({
// children: <div />,
// noGradient: true,
// }),
// []
// );
// Redirect the user to the previous page if he is not allowed to read/update a document
if (shouldRedirectToHomepageWhenEditingEntry) {
return <Redirect to={from} />;
@ -477,13 +470,10 @@ const EditViewDataManagerProvider = ({
}}
>
<>
{/* <OverlayBlocker
key="overlayBlocker"
isOpen={status !== 'resolved'}
{...overlayBlockerParams}
/> */}
{isLoadingForData ? (
<LoadingIndicatorPage />
<Main aria-busy="true">
<LoadingIndicatorPage />
</Main>
) : (
<>
<Prompt
@ -506,7 +496,7 @@ EditViewDataManagerProvider.defaultProps = {
EditViewDataManagerProvider.propTypes = {
allLayoutData: PropTypes.object.isRequired,
allowedActions: PropTypes.object.isRequired,
children: PropTypes.arrayOf(PropTypes.element).isRequired,
children: PropTypes.node.isRequired,
componentsDataStructure: PropTypes.object.isRequired,
contentTypeDataStructure: PropTypes.object.isRequired,
createActionAllowedFields: PropTypes.array.isRequired,

View File

@ -0,0 +1,84 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';
import styled from 'styled-components';
import { Box } from '@strapi/parts/Box';
import { Row } from '@strapi/parts/Row';
import { Text } from '@strapi/parts/Text';
import Bullet from '@strapi/icons/Bullet';
import { pxToRem } from '@strapi/helper-plugin';
import { getTrad } from '../../../utils';
import { connect, select } from './utils';
const CustomBullet = styled(Bullet)`
width: ${pxToRem(6)};
height: ${pxToRem(6)};
* {
fill: ${({ theme, $bulletColor }) => theme.colors[$bulletColor]};
}
`;
const DraftAndPublishBadge = ({ hasDraftAndPublish, isPublished }) => {
const { formatMessage } = useIntl();
if (!hasDraftAndPublish) {
return null;
}
const colors = {
draft: {
textColor: 'secondary700',
bulletColor: 'secondary600',
box: {
background: 'secondary100',
borderColor: 'secondary200',
},
},
published: {
textColor: 'success700',
bullet: 'success600',
box: {
background: 'success100',
borderColor: 'success200',
},
},
};
const colorProps = isPublished ? colors.published : colors.draft;
return (
<Box hasRadius as="aside" paddingTop={4} paddingBottom={4} paddingLeft={5} {...colorProps.box}>
<Box as={Row}>
<CustomBullet $bulletColor={colorProps.bulletColor} />
<Box paddingLeft={3}>
<Text textColor={colorProps.textColor}>
{formatMessage({
id: getTrad('containers.Edit.information.editing'),
defaultMessage: 'Editing',
})}
&nbsp;
</Text>
<Text textColor={colorProps.textColor} bold>
{isPublished &&
formatMessage({
id: getTrad('containers.Edit.information.publishedVersion'),
defaultMessage: 'published version',
})}
{!isPublished &&
formatMessage({
id: getTrad('containers.Edit.information.draftVersion'),
defaultMessage: 'draft version',
})}
</Text>
</Box>
</Box>
</Box>
);
};
DraftAndPublishBadge.propTypes = {
hasDraftAndPublish: PropTypes.bool.isRequired,
isPublished: PropTypes.bool.isRequired,
};
export default connect(DraftAndPublishBadge, select);
export { DraftAndPublishBadge };

View File

@ -0,0 +1,225 @@
/**
*
* Tests for DraftAndPublishBadge
*
*/
/* eslint-disable no-irregular-whitespace */
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeProvider, lightTheme } from '@strapi/parts';
import { IntlProvider } from 'react-intl';
import { DraftAndPublishBadge } from '../index';
const makeApp = props => (
<ThemeProvider theme={lightTheme}>
<IntlProvider locale="en" messages={{}} defaultLocale="en">
<DraftAndPublishBadge {...props} />
</IntlProvider>
</ThemeProvider>
);
describe('<DraftAndPublishBadge />', () => {
it('renders and matches the snapshot', () => {
const App = makeApp({ hasDraftAndPublish: true, isPublished: true });
const {
container: { firstChild },
} = render(App);
expect(firstChild).toMatchInlineSnapshot(`
.c0 {
background: #eafbe7;
padding-top: 16px;
padding-bottom: 16px;
padding-left: 20px;
border-radius: 4px;
border: 1px solid #c6f0c2;
}
.c3 {
padding-left: 12px;
}
.c1 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c4 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #2f6846;
}
.c5 {
font-weight: 500;
font-size: 0.875rem;
line-height: 1.43;
color: #2f6846;
}
.c2 {
width: 0.375rem;
height: 0.375rem;
}
<aside
class="c0"
>
<div
class="c1 "
>
<svg
class="c2"
fill="none"
height="1em"
viewBox="0 0 4 4"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<rect
fill="#A5A5BA"
height="4"
rx="2"
width="4"
/>
</svg>
<div
class="c3"
>
<span
class="c4"
>
Editing
 
</span>
<span
class="c5"
>
published version
</span>
</div>
</div>
</aside>
`);
});
it('should show the draft design when it is not published', () => {
const App = makeApp({ hasDraftAndPublish: true, isPublished: false });
const {
container: { firstChild },
} = render(App);
expect(firstChild).toMatchInlineSnapshot(`
.c0 {
background: #eaf5ff;
padding-top: 16px;
padding-bottom: 16px;
padding-left: 20px;
border-radius: 4px;
border: 1px solid #b8e1ff;
}
.c3 {
padding-left: 12px;
}
.c1 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c4 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #006096;
}
.c5 {
font-weight: 500;
font-size: 0.875rem;
line-height: 1.43;
color: #006096;
}
.c2 {
width: 0.375rem;
height: 0.375rem;
}
.c2 * {
fill: #0c75af;
}
<aside
class="c0"
>
<div
class="c1 "
>
<svg
class="c2"
fill="none"
height="1em"
viewBox="0 0 4 4"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<rect
fill="#A5A5BA"
height="4"
rx="2"
width="4"
/>
</svg>
<div
class="c3"
>
<span
class="c4"
>
Editing
 
</span>
<span
class="c5"
>
draft version
</span>
</div>
</div>
</aside>
`);
});
it('should show return null when hasDraftAndPublish is falsy', () => {
const App = makeApp({ hasDraftAndPublish: false, isPublished: false });
const { queryByText } = render(App);
expect(queryByText('Editing')).not.toBeInTheDocument();
});
});

View File

@ -0,0 +1,12 @@
import React from 'react';
function connect(WrappedComponent, select) {
return function(props) {
// eslint-disable-next-line react/prop-types
const selectors = select();
return <WrappedComponent {...props} {...selectors} />;
};
}
export default connect;

View File

@ -0,0 +1,2 @@
export { default as connect } from './connect';
export { default as select } from './select';

View File

@ -0,0 +1,14 @@
import { useContentManagerEditViewDataManager } from '@strapi/helper-plugin';
function useSelect() {
const { initialData, hasDraftAndPublish } = useContentManagerEditViewDataManager();
const isPublished = initialData.published_at !== undefined && initialData.published_at !== null;
return {
hasDraftAndPublish,
isPublished,
};
}
export default useSelect;

View File

@ -1,23 +1,25 @@
import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
import React, { memo, useRef, useState } from 'react';
import { useIntl } from 'react-intl';
import { Header as PluginHeader } from '@buffetjs/custom';
import { get, isEqual, isEmpty, toString } from 'lodash';
import { useHistory } from 'react-router';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';
import isEmpty from 'lodash/isEmpty';
import BackIcon from '@strapi/icons/BackIcon';
import { HeaderLayout } from '@strapi/parts/Layout';
import { Box } from '@strapi/parts/Box';
import { Button } from '@strapi/parts/Button';
import { Dialog, DialogBody, DialogFooter } from '@strapi/parts/Dialog';
import { Link } from '@strapi/parts/Link';
import { Row } from '@strapi/parts/Row';
import { Text } from '@strapi/parts/Text';
import { Stack } from '@strapi/parts/Stack';
import AlertWarningIcon from '@strapi/icons/AlertWarningIcon';
import CheckIcon from '@strapi/icons/CheckIcon';
import PropTypes from 'prop-types';
import isEqualFastCompare from 'react-fast-compare';
import { Text } from '@buffetjs/core';
import { templateObject, ModalConfirm } from '@strapi/helper-plugin';
import { getTrad } from '../../../utils';
import { connect, getDraftRelations, select } from './utils';
const primaryButtonObject = {
color: 'primary',
type: 'button',
style: {
minWidth: 150,
fontWeight: 600,
},
};
const Header = ({
allowedActions: { canUpdate, canCreate, canPublish },
componentLayouts,
@ -31,205 +33,255 @@ const Header = ({
onUnpublish,
status,
}) => {
const { goBack } = useHistory();
const [showWarningUnpublish, setWarningUnpublish] = useState(false);
const { formatMessage } = useIntl();
const formatMessageRef = useRef(formatMessage);
const [draftRelationsCount, setDraftRelationsCount] = useState(0);
const [showWarningDraftRelation, setShowWarningDraftRelation] = useState(false);
const [shouldUnpublish, setShouldUnpublish] = useState(false);
const [shouldPublish, setShouldPublish] = useState(false);
const { formatMessage } = useIntl();
const draftRelationsCountRef = useRef(0);
const currentContentTypeMainField = useMemo(() => get(layout, ['settings', 'mainField'], 'id'), [
layout,
]);
const currentContentTypeMainField = get(layout, ['settings', 'mainField'], 'id');
const currentContentTypeName = get(layout, ['info', 'displayName'], 'NOT FOUND');
const didChangeData =
!isEqual(initialData, modifiedData) || (isCreatingEntry && !isEmpty(modifiedData));
const currentContentTypeName = useMemo(() => get(layout, ['info', 'name']), [layout]);
const createEntryIntlTitle = formatMessage({
id: getTrad('containers.Edit.pluginHeader.title.new'),
defaultMessage: 'Create an entry',
});
const didChangeData = useMemo(() => {
return !isEqual(initialData, modifiedData) || (isCreatingEntry && !isEmpty(modifiedData));
}, [initialData, isCreatingEntry, modifiedData]);
const apiID = useMemo(() => layout.apiID, [layout.apiID]);
let title = createEntryIntlTitle;
/* eslint-disable indent */
const entryHeaderTitle = isCreatingEntry
? formatMessage({
id: getTrad('containers.Edit.pluginHeader.title.new'),
})
: templateObject({ mainField: currentContentTypeMainField }, initialData).mainField;
/* eslint-enable indent */
if (!isCreatingEntry && !isSingleType) {
title = initialData[currentContentTypeMainField] || currentContentTypeName;
}
const headerTitle = useMemo(() => {
const title = isSingleType ? currentContentTypeName : entryHeaderTitle;
if (isSingleType) {
title = currentContentTypeName;
}
return title || currentContentTypeName;
}, [currentContentTypeName, entryHeaderTitle, isSingleType]);
const checkIfHasDraftRelations = useCallback(() => {
const checkIfHasDraftRelations = () => {
const count = getDraftRelations(modifiedData, layout, componentLayouts);
setDraftRelationsCount(count);
draftRelationsCountRef.current = count;
return count > 0;
}, [modifiedData, layout, componentLayouts]);
return count;
};
const headerActions = useMemo(() => {
let headerActions = [];
let primaryAction = null;
if ((isCreatingEntry && canCreate) || (!isCreatingEntry && canUpdate)) {
headerActions = [
{
disabled: !didChangeData,
color: 'success',
label: formatMessage({
id: getTrad('containers.Edit.submit'),
}),
isLoading: status === 'submit-pending',
type: 'submit',
style: {
minWidth: 150,
fontWeight: 600,
},
},
];
}
if (isCreatingEntry && canCreate) {
primaryAction = (
<Button disabled={!didChangeData} isLoading={status === 'submit-pending'} type="submit">
{formatMessage({
id: getTrad('containers.Edit.submit'),
defaultMessage: 'Save',
})}
</Button>
);
}
if (hasDraftAndPublish && canPublish) {
const isPublished = !isEmpty(initialData.published_at);
const isLoading = isPublished ? status === 'unpublish-pending' : status === 'publish-pending';
const labelID = isPublished ? 'app.utils.unpublish' : 'app.utils.publish';
/* eslint-disable indent */
const onClick = isPublished
? () => setWarningUnpublish(true)
: e => {
if (!checkIfHasDraftRelations()) {
onPublish(e);
} else {
setShowWarningDraftRelation(true);
}
};
/* eslint-enable indent */
if (!isCreatingEntry && canUpdate) {
const shouldShowPublishButton = hasDraftAndPublish && canPublish;
const isPublished = !isEmpty(initialData.published_at);
const isPublishButtonLoading = isPublished
? status === 'unpublish-pending'
: status === 'publish-pending';
const pubishButtonLabel = isPublished
? { id: 'app.utils.unpublish', defaultMessage: 'Unpublish' }
: { id: 'app.utils.publish', defaultMessage: 'Publish' };
const action = {
...primaryButtonObject,
disabled: isCreatingEntry || didChangeData,
isLoading,
label: formatMessage({ id: labelID }),
onClick,
};
/* eslint-disable indent */
const onClick = isPublished
? () => setWarningUnpublish(true)
: () => {
if (checkIfHasDraftRelations() === 0) {
onPublish();
} else {
setShowWarningDraftRelation(true);
}
};
/* eslint-enable indent */
headerActions.unshift(action);
}
primaryAction = (
<Row>
{shouldShowPublishButton && (
<Button
disabled={didChangeData}
loading={isPublishButtonLoading}
onClick={onClick}
startIcon={<CheckIcon />}
variant="secondary"
>
{formatMessage(pubishButtonLabel)}
</Button>
)}
<Box paddingLeft={shouldShowPublishButton ? 2 : 0}>
<Button disabled={!didChangeData} loading={status === 'submit-pending'} type="submit">
{formatMessage({
id: getTrad('containers.Edit.submit'),
defaultMessage: 'Save',
})}
</Button>
</Box>
</Row>
);
}
return headerActions;
}, [
isCreatingEntry,
canCreate,
canUpdate,
hasDraftAndPublish,
canPublish,
didChangeData,
formatMessage,
status,
initialData,
onPublish,
checkIfHasDraftRelations,
]);
const toggleWarningUnpublish = () => setWarningUnpublish(prevState => !prevState);
const toggleWarningDraftRelation = () => setShowWarningDraftRelation(prevState => !prevState);
const headerProps = useMemo(() => {
return {
title: {
label: toString(headerTitle),
},
content: `${formatMessageRef.current({ id: getTrad('api.id') })} : ${apiID}`,
actions: headerActions,
};
}, [headerActions, headerTitle, apiID]);
const handlePublish = () => {
toggleWarningDraftRelation();
draftRelationsCountRef.current = 0;
onPublish();
};
const toggleWarningPublish = () => setWarningUnpublish(prevState => !prevState);
const handleUnpublish = () => {
toggleWarningUnpublish();
onUnpublish();
};
const toggleWarningDraftRelation = useCallback(() => {
setShowWarningDraftRelation(prev => !prev);
}, []);
const handleConfirmPublish = useCallback(() => {
setShouldPublish(true);
setShowWarningDraftRelation(false);
}, []);
const handleConfirmUnpublish = useCallback(() => {
setShouldUnpublish(true);
setWarningUnpublish(false);
}, []);
const handleCloseModalPublish = useCallback(
e => {
if (shouldPublish) {
onPublish(e);
}
setShouldUnpublish(false);
},
[onPublish, shouldPublish]
);
const handleCloseModalUnpublish = useCallback(
e => {
if (shouldUnpublish) {
onUnpublish(e);
}
setShouldUnpublish(false);
},
[onUnpublish, shouldUnpublish]
);
const contentIdSuffix = draftRelationsCount > 1 ? 'plural' : 'singular';
const subtitle = `${formatMessage({
id: getTrad('api.id'),
defaultMessage: 'API ID ',
})} : ${layout.apiID}`;
return (
<>
<PluginHeader {...headerProps} />
{hasDraftAndPublish && (
<>
<ModalConfirm
isOpen={showWarningUnpublish}
toggle={toggleWarningPublish}
content={{
id: getTrad('popUpWarning.warning.unpublish'),
values: {
br: () => <br />,
},
<HeaderLayout
title={title}
primaryAction={primaryAction}
subtitle={subtitle}
navigationAction={
<Link
startIcon={<BackIcon />}
// Needed in order to redirect the user with the correct search params
// Since parts is using a link from react-router-dom the best way to do it is to disable the
// event
onClick={e => {
e.preventDefault();
goBack();
}}
type="xwarning"
onConfirm={handleConfirmUnpublish}
onClosed={handleCloseModalUnpublish}
to="/"
>
<Text>{formatMessage({ id: getTrad('popUpWarning.warning.unpublish-question') })}</Text>
</ModalConfirm>
<ModalConfirm
confirmButtonLabel={{
id: getTrad('popUpwarning.warning.has-draft-relations.button-confirm'),
}}
isOpen={showWarningDraftRelation}
toggle={toggleWarningDraftRelation}
onClosed={handleCloseModalPublish}
onConfirm={handleConfirmPublish}
type="success"
content={{
id: getTrad(`popUpwarning.warning.has-draft-relations.message.${contentIdSuffix}`),
values: {
count: draftRelationsCount,
b: chunks => (
<Text as="span" fontWeight="bold">
{chunks}
</Text>
),
br: () => <br />,
},
}}
>
<Text>{formatMessage({ id: getTrad('popUpWarning.warning.publish-question') })}</Text>
</ModalConfirm>
</>
{formatMessage({
id: 'app.components.HeaderLayout.link.go-back',
defaultMessage: 'Back',
})}
</Link>
}
/>
{showWarningUnpublish && (
<Dialog
onClose={toggleWarningUnpublish}
title="Confirmation"
labelledBy="confirmation"
describedBy="confirm-description"
isOpen={showWarningUnpublish}
>
<DialogBody icon={<AlertWarningIcon />}>
<Stack size={2}>
<Row justifyContent="center" style={{ textAlign: 'center' }}>
<Text id="confirm-description">
{formatMessage(
{
id: getTrad('popUpWarning.warning.unpublish'),
defaultMessage:
'Unpublish this content will automatically change it to a draft.',
},
{
br: () => <br />,
}
)}
</Text>
</Row>
<Row justifyContent="center" style={{ textAlign: 'center' }}>
<Text id="confirm-description">
{formatMessage({
id: getTrad('popUpWarning.warning.unpublish-question'),
defaultMessage: 'Are you sure you want to unpublish it?',
})}
</Text>
</Row>
</Stack>
</DialogBody>
<DialogFooter
startAction={
<Button onClick={toggleWarningUnpublish} variant="tertiary">
{formatMessage({
id: 'components.popUpWarning.button.cancel',
defaultMessage: 'No, cancel',
})}
</Button>
}
endAction={
<Button variant="danger-light" onClick={handleUnpublish}>
{formatMessage({
id: 'components.popUpWarning.button.confirm',
defaultMessage: 'Yes, confirm',
})}
</Button>
}
/>
</Dialog>
)}
{showWarningDraftRelation && (
<Dialog
onClose={toggleWarningDraftRelation}
title="Confirmation"
labelledBy="confirmation"
describedBy="confirm-description"
isOpen={showWarningDraftRelation}
>
<DialogBody icon={<AlertWarningIcon />}>
<Stack size={2}>
<Row justifyContent="center" style={{ textAlign: 'center' }}>
<Text id="confirm-description">
{draftRelationsCountRef.current}
{formatMessage(
{
id: getTrad(`popUpwarning.warning.has-draft-relations.message`),
defaultMessage:
'<b>{count, plural, =0 { of your content relations is} one { of your content relations is} other { of your content relations are}}</b> not published yet.<br></br>It might engender broken links and errors on your project.',
},
{
br: () => <br />,
b: chunks => <Text bold>{chunks}</Text>,
count: draftRelationsCountRef.current,
}
)}
</Text>
</Row>
<Row justifyContent="center" style={{ textAlign: 'center' }}>
<Text id="confirm-description">
{formatMessage({
id: getTrad('popUpWarning.warning.publish-question'),
defaultMessage: 'Do you still want to publish it?',
})}
</Text>
</Row>
</Stack>
</DialogBody>
<DialogFooter
startAction={
<Button onClick={toggleWarningDraftRelation} variant="tertiary">
{formatMessage({
id: 'components.popUpWarning.button.cancel',
defaultMessage: 'No, cancel',
})}
</Button>
}
endAction={
<Button variant="success" onClick={handlePublish}>
{formatMessage({
id: getTrad('popUpwarning.warning.has-draft-relations.button-confirm'),
defaultMessage: 'Yes, publish',
})}
</Button>
}
/>
</Dialog>
)}
</>
);
@ -256,3 +308,4 @@ Header.propTypes = {
const Memoized = memo(Header, isEqualFastCompare);
export default connect(Memoized, select);
export { Header };

View File

@ -0,0 +1,322 @@
/**
*
* Tests for Header
*
*/
import React from 'react';
import { render } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import { MemoryRouter } from 'react-router-dom';
import Theme from '../../../../../components/Theme';
import { Header } from '../index';
import components from '../utils/tests/data/compos-schema.json';
import ct from '../utils/tests/data/ct-schema.json';
const defaultProps = {
allowedActions: { canUpdate: true, canCreate: true, canPublish: true },
componentLayouts: components,
initialData: {},
isCreatingEntry: true,
isSingleType: false,
hasDraftAndPublish: false,
layout: ct,
modifiedData: {},
onPublish: jest.fn(),
onUnpublish: jest.fn(),
status: 'resolved',
};
const makeApp = (props = defaultProps) => {
return (
<MemoryRouter>
<IntlProvider locale="en" defaultLocale="en" messages={{}}>
<Theme>
<Header {...props} />
</Theme>
</IntlProvider>
</MemoryRouter>
);
};
describe('CONTENT MANAGER | EditView | Header', () => {
it('renders and matches the snapshot', () => {
const {
container: { firstChild },
} = render(makeApp());
expect(firstChild).toMatchInlineSnapshot(`
.c0 {
background: #f6f6f9;
padding-top: 24px;
padding-right: 56px;
padding-bottom: 56px;
padding-left: 56px;
}
.c1 {
padding-bottom: 12px;
}
.c8 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c9 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.c10 {
font-weight: 600;
font-size: 2rem;
line-height: 1.25;
color: #32324d;
}
.c15 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #666687;
}
.c16 {
font-size: 1rem;
line-height: 1.5;
}
.c14 {
font-weight: 500;
font-size: 0.75rem;
line-height: 1.33;
color: #32324d;
}
.c11 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
cursor: pointer;
padding: 8px;
border-radius: 4px;
background: #ffffff;
border: 1px solid #dcdce4;
}
.c11 svg {
height: 12px;
width: 12px;
}
.c11 svg > g,
.c11 svg path {
fill: #ffffff;
}
.c11[aria-disabled='true'] {
pointer-events: none;
}
.c12 {
padding: 8px 16px;
background: #4945ff;
border: none;
border: 1px solid #4945ff;
background: #4945ff;
}
.c12 .c13 {
color: #ffffff;
}
.c12[aria-disabled='true'] {
border: 1px solid #dcdce4;
background: #eaeaef;
}
.c12[aria-disabled='true'] .c13 {
color: #666687;
}
.c12[aria-disabled='true'] svg > g,
.c12[aria-disabled='true'] svg path {
fill: #666687;
}
.c12[aria-disabled='true']:active {
border: 1px solid #dcdce4;
background: #eaeaef;
}
.c12[aria-disabled='true']:active .c13 {
color: #666687;
}
.c12[aria-disabled='true']:active svg > g,
.c12[aria-disabled='true']:active svg path {
fill: #666687;
}
.c12:hover {
border: 1px solid #7b79ff;
background: #7b79ff;
}
.c12:active {
border: 1px solid #4945ff;
background: #4945ff;
}
.c5 {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.43;
color: #4945ff;
}
.c6 {
font-weight: 600;
line-height: 1.14;
}
.c7 {
font-weight: 600;
font-size: 0.6875rem;
line-height: 1.45;
text-transform: uppercase;
}
.c3 {
padding-right: 8px;
}
.c2 {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-transform: uppercase;
-webkit-text-decoration: none;
text-decoration: none;
}
.c2 svg path {
fill: #4945ff;
}
.c2 svg {
font-size: 0.625rem;
}
.c4 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
<div
class=""
style="height: 0px;"
>
<div
class="c0"
data-strapi-header="true"
>
<div
class="c1"
>
<a
aria-current="page"
class="c2 active"
href="/"
>
<span
aria-hidden="true"
class="c3 c4"
>
<svg
fill="none"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M24 13.3a.2.2 0 01-.2.2H5.74l8.239 8.239a.2.2 0 010 .282L12.14 23.86a.2.2 0 01-.282 0L.14 12.14a.2.2 0 010-.282L11.86.14a.2.2 0 01.282 0L13.98 1.98a.2.2 0 010 .282L5.74 10.5H23.8c.11 0 .2.09.2.2v2.6z"
fill="#212134"
/>
</svg>
</span>
<span
class="c5 c6 c7"
>
Back
</span>
</a>
</div>
<div
class="c8"
>
<div
class="c9"
>
<h1
class="c10"
id="main-content-title"
>
Create an entry
</h1>
</div>
<button
aria-disabled="true"
class="c11 c12"
disabled=""
type="submit"
>
<span
class="c13 c14"
>
Save
</span>
</button>
</div>
<p
class="c15 c16"
>
API ID : restaurant
</p>
</div>
</div>
`);
});
});

View File

@ -1,7 +1,21 @@
// import React, { memo, useCallback, useMemo } from 'react';
// import PropTypes from 'prop-types';
// import { get } from 'lodash';
// import { BaselineAlignment, LiLink, CheckPermissions, useTracking } from '@strapi/helper-plugin';
import React, {
memo,
// useCallback,
useMemo,
} from 'react';
import PropTypes from 'prop-types';
// import get from 'lodash/get';
// import // BaselineAlignment,
// LiLink,
// CheckPermissions,
// useTracking,
// '@strapi/helper-plugin';
import { ContentLayout } from '@strapi/parts/Layout';
import { Box } from '@strapi/parts/Box';
import { Grid, GridItem } from '@strapi/parts/Grid';
import { Main } from '@strapi/parts/Main';
import { Stack } from '@strapi/parts/Stack';
// import { Padded } from '@buffetjs/core';
// import { InjectionZone } from '../../../shared/components';
// import permissions from '../../../permissions';
@ -11,12 +25,15 @@
// import FieldComponent from '../../components/FieldComponent';
// import Inputs from '../../components/Inputs';
// import SelectWrapper from '../../components/SelectWrapper';
// import CollectionTypeFormWrapper from '../../components/CollectionTypeFormWrapper';
// import EditViewDataManagerProvider from '../../components/EditViewDataManagerProvider';
// import SingleTypeFormWrapper from '../../components/SingleTypeFormWrapper';
// import BackHeader from '../../components/BackHeader';
// import Header from './Header';
// import { createAttributesLayout, getFieldsActionMatchingPermissions } from './utils';
import CollectionTypeFormWrapper from '../../components/CollectionTypeFormWrapper';
import EditViewDataManagerProvider from '../../components/EditViewDataManagerProvider';
import SingleTypeFormWrapper from '../../components/SingleTypeFormWrapper';
import DraftAndPublishBadge from './DraftAndPublishBadge';
import Header from './Header';
import {
// createAttributesLayout,
getFieldsActionMatchingPermissions,
} from './utils';
// import { LinkWrapper, SubWrapper } from './components';
// import DeleteLink from './DeleteLink';
// import InformationCard from './InformationCard';
@ -26,282 +43,363 @@
// 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 { trackUsage } = useTracking();
// const {
// createActionAllowedFields,
// readActionAllowedFields,
// updateActionAllowedFields,
// } = useMemo(() => {
// return getFieldsActionMatchingPermissions(userPermissions, slug);
// }, [userPermissions, slug]);
// const configurationPermissions = useMemo(() => {
// return isSingleType
// ? cmPermissions.singleTypesConfigurations
// : cmPermissions.collectionTypesConfigurations;
// }, [isSingleType]);
const EditView = ({
allowedActions,
isSingleType,
goBack,
layout,
slug,
id,
origin,
userPermissions,
}) => {
// const { trackUsage } = useTracking();
const {
createActionAllowedFields,
readActionAllowedFields,
updateActionAllowedFields,
} = useMemo(() => {
return getFieldsActionMatchingPermissions(userPermissions, slug);
}, [userPermissions, slug]);
// const configurationPermissions = useMemo(() => {
// return isSingleType
// ? cmPermissions.singleTypesConfigurations
// : cmPermissions.collectionTypesConfigurations;
// }, [isSingleType]);
// // FIXME when changing the routing
// const configurationsURL = `/content-manager/${
// isSingleType ? 'singleType' : 'collectionType'
// }/${slug}/configurations/edit`;
// const currentContentTypeLayoutData = useMemo(() => get(layout, ['contentType'], {}), [layout]);
// 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 = useMemo(
() => (isSingleType ? SingleTypeFormWrapper : CollectionTypeFormWrapper),
[isSingleType]
);
// // Check if a block is a dynamic zone
// const isDynamicZone = useCallback(block => {
// return block.every(subBlock => {
// return subBlock.every(obj => obj.fieldSchema.type === 'dynamiczone');
// });
// }, []);
// Check if a block is a dynamic zone
// const isDynamicZone = useCallback(block => {
// return block.every(subBlock => {
// return subBlock.every(obj => obj.fieldSchema.type === 'dynamiczone');
// });
// }, []);
// const formattedContentTypeLayout = useMemo(() => {
// if (!currentContentTypeLayoutData.layouts) {
// return [];
// }
// const formattedContentTypeLayout = useMemo(() => {
// if (!currentContentTypeLayoutData.layouts) {
// return [];
// }
// return createAttributesLayout(
// currentContentTypeLayoutData.layouts.edit,
// currentContentTypeLayoutData.attributes
// );
// }, [currentContentTypeLayoutData]);
// return createAttributesLayout(
// currentContentTypeLayoutData.layouts.edit,
// currentContentTypeLayoutData.attributes
// );
// }, [currentContentTypeLayoutData]);
// return (
// <DataManagementWrapper allLayoutData={layout} slug={slug} id={id} origin={origin}>
// {({
// componentsDataStructure,
// contentTypeDataStructure,
// data,
// isCreatingEntry,
// isLoadingForData,
// onDelete,
// onDeleteSucceeded,
// onPost,
// onPublish,
// onPut,
// onUnpublish,
// redirectionLink,
// status,
// }) => {
// return (
// <EditViewDataManagerProvider
// allowedActions={allowedActions}
// allLayoutData={layout}
// createActionAllowedFields={createActionAllowedFields}
// componentsDataStructure={componentsDataStructure}
// contentTypeDataStructure={contentTypeDataStructure}
// from={redirectionLink}
// initialValues={data}
// isCreatingEntry={isCreatingEntry}
// isLoadingForData={isLoadingForData}
// isSingleType={isSingleType}
// onPost={onPost}
// onPublish={onPublish}
// onPut={onPut}
// onUnpublish={onUnpublish}
// readActionAllowedFields={readActionAllowedFields}
// redirectToPreviousPage={goBack}
// slug={slug}
// status={status}
// updateActionAllowedFields={updateActionAllowedFields}
// >
// <BackHeader onClick={goBack} />
// <Container className="container-fluid">
// <Header allowedActions={allowedActions} />
// <div className="row" style={{ paddingTop: 3 }}>
// <div className="col-md-12 col-lg-9" style={{ marginBottom: 13 }}>
// {formattedContentTypeLayout.map((block, blockIndex) => {
// if (isDynamicZone(block)) {
// const {
// 0: {
// 0: { name, fieldSchema, metadatas, labelIcon },
// },
// } = block;
// const baselineAlignementSize = blockIndex === 0 ? '3px' : '0';
return (
<DataManagementWrapper allLayoutData={layout} slug={slug} id={id} origin={origin}>
{({
componentsDataStructure,
contentTypeDataStructure,
data,
isCreatingEntry,
isLoadingForData,
// onDelete,
// onDeleteSucceeded,
onPost,
onPublish,
onPut,
onUnpublish,
redirectionLink,
status,
}) => {
return (
<EditViewDataManagerProvider
allowedActions={allowedActions}
allLayoutData={layout}
createActionAllowedFields={createActionAllowedFields}
componentsDataStructure={componentsDataStructure}
contentTypeDataStructure={contentTypeDataStructure}
from={redirectionLink}
initialValues={data}
isCreatingEntry={isCreatingEntry}
isLoadingForData={isLoadingForData}
isSingleType={isSingleType}
onPost={onPost}
onPublish={onPublish}
onPut={onPut}
onUnpublish={onUnpublish}
readActionAllowedFields={readActionAllowedFields}
redirectToPreviousPage={goBack}
slug={slug}
status={status}
updateActionAllowedFields={updateActionAllowedFields}
>
<Main aria-busy={status !== 'resolved'}>
<Header allowedActions={allowedActions} />
<ContentLayout>
<Grid gap={4}>
<GridItem col={9} s={12}>
<Box
hasRadius
background="neutral0"
shadow="tableShadow"
paddingLeft={8}
paddingRight={8}
paddingTop={6}
paddingBottom={6}
>
inputs TODO
</Box>
</GridItem>
<GridItem col={3} s={12}>
<Stack size={2}>
<DraftAndPublishBadge />
<Box
as="aside"
background="neutral0"
borderColor="neutral150"
hasRadius
paddingBottom={4}
paddingLeft={4}
paddingRight={4}
paddingTop={6}
>
infos + InjectionZone
</Box>
</Stack>
</GridItem>
</Grid>
</ContentLayout>
</Main>
</EditViewDataManagerProvider>
);
}}
</DataManagementWrapper>
);
// return (
// <BaselineAlignment key={blockIndex} top size={baselineAlignementSize}>
// <DynamicZone
// name={name}
// fieldSchema={fieldSchema}
// labelIcon={labelIcon}
// metadatas={metadatas}
// />
// </BaselineAlignment>
// );
// }
// return (
// <DataManagementWrapper allLayoutData={layout} slug={slug} id={id} origin={origin}>
// {({
// componentsDataStructure,
// contentTypeDataStructure,
// data,
// isCreatingEntry,
// isLoadingForData,
// onDelete,
// onDeleteSucceeded,
// onPost,
// onPublish,
// onPut,
// onUnpublish,
// redirectionLink,
// status,
// }) => {
// return (
// <EditViewDataManagerProvider
// allowedActions={allowedActions}
// allLayoutData={layout}
// createActionAllowedFields={createActionAllowedFields}
// componentsDataStructure={componentsDataStructure}
// contentTypeDataStructure={contentTypeDataStructure}
// from={redirectionLink}
// initialValues={data}
// isCreatingEntry={isCreatingEntry}
// isLoadingForData={isLoadingForData}
// isSingleType={isSingleType}
// onPost={onPost}
// onPublish={onPublish}
// onPut={onPut}
// onUnpublish={onUnpublish}
// readActionAllowedFields={readActionAllowedFields}
// redirectToPreviousPage={goBack}
// slug={slug}
// status={status}
// updateActionAllowedFields={updateActionAllowedFields}
// >
// <Container className="container-fluid">
// <Header allowedActions={allowedActions} />
// <div className="row" style={{ paddingTop: 3 }}>
// <div className="col-md-12 col-lg-9" style={{ marginBottom: 13 }}>
// {formattedContentTypeLayout.map((block, blockIndex) => {
// if (isDynamicZone(block)) {
// const {
// 0: {
// 0: { name, fieldSchema, metadatas, labelIcon },
// },
// } = block;
// const baselineAlignementSize = blockIndex === 0 ? '3px' : '0';
// return (
// <FormWrapper key={blockIndex}>
// {block.map((fieldsBlock, fieldsBlockIndex) => {
// return (
// <div className="row" key={fieldsBlockIndex}>
// {fieldsBlock.map(
// ({ name, size, fieldSchema, labelIcon, metadatas }, fieldIndex) => {
// const isComponent = fieldSchema.type === 'component';
// return (
// <BaselineAlignment key={blockIndex} top size={baselineAlignementSize}>
// <DynamicZone
// name={name}
// fieldSchema={fieldSchema}
// labelIcon={labelIcon}
// metadatas={metadatas}
// />
// </BaselineAlignment>
// );
// }
// if (isComponent) {
// const { component, max, min, repeatable = false } = fieldSchema;
// const componentUid = fieldSchema.component;
// return (
// <FormWrapper key={blockIndex}>
// {block.map((fieldsBlock, fieldsBlockIndex) => {
// return (
// <div className="row" key={fieldsBlockIndex}>
// {fieldsBlock.map(
// ({ name, size, fieldSchema, labelIcon, metadatas }, fieldIndex) => {
// const isComponent = fieldSchema.type === 'component';
// return (
// <FieldComponent
// key={componentUid}
// componentUid={component}
// labelIcon={labelIcon}
// isRepeatable={repeatable}
// label={metadatas.label}
// max={max}
// min={min}
// name={name}
// />
// );
// }
// if (isComponent) {
// const { component, max, min, repeatable = false } = fieldSchema;
// const componentUid = fieldSchema.component;
// return (
// <div className={`col-${size}`} key={name}>
// <Inputs
// autoFocus={
// blockIndex === 0 &&
// fieldsBlockIndex === 0 &&
// fieldIndex === 0
// }
// fieldSchema={fieldSchema}
// keys={name}
// labelIcon={labelIcon}
// metadatas={metadatas}
// />
// </div>
// );
// }
// )}
// </div>
// );
// })}
// </FormWrapper>
// );
// })}
// </div>
// <div className="col-md-12 col-lg-3">
// <InformationCard />
// <Padded size="smd" top />
// {currentContentTypeLayoutData.layouts.editRelations.length > 0 && (
// <SubWrapper style={{ padding: '0 20px 1px', marginBottom: '25px' }}>
// <div style={{ paddingTop: '22px' }}>
// {currentContentTypeLayoutData.layouts.editRelations.map(
// ({ name, fieldSchema, labelIcon, metadatas, queryInfos }) => {
// return (
// <SelectWrapper
// {...fieldSchema}
// {...metadatas}
// key={name}
// labelIcon={labelIcon}
// name={name}
// relationsType={fieldSchema.relationType}
// queryInfos={queryInfos}
// />
// );
// }
// )}
// </div>
// </SubWrapper>
// )}
// <LinkWrapper>
// <ul>
// <CheckPermissions permissions={configurationPermissions}>
// <LiLink
// message={{
// id: 'app.links.configure-view',
// }}
// icon="layout"
// url={configurationsURL}
// onClick={() => {
// // trackUsage('willEditContentTypeLayoutFromEditView');
// }}
// />
// </CheckPermissions>
// {slug !== 'strapi::administrator' && (
// <CheckPermissions permissions={ctbPermissions}>
// <LiLink
// message={{
// id: getTrad('containers.Edit.Link.Fields'),
// }}
// onClick={() => {
// trackUsage('willEditEditLayout');
// }}
// icon="fa-cog"
// url={`/plugins/content-type-builder/content-types/${slug}`}
// />
// </CheckPermissions>
// )}
// {/* TODO add DOCUMENTATION */}
// <InjectionZone area="contentManager.editView.right-links" slug={slug} />
// return (
// <FieldComponent
// key={componentUid}
// componentUid={component}
// labelIcon={labelIcon}
// isRepeatable={repeatable}
// label={metadatas.label}
// max={max}
// min={min}
// name={name}
// />
// );
// }
// {allowedActions.canDelete && (
// <DeleteLink
// isCreatingEntry={isCreatingEntry}
// onDelete={onDelete}
// onDeleteSucceeded={onDeleteSucceeded}
// />
// )}
// </ul>
// </LinkWrapper>
// </div>
// </div>
// </Container>
// </EditViewDataManagerProvider>
// );
// }}
// </DataManagementWrapper>
// );
// };
// return (
// <div className={`col-${size}`} key={name}>
// <Inputs
// autoFocus={
// blockIndex === 0 &&
// fieldsBlockIndex === 0 &&
// fieldIndex === 0
// }
// fieldSchema={fieldSchema}
// keys={name}
// labelIcon={labelIcon}
// metadatas={metadatas}
// />
// </div>
// );
// }
// )}
// </div>
// );
// })}
// </FormWrapper>
// );
// })}
// </div>
// <div className="col-md-12 col-lg-3">
// <InformationCard />
// <Padded size="smd" top />
// {currentContentTypeLayoutData.layouts.editRelations.length > 0 && (
// <SubWrapper style={{ padding: '0 20px 1px', marginBottom: '25px' }}>
// <div style={{ paddingTop: '22px' }}>
// {currentContentTypeLayoutData.layouts.editRelations.map(
// ({ name, fieldSchema, labelIcon, metadatas, queryInfos }) => {
// return (
// <SelectWrapper
// {...fieldSchema}
// {...metadatas}
// key={name}
// labelIcon={labelIcon}
// name={name}
// relationsType={fieldSchema.relationType}
// queryInfos={queryInfos}
// />
// );
// }
// )}
// </div>
// </SubWrapper>
// )}
// <LinkWrapper>
// <ul>
// <CheckPermissions permissions={configurationPermissions}>
// <LiLink
// message={{
// id: 'app.links.configure-view',
// }}
// icon="layout"
// url={configurationsURL}
// onClick={() => {
// // trackUsage('willEditContentTypeLayoutFromEditView');
// }}
// />
// </CheckPermissions>
// {slug !== 'strapi::administrator' && (
// <CheckPermissions permissions={ctbPermissions}>
// <LiLink
// message={{
// id: getTrad('containers.Edit.Link.Fields'),
// }}
// onClick={() => {
// trackUsage('willEditEditLayout');
// }}
// icon="fa-cog"
// url={`/plugins/content-type-builder/content-types/${slug}`}
// />
// </CheckPermissions>
// )}
// {/* TODO add DOCUMENTATION */}
// <InjectionZone area="contentManager.editView.right-links" slug={slug} />
// EditView.defaultProps = {
// id: null,
// isSingleType: false,
// origin: null,
// userPermissions: [],
// };
// {allowedActions.canDelete && (
// <DeleteLink
// isCreatingEntry={isCreatingEntry}
// onDelete={onDelete}
// onDeleteSucceeded={onDeleteSucceeded}
// />
// )}
// </ul>
// </LinkWrapper>
// </div>
// </div>
// </Container>
// </EditViewDataManagerProvider>
// );
// }}
// </DataManagementWrapper>
// );
};
// EditView.propTypes = {
// allowedActions: PropTypes.shape({
// canRead: PropTypes.bool.isRequired,
// canUpdate: PropTypes.bool.isRequired,
// 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,
// origin: PropTypes.string,
// slug: PropTypes.string.isRequired,
// userPermissions: PropTypes.array,
// };
EditView.defaultProps = {
id: null,
isSingleType: false,
origin: null,
userPermissions: [],
};
// export { EditView };
// export default memo(EditView);
EditView.propTypes = {
allowedActions: PropTypes.shape({
canRead: PropTypes.bool.isRequired,
canUpdate: PropTypes.bool.isRequired,
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,
origin: PropTypes.string,
slug: PropTypes.string.isRequired,
userPermissions: PropTypes.array,
};
export default () => 'TODO Edit view';
export { EditView };
export default memo(EditView);
// export default () => 'TODO Edit view';

View File

@ -211,6 +211,7 @@
"Users.components.List.empty.withFilters": "There is no users with the applied filters...",
"Users.components.List.empty.withSearch": "There is no users corresponding to the search ({search})...",
"app.components.go-back": "Go back",
"app.components.HeaderLayout.link.go-back": "Back",
"app.components.ToggleCheckbox.on-label": "On",
"app.components.ToggleCheckbox.off-label": "Off",
"app.components.BlockLink.blog": "Blog",
@ -594,9 +595,7 @@
"content-manager.popUpWarning.warning.unpublish-question": "Are you sure you want to unpublish it?",
"content-manager.popUpWarning.warning.updateAllSettings": "This will modify all your settings",
"content-manager.popUpwarning.warning.has-draft-relations.button-confirm": "Yes, publish",
"content-manager.popUpwarning.warning.has-draft-relations.message.plural": "<b>{count} of your content relations are</b> not published yet.<br></br>It might engender broken links and errors on your project.",
"content-manager.popUpwarning.warning.has-draft-relations.message.singular": "<b>{count} of your content relations is</b> not published yet.<br></br>It might engender broken links and errors on your project.",
"content-manager.popUpwarning.warning.has-draft-relations.second-message": "It might engender broken links and errors on your project.",
"content-manager.popUpwarning.warning.has-draft-relations.message": "<b>{count, plural, =0 { of your content relations is} one { of your content relations is} other { of your content relations are}}</b> not published yet.<br></br>It might engender broken links and errors on your project.",
"content-manager.success.record.delete": "Deleted",
"content-manager.success.record.publish": "Published",
"content-manager.success.record.save": "Saved",

View File

@ -6,6 +6,7 @@
import React from 'react';
import { render {{~#if useRedux}} as tlRender {{~/if}} } from '@testing-library/react';
import { ThemeProvider, lightTheme } from '@strapi/parts';
{{#if useRedux}}
import { Provider } from 'react-redux';
import { createStore, combineReducers } from 'redux';
@ -49,11 +50,15 @@ describe('<{{name}} />', () => {
container: { firstChild },
} = render(
{{#if useI18n}}
<IntlProvider locale="en" messages={messages} defaultLocale="en">
<{{name}} />
</IntlProvider>,
<ThemeProvider theme={lightTheme}>
<IntlProvider locale="en" messages={messages} defaultLocale="en">
<{{name}} />
</IntlProvider>
</ThemeProvider>
{{else}}
<{{name}} />
<ThemeProvider theme={lightTheme}>
<{{name}} />
</ThemeProvider>
{{/if}}
);