Merge pull request #17047 from strapi/chore/rw-ee_else_ce

Chore: Refactor CM list-view review-workflow columns to use useEnterprise
This commit is contained in:
Gustav Hansen 2023-07-06 14:24:54 +02:00 committed by GitHub
commit e47c94e31b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 179 additions and 122 deletions

View File

@ -54,3 +54,8 @@ if (!Component) {
return;
}
```
### `enabled`
Similar to react-query this boolean flag allows disabling the EE import, e.g. when more than one condition needs to be applied. If `enabled`
is set to false, the first argument (CE_DATA) will be returned.

View File

@ -1,6 +1,16 @@
import React from 'react';
import { BaseCheckbox, IconButton, Tbody, Td, Tr, Flex } from '@strapi/design-system';
import {
BaseCheckbox,
IconButton,
Tbody,
Td,
Tr,
Flex,
lightTheme,
Status,
Typography,
} from '@strapi/design-system';
import { useTracking, useFetchClient, useAPIErrorHandler } from '@strapi/helper-plugin';
import { Trash, Duplicate, Pencil } from '@strapi/icons';
import { AxiosError } from 'axios';
@ -8,15 +18,19 @@ import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';
import { Link, useHistory } from 'react-router-dom';
import { useEnterprise } from '../../../../../hooks/useEnterprise';
import { getFullName } from '../../../../../utils';
import { usePluginsQueryParams } from '../../../../hooks';
import { getTrad } from '../../../../utils';
import CellContent from '../CellContent';
const REVIEW_WORKFLOW_COLUMNS_CE = () => null;
export const TableRows = ({
canCreate,
canDelete,
contentType,
features: { hasDraftAndPublish, hasReviewWorkflows },
headers,
entriesToDelete,
onClickDelete,
@ -33,6 +47,18 @@ export const TableRows = ({
const { trackUsage } = useTracking();
const pluginsQueryParams = usePluginsQueryParams();
const { formatAPIError } = useAPIErrorHandler(getTrad);
const ReviewWorkflowsStage = useEnterprise(
REVIEW_WORKFLOW_COLUMNS_CE,
async () =>
(
await import(
'../../../../../../../ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn'
)
).ReviewWorkflowsStageEE,
{
enabled: hasReviewWorkflows,
}
);
/**
*
@ -74,6 +100,11 @@ export const TableRows = ({
}
};
// block rendering until the review stage component is fully loaded in EE
if (!ReviewWorkflowsStage) {
return null;
}
/**
* Table Cells with actions e.g edit, delete, duplicate have `stopPropagation`
* to prevent the row from being selected.
@ -113,20 +144,59 @@ export const TableRows = ({
/>
</Td>
)}
{headers.map(({ key, cellFormatter, name, ...rest }) => {
if (hasDraftAndPublish && name === 'publishedAt') {
return (
<Td key={key}>
<Status
width="min-content"
showBullet={false}
variant={data.publishedAt ? 'success' : 'secondary'}
size="S"
>
<Typography
fontWeight="bold"
textColor={`${data.publishedAt ? 'success' : 'secondary'}700`}
>
{formatMessage({
id: getTrad(
`containers.List.${data.publishedAt ? 'published' : 'draft'}`
),
defaultMessage: data.publishedAt ? 'Published' : 'Draft',
})}
</Typography>
</Status>
</Td>
);
}
if (hasReviewWorkflows && name === 'strapi_reviewWorkflows_stage') {
return (
<Td key={key}>
{data.strapi_reviewWorkflows_stage ? (
<ReviewWorkflowsStage
color={
data.strapi_reviewWorkflows_stage.color ?? lightTheme.colors.primary600
}
name={data.strapi_reviewWorkflows_stage.name}
/>
) : (
<Typography textColor="neutral800">-</Typography>
)}
</Td>
);
}
return (
<Td key={key}>
{typeof cellFormatter === 'function' ? (
cellFormatter(data, { key, name, ...rest })
) : (
<CellContent
content={data[name.split('.')[0]]}
name={name}
contentType={contentType}
{...rest}
rowId={data.id}
/>
)}
<CellContent
content={data[name.split('.')[0]]}
name={name}
contentType={contentType}
{...rest}
rowId={data.id}
/>
</Td>
);
})}
@ -212,6 +282,10 @@ TableRows.propTypes = {
uid: PropTypes.string.isRequired,
}).isRequired,
entriesToDelete: PropTypes.array,
features: PropTypes.shape({
hasDraftAndPublish: PropTypes.bool.isRequired,
hasReviewWorkflows: PropTypes.bool.isRequired,
}).isRequired,
headers: PropTypes.array.isRequired,
onClickDelete: PropTypes.func,
onSelectRow: PropTypes.func,

View File

@ -10,8 +10,6 @@ import {
HeaderLayout,
useNotifyAT,
Flex,
Typography,
Status,
} from '@strapi/design-system';
import {
NoPermissions,
@ -33,7 +31,6 @@ import {
} from '@strapi/helper-plugin';
import { ArrowLeft, Cog, Plus } from '@strapi/icons';
import axios from 'axios';
import getReviewWorkflowsColumn from 'ee_else_ce/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn';
import isEqual from 'lodash/isEqual';
import PropTypes from 'prop-types';
import { stringify } from 'qs';
@ -45,6 +42,7 @@ import { bindActionCreators, compose } from 'redux';
import styled from 'styled-components';
import { INJECT_COLUMN_IN_TABLE } from '../../../exposedHooks';
import { useEnterprise } from '../../../hooks/useEnterprise';
import { selectAdminPermissions } from '../../../pages/App/selectors';
import { InjectionZone } from '../../../shared/components';
import AttributeFilter from '../../components/AttributeFilter';
@ -67,6 +65,8 @@ const ConfigureLayoutBox = styled(Box)`
}
`;
const REVIEW_WORKFLOW_COLUMNS_CE = null;
function ListView({
canCreate,
canDelete,
@ -107,8 +107,24 @@ function ListView({
const { pathname } = useLocation();
const { push } = useHistory();
const { formatMessage } = useIntl();
const hasDraftAndPublish = options?.draftAndPublish || false;
const fetchClient = useFetchClient();
const hasDraftAndPublish = options?.draftAndPublish ?? false;
const hasReviewWorkflows = options?.reviewWorkflows ?? false;
const reviewWorkflowColumns = useEnterprise(
REVIEW_WORKFLOW_COLUMNS_CE,
async () =>
(
await import(
'../../../../../ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants'
)
).REVIEW_WORKFLOW_COLUMNS_EE,
{
enabled: !!options?.reviewWorkflows,
}
);
const { post, del } = fetchClient;
const bulkPublishMutation = useMutation(
@ -388,24 +404,8 @@ function ListView({
};
});
if (!hasDraftAndPublish) {
return formattedHeaders;
}
// this should not exist. Ideally we would use registerHook() similar to what has been done
// in the i18n plugin. In order to do that review-workflows should have been a plugin. In
// a future iteration we need to find a better pattern.
// In CE this will return null - in EE a column definition including the custom formatting component.
const reviewWorkflowColumn = getReviewWorkflowsColumn(layout);
if (reviewWorkflowColumn) {
formattedHeaders.push(reviewWorkflowColumn);
}
return [
...formattedHeaders,
{
if (hasDraftAndPublish) {
formattedHeaders.push({
key: '__published_at_temp_key__',
name: 'publishedAt',
fieldSchema: {
@ -419,25 +419,29 @@ function ListView({
searchable: false,
sortable: true,
},
// eslint-disable-next-line react/no-unstable-nested-components
cellFormatter(cellData) {
const isPublished = cellData.publishedAt;
const variant = isPublished ? 'success' : 'secondary';
});
}
return (
<Status width="min-content" showBullet={false} variant={variant} size="S">
<Typography fontWeight="bold" textColor={`${variant}700`}>
{formatMessage({
id: getTrad(`containers.List.${isPublished ? 'published' : 'draft'}`),
defaultMessage: isPublished ? 'Published' : 'Draft',
})}
</Typography>
</Status>
);
},
},
];
}, [runHookWaterfall, displayedHeaders, layout, hasDraftAndPublish, formatMessage]);
if (reviewWorkflowColumns) {
// Make sure the column header label is translated
if (typeof reviewWorkflowColumns.metadatas.label !== 'string') {
reviewWorkflowColumns.metadatas.label = formatMessage(
reviewWorkflowColumns.metadatas.label
);
}
formattedHeaders.push(reviewWorkflowColumns);
}
return formattedHeaders;
}, [
runHookWaterfall,
displayedHeaders,
layout,
reviewWorkflowColumns,
hasDraftAndPublish,
formatMessage,
]);
const subtitle = canRead
? formatMessage(
@ -580,6 +584,10 @@ function ListView({
canCreate={canCreate}
canDelete={canDelete}
contentType={contentType}
features={{
hasDraftAndPublish,
hasReviewWorkflows,
}}
headers={tableHeaders}
rows={data}
withBulkActions

View File

@ -54,6 +54,14 @@ describe('useEnterprise (EE)', () => {
await waitFor(() => expect(result.current).toStrictEqual(EE_DATA_FIXTURE));
});
test('Returns CE data, when enabled is set to false', async () => {
const { result } = setup(CE_DATA_FIXTURE, async () => EE_DATA_FIXTURE, {
enabled: false,
});
await waitFor(() => expect(result.current).toStrictEqual(CE_DATA_FIXTURE));
});
test('Returns a custom defaultValue on first render followed by the EE data', async () => {
const { result } = setup(CE_DATA_FIXTURE, async () => EE_DATA_FIXTURE, {
defaultValue: false,

View File

@ -9,7 +9,7 @@ function isEnterprise() {
export function useEnterprise(
ceData,
eeCallback,
{ defaultValue = null, combine = (ceData, eeData) => eeData } = {}
{ defaultValue = null, combine = (ceData, eeData) => eeData, enabled = true } = {}
) {
const eeCallbackRef = useCallbackRef(eeCallback);
const combineCallbackRef = useCallbackRef(combine);
@ -17,7 +17,7 @@ export function useEnterprise(
// We have to use a nested object here, because functions (e.g. Components)
// can not be stored as value directly
const [{ data }, setData] = React.useState({
data: isEnterprise() ? defaultValue : ceData,
data: isEnterprise() && enabled ? defaultValue : ceData,
});
React.useEffect(() => {
@ -27,10 +27,10 @@ export function useEnterprise(
setData({ data: combineCallbackRef(ceData, eeData) });
}
if (isEnterprise()) {
if (isEnterprise() && enabled) {
importEE();
}
}, [ceData, eeCallbackRef, combineCallbackRef]);
}, [ceData, eeCallbackRef, combineCallbackRef, enabled]);
return data;
}

View File

@ -1,58 +0,0 @@
import React from 'react';
import { Typography } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import getTrad from '../../../../../../../admin/src/content-manager/utils/getTrad';
import { STAGE_COLOR_DEFAULT } from '../../../../../pages/SettingsPage/pages/ReviewWorkflows/constants';
import ReviewWorkflowsStage from '.';
export default (layout) => {
const { formatMessage } = useIntl();
// TODO: As soon as the feature was enabled in EE mode, the BE currently does not have a way to send
// `false` once a user is in CE mode again. We shouldn't have to perform the window.strapi.isEE check here
// and it is meant to be in interim solution until we find a better one.
const hasReviewWorkflows =
(window.strapi.features.isEnabled(window.strapi.features.REVIEW_WORKFLOWS) &&
layout.contentType.options?.reviewWorkflows) ??
false;
if (!hasReviewWorkflows) {
return null;
}
return {
key: '__strapi_reviewWorkflows_stage_temp_key__',
name: 'strapi_reviewWorkflows_stage',
fieldSchema: {
type: 'relation',
},
metadatas: {
label: formatMessage({
id: getTrad(`containers.ListPage.table-headers.reviewWorkflows.stage`),
defaultMessage: 'Review stage',
}),
searchable: false,
sortable: true,
mainField: {
name: 'name',
schema: {
type: 'string',
},
},
},
cellFormatter({ strapi_reviewWorkflows_stage }) {
// if entities are created e.g. through lifecycle methods
// they may not have a stage assigned
if (!strapi_reviewWorkflows_stage) {
return <Typography textColor="neutral800">-</Typography>;
}
const { color, name } = strapi_reviewWorkflows_stage;
return <ReviewWorkflowsStage color={color ?? STAGE_COLOR_DEFAULT} name={name} />;
},
};
};

View File

@ -1,3 +0,0 @@
import { ReviewWorkflowsStageEE } from './ReviewWorkflowsStageEE';
export default ReviewWorkflowsStageEE;

View File

@ -4,7 +4,7 @@ import { Box, Flex, Typography } from '@strapi/design-system';
import { pxToRem } from '@strapi/helper-plugin';
import PropTypes from 'prop-types';
import { getStageColorByHex } from '../../../../../pages/SettingsPage/pages/ReviewWorkflows/utils/colors';
import { getStageColorByHex } from '../../../../pages/SettingsPage/pages/ReviewWorkflows/utils/colors';
export function ReviewWorkflowsStageEE({ color, name }) {
const { themeColorName } = getStageColorByHex(color);

View File

@ -0,0 +1,24 @@
import getTrad from '../../../../../../admin/src/content-manager/utils/getTrad';
export const REVIEW_WORKFLOW_COLUMNS_EE = {
key: '__strapi_reviewWorkflows_stage_temp_key__',
name: 'strapi_reviewWorkflows_stage',
fieldSchema: {
type: 'relation',
},
metadatas: {
// formatMessage() will be applied when the column is rendered
label: {
id: getTrad(`containers.ListPage.table-headers.reviewWorkflows.stage`),
defaultMessage: 'Review stage',
},
searchable: false,
sortable: true,
mainField: {
name: 'name',
schema: {
type: 'string',
},
},
},
};

View File

@ -0,0 +1 @@
export * from './ReviewWorkflowsStageEE';

View File

@ -4,12 +4,12 @@ import { lightTheme, ThemeProvider } from '@strapi/design-system';
import { render } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import ReviewWorkflowsStage from '..';
import { ReviewWorkflowsStageEE } from '..';
const ComponentFixture = (props) => (
<ThemeProvider theme={lightTheme}>
<IntlProvider locale="en" messages={{}}>
<ReviewWorkflowsStage {...props} />
<ReviewWorkflowsStageEE {...props} />
</IntlProvider>
</ThemeProvider>
);