feat(content-releases): list releases with content type entry (#18999)

This commit is contained in:
markkaylor 2023-12-08 10:44:49 +01:00 committed by GitHub
parent 35f56a0444
commit 6eaefa820c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 16 deletions

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import { skipToken } from '@reduxjs/toolkit/query';
import {
Box,
Button,
@ -206,15 +207,25 @@ export const CMReleasesContainer = () => {
isCreatingEntry,
allLayoutData: { contentType },
} = useCMEditViewDataManager();
const params = useParams<{ id?: string }>();
const params = useParams<{ id: string }>();
const toggleAddActionToReleaseModal = () => setShowModal((prev) => !prev);
const canFetch = params?.id != null && contentType?.uid != null;
const fetchParams = canFetch
? {
contentTypeUid: contentType.uid,
entryId: params.id,
hasEntryAttached: true,
}
: skipToken;
// Get all 'pending' releases that have the entry attached
const response = useGetReleasesForEntryQuery(fetchParams);
const releases = response.data?.data;
/**
* If we don't have a contentType.uid or params.id no use showing the injection zone
* If we don't have a contentType.uid or params.id then the data was never fetched
* TODO: Should we handle this with an error message in the UI or just not show the container?
*/
if (!contentType?.uid || !params.id) {
if (!canFetch) {
return null;
}
@ -223,10 +234,23 @@ export const CMReleasesContainer = () => {
* - Content types without draft and publish cannot add entries to release
* TODO v5: All contentTypes will have draft and publish enabled
*/
if (isCreatingEntry || !contentType.options?.draftAndPublish) {
if (isCreatingEntry || !contentType?.options?.draftAndPublish) {
return null;
}
const toggleAddActionToReleaseModal = () => setShowModal((prev) => !prev);
const getReleaseColorVariant = (
actionType: 'publish' | 'unpublish',
shade: '100' | '200' | '600'
) => {
if (actionType === 'unpublish') {
return `secondary${shade}`;
}
return `success${shade}`;
};
return (
<CheckPermissions permissions={PERMISSIONS.main}>
<Box
@ -241,13 +265,56 @@ export const CMReleasesContainer = () => {
padding={4}
shadow="tableShadow"
>
<Flex direction="column" alignItems="stretch" gap={4}>
<Flex direction="column" alignItems="stretch" gap={3}>
<Typography variant="sigma" textColor="neutral600" textTransform="uppercase">
{formatMessage({
id: 'content-releases.plugin.name',
defaultMessage: 'RELEASES',
defaultMessage: 'Releases',
})}
</Typography>
{releases?.map((release) => {
return (
<Flex
key={release.id}
direction="column"
alignItems="start"
borderWidth="1px"
borderStyle="solid"
borderColor={getReleaseColorVariant(release.action.type, '200')}
overflow="hidden"
hasRadius
>
<Box
paddingTop={3}
paddingBottom={3}
paddingLeft={4}
paddingRight={4}
background={getReleaseColorVariant(release.action.type, '100')}
width="100%"
>
<Typography
fontSize={1}
variant="pi"
textColor={getReleaseColorVariant(release.action.type, '600')}
>
{formatMessage(
{
id: 'content-releases.content-manager-edit-view.list-releases.title',
defaultMessage:
'{isPublish, select, true {Will be published in} other {Will be unpublished in}}',
},
{ isPublish: release.action.type === 'publish' }
)}
</Typography>
</Box>
<Box padding={4}>
<Typography fontSize={2} fontWeight="bold" variant="omega" textColor="neutral700">
{release.name}
</Typography>
</Box>
</Flex>
);
})}
<CheckPermissions permissions={PERMISSIONS.createAction}>
<Button
justifyContent="center"

View File

@ -45,6 +45,11 @@ const FieldWrapper = styled(Field)<FieldWrapperProps>`
background-color: ${({ theme }) => theme.colors.primary100};
border-color: ${({ theme }) => theme.colors.primary700};
}
&[data-checked='false'] {
border-left: ${({ actionType }) => actionType === 'unpublish' && 'none'};
border-right: ${({ actionType }) => actionType === 'publish' && 'none'};
}
`;
interface ActionOptionProps {

View File

@ -1,5 +1,5 @@
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
import { screen } from '@testing-library/react';
import { screen, within } from '@testing-library/react';
import { render, server } from '@tests/utils';
import { rest } from 'msw';
@ -28,7 +28,7 @@ jest.mock('react-router-dom', () => ({
}));
describe('CMReleasesContainer', () => {
it('should not render the injection zone when creating an entry', () => {
it('should not render the container when creating an entry', () => {
// @ts-expect-error - Ignore error
useCMEditViewDataManager.mockReturnValueOnce({
isCreatingEntry: true,
@ -47,7 +47,7 @@ describe('CMReleasesContainer', () => {
expect(informationBox).not.toBeInTheDocument();
});
it('should not render the injection zone without draft and publish enabled', () => {
it('should not render the container without draft and publish enabled', async () => {
// @ts-expect-error - Ignore error
useCMEditViewDataManager.mockReturnValueOnce({
isCreatingEntry: false,
@ -66,11 +66,11 @@ describe('CMReleasesContainer', () => {
expect(informationBox).not.toBeInTheDocument();
});
it('should render the injection zone', () => {
it('should render the container', async () => {
render(<CMReleasesContainer />);
const addToReleaseButton = screen.getByRole('button', { name: 'Add to release' });
const informationBox = screen.getByRole('complementary', { name: 'Releases' });
const addToReleaseButton = await screen.findByRole('button', { name: 'Add to release' });
expect(informationBox).toBeInTheDocument();
expect(addToReleaseButton).toBeInTheDocument();
});
@ -94,7 +94,7 @@ describe('CMReleasesContainer', () => {
rest.get('/content-releases', (req, res, ctx) => {
return res(
ctx.json({
data: [{ name: 'release1', id: '1' }],
data: [{ name: 'release1', id: '1', action: { type: 'publish' } }],
})
);
})
@ -113,4 +113,26 @@ describe('CMReleasesContainer', () => {
const submitButtom = screen.getByRole('button', { name: 'Continue' });
expect(submitButtom).toBeEnabled();
});
it('should list releases', async () => {
// Mock the response from the server
server.use(
rest.get('/content-releases', (req, res, ctx) => {
return res(
ctx.json({
data: [
{ name: 'release1', id: '1', action: { type: 'publish' } },
{ name: 'release2', id: '2', action: { type: 'unpublish' } },
],
})
);
})
);
render(<CMReleasesContainer />);
const informationBox = await screen.findByRole('complementary', { name: 'Releases' });
expect(within(informationBox).getByText('release1')).toBeInTheDocument();
expect(within(informationBox).getByText('release2')).toBeInTheDocument();
});
});

View File

@ -37,7 +37,7 @@ const releaseApi = createApi({
return {
getReleasesForEntry: build.query<
GetContentTypeEntryReleases.Response,
GetContentTypeEntryReleases.Request['query']
Partial<GetContentTypeEntryReleases.Request['query']>
>({
query(params) {
return {

View File

@ -6,6 +6,7 @@
"content-manager-edit-view.add-to-release.continue-button": "Continue",
"content-manager-edit-view.add-to-release": "Add to release",
"content-manager-edit-view.add-to-release.notification.success": "Entry added to release",
"content-manager-edit-view.list-releases.title": "{isPublish, select, true {Will be published in} other {Will be unpublished in}}",
"content-manager.notification.entry-error": "Failed to get entry data",
"plugin.name": "Releases",
"pages.Releases.title": "Releases",

View File

@ -36,7 +36,6 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
...query,
populate: {
actions: {
// @ts-expect-error TS error on populate, is not considering count
count: true,
},
},
@ -301,7 +300,7 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
}
});
// When the transaction fails it throws an error, when it is successful proceed to updating the release
// When the transaction fails it throws an error, when it is successful proceed to updating the release
const release = await strapi.entityService.update(RELEASE_MODEL_UID, releaseId, {
data: {
/*