mirror of
https://github.com/strapi/strapi.git
synced 2025-12-10 14:34:22 +00:00
feat(content-releases): list releases with content type entry (#18999)
This commit is contained in:
parent
35f56a0444
commit
6eaefa820c
@ -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"
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -37,7 +37,7 @@ const releaseApi = createApi({
|
||||
return {
|
||||
getReleasesForEntry: build.query<
|
||||
GetContentTypeEntryReleases.Response,
|
||||
GetContentTypeEntryReleases.Request['query']
|
||||
Partial<GetContentTypeEntryReleases.Request['query']>
|
||||
>({
|
||||
query(params) {
|
||||
return {
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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: {
|
||||
/*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user