Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add history document action #19683

Merged
merged 10 commits into from Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 14 additions & 6 deletions e2e/tests/content-manager/history.spec.ts
Expand Up @@ -36,7 +36,8 @@ describeOnCondition(hasFutureFlag)('History', () => {
await titleInput.fill(frenchTitle);
await page.getByRole('button', { name: 'Save' }).click();
// Go to the history page
await page.getByRole('link', { name: 'History' }).click();
await page.getByRole('button', { name: /more actions/i }).click();
await page.getByRole('menuitem', { name: /content history/i }).click();
await page.waitForURL(HISTORY_URL);
await expect(titleInput).toHaveValue(frenchTitle);

Expand All @@ -51,7 +52,8 @@ describeOnCondition(hasFutureFlag)('History', () => {
await titleInput.fill(englishTitle);
await page.getByRole('button', { name: 'Save' }).click();
// Go to the history page
await page.getByRole('link', { name: 'History' }).click();
await page.getByRole('button', { name: /more actions/i }).click();
await page.getByRole('menuitem', { name: /content history/i }).click();
await page.waitForURL(HISTORY_URL);
const versionCards = await page.getByRole('listitem', { name: 'Version card' });
await expect(versionCards).toHaveCount(1);
Expand All @@ -76,7 +78,9 @@ describeOnCondition(hasFutureFlag)('History', () => {
await titleInput.fill('Being from Kansas City');
await page.getByRole('button', { name: 'Save' }).click();
// Go to the history page
await page.getByRole('link', { name: 'History' }).click();
await page.getByRole('button', { name: /more actions/i }).click();
await page.getByRole('menuitem', { name: /content history/i }).click();
await page.waitForURL(HISTORY_URL);
await expect(versionCards).toHaveCount(2);
// Assert the most recent version is the current version
versionCards.first().click();
Expand Down Expand Up @@ -142,7 +146,8 @@ describeOnCondition(hasFutureFlag)('History', () => {
await titleInput.fill(frenchTitle);
await page.getByRole('button', { name: 'Save' }).click();
// Go to the history page
await page.getByRole('link', { name: 'History' }).click();
await page.getByRole('button', { name: /more actions/i }).click();
await page.getByRole('menuitem', { name: /content history/i }).click();
await page.waitForURL(HISTORY_URL);
await expect(titleInput).toHaveValue(frenchTitle);

Expand All @@ -156,7 +161,8 @@ describeOnCondition(hasFutureFlag)('History', () => {
await titleInput.fill(englishTitle);
await page.getByRole('button', { name: 'Save' }).click();
// Go to the history page
await page.getByRole('link', { name: 'History' }).click();
await page.getByRole('button', { name: /more actions/i }).click();
await page.getByRole('menuitem', { name: /content history/i }).click();
await page.waitForURL(HISTORY_URL);
const versionCards = await page.getByRole('listitem', { name: 'Version card' });
await expect(versionCards).toHaveCount(1);
Expand All @@ -180,7 +186,9 @@ describeOnCondition(hasFutureFlag)('History', () => {
*/
await page.getByRole('textbox', { name: 'title' }).fill('Welcome to AFC Richmond');
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('link', { name: 'History' }).click();
await page.getByRole('button', { name: /more actions/i }).click();
await page.getByRole('menuitem', { name: /content history/i }).click();
await page.waitForURL(HISTORY_URL);
await expect(versionCards).toHaveCount(2);
// Assert the most recent version is the current version
versionCards.first().click();
Expand Down
5 changes: 3 additions & 2 deletions packages/core/admin/admin/custom.d.ts
@@ -1,13 +1,14 @@
/// <reference types="vite/client" />

import { type StrapiTheme } from '@strapi/design-system';
import { type Attribute, type FeaturesService } from '@strapi/types';
import { type BaseEditor } from 'slate';
import { type HistoryEditor } from 'slate-history';
import { type ReactEditor } from 'slate-react';

import { type LinkEditor } from './src/content-manager/pages/EditView/components/FormInputs/BlocksInput/plugins/withLinks';

import type { FeaturesConfig, Attribute } from '@strapi/types';

declare module 'styled-components' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface DefaultTheme extends StrapiTheme {}
Expand All @@ -28,7 +29,7 @@ interface BrowserStrapi {
backendURL: string;
isEE: boolean;
future: {
isEnabled: (name: keyof FeaturesService['config']) => boolean;
isEnabled: (name: keyof FeaturesConfig['future']) => boolean;
};
features: {
SSO: 'sso';
Expand Down
8 changes: 0 additions & 8 deletions packages/core/admin/admin/src/StrapiApp.tsx
Expand Up @@ -30,7 +30,6 @@ import {
} from './components/InjectionZone';
import { Providers } from './components/Providers';
import { HOOKS } from './constants';
import { InjectedLink } from './content-manager/history/components/InjectedLink';
import { routes as cmRoutes } from './content-manager/router';
import { ContentManagerPlugin } from './core/apis/content-manager';
import { CustomFields } from './core/apis/CustomFields';
Expand Down Expand Up @@ -356,13 +355,6 @@ class StrapiApp {
}
});

// TODO: remove once we can add the link via a document action instead
this.injectContentManagerComponent('editView', 'right-links', {
name: 'history',
Component: InjectedLink,
slug: 'history',
});

if (isFunction(customBootstrap)) {
customBootstrap({
addComponents: this.addComponents,
Expand Down
@@ -0,0 +1,43 @@
import * as React from 'react';

import { useQueryParams } from '@strapi/helper-plugin';
import { Clock } from '@strapi/icons';
import { stringify } from 'qs';
import { useIntl } from 'react-intl';
import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';

import type { DocumentActionComponent } from '../../../core/apis/content-manager';

const StyledClock = styled(Clock)`
path {
fill: currentColor;
}
`;

const HistoryAction: DocumentActionComponent = ({ model, document }) => {
const { formatMessage } = useIntl();
const [{ query }] = useQueryParams<{ plugins?: Record<string, unknown> }>();
const navigate = useNavigate();
const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });

// TODO also check license
if (!window.strapi.future.isEnabled('history')) {
return null;
}

return {
icon: <StyledClock />,
label: formatMessage({
id: 'content-manager.history.document-action',
defaultMessage: 'Content History',
}),
onClick: () => navigate({ pathname: 'history', search: pluginsQueryParams }),
disabled: !document || !document.id || !model.startsWith('api::'),
remidej marked this conversation as resolved.
Show resolved Hide resolved
position: 'header',
};
};

HistoryAction.type = 'history';

export { HistoryAction };

This file was deleted.

Expand Up @@ -37,7 +37,7 @@ const VersionContent = () => {
return (
<ContentLayout>
<Form disabled={true} method="PUT" initialValues={version.data}>
<Flex direction="column" alignItems="stretch" gap={6}>
<Flex direction="column" alignItems="stretch" gap={6} position="relative">
{layout.map((panel, index) => {
if (panel.some((row) => row.some((field) => field.type === 'dynamiczone'))) {
const [row] = panel;
Expand Down
Expand Up @@ -281,18 +281,39 @@ const DocumentActionsMenu = ({
<Menu.Content top="4px" maxHeight={undefined} popoverPlacement="bottom-end">
{actions.map((action) => {
return (
<React.Fragment key={action.id}>
<Menu.Item
disabled={action.disabled}
/* @ts-expect-error – TODO: this is an error in the DS where it is most likely a synthetic event, not regular. */
onSelect={handleClick(action)}
>
<Menu.Item
disabled={action.disabled}
/* @ts-expect-error – TODO: this is an error in the DS where it is most likely a synthetic event, not regular. */
onSelect={handleClick(action)}
display="block"
key={action.id}
>
<Flex justifyContent="space-between" gap={4}>
<Flex color={convertActionVariantToColor(action.variant)} gap={2} as="span">
{action.icon}
{action.label}
</Flex>
</Menu.Item>
</React.Fragment>
{/* TODO: remove this in 5.1 release */}
{action.id.startsWith('HistoryAction') && (
<Flex
alignItems="center"
background="alternative100"
borderStyle="solid"
borderColor="alternative200"
borderWidth="1px"
height={5}
paddingLeft={2}
paddingRight={2}
hasRadius
color="alternative600"
>
<Typography variant="sigma" fontWeight="bold" lineHeight={1}>
{formatMessage({ id: 'global.new', defaultMessage: 'New' })}
</Typography>
</Flex>
)}
</Flex>
</Menu.Item>
);
})}
{children}
Expand Down
Expand Up @@ -326,6 +326,7 @@ const Information = ({ activeTab }: InformationProps) => {
padding={5}
gap={3}
alignItems="flex-start"
width="100%"
>
{information.map((info) => (
<Flex gap={1} direction="column" alignItems="flex-start" key={info.label}>
Expand Down
3 changes: 3 additions & 0 deletions packages/core/admin/admin/src/core/apis/content-manager.ts
Expand Up @@ -6,6 +6,7 @@
*/

import { ReviewWorkflowsPanel } from '../../../../ee/admin/src/content-manager/pages/EditView/components/ReviewWorkflowsPanel';
import { HistoryAction } from '../../content-manager/history/components/HistoryAction';
import {
DEFAULT_ACTIONS,
type DocumentActionDescription,
Expand Down Expand Up @@ -80,6 +81,7 @@ interface DocumentActionComponent
| 'discard'
| 'edit'
| 'edit-the-model'
| 'history'
| 'publish'
| 'unpublish'
| 'update';
Expand All @@ -105,6 +107,7 @@ class ContentManagerPlugin {
...DEFAULT_ACTIONS,
...DEFAULT_TABLE_ROW_ACTIONS,
...DEFAULT_HEADER_ACTIONS,
HistoryAction,
];
editViewSidePanels: PanelComponent[] = [ActionsPanel, ReviewWorkflowsPanel];
headerActions: HeaderActionComponent[] = [];
Expand Down
4 changes: 2 additions & 2 deletions packages/core/admin/admin/src/render.ts
Expand Up @@ -4,7 +4,7 @@ import { createRoot } from 'react-dom/client';

import { StrapiApp, StrapiAppConstructorArgs } from './StrapiApp';

import type { FeaturesService } from '@strapi/types';
import type { FeaturesConfig, FeaturesService } from '@strapi/types';

interface RenderAdminArgs {
customisations: {
Expand Down Expand Up @@ -34,7 +34,7 @@ const renderAdmin = async (
isEE: false,
telemetryDisabled: process.env.STRAPI_TELEMETRY_DISABLED === 'true' ? true : false,
future: {
isEnabled: (name: keyof FeaturesService['config']) => {
isEnabled: (name: keyof FeaturesConfig['future']) => {
return features?.future?.[name] === true;
},
},
Expand Down
2 changes: 2 additions & 0 deletions packages/core/admin/admin/src/translations/en.json
Expand Up @@ -927,6 +927,7 @@
"content-manager.utils.data-loaded": "The {number, plural, =1 {entry has} other {entries have}} successfully been loaded",
"content-manager.listView.validation.errors.title": "Action required",
"content-manager.listView.validation.errors.message": "Please make sure all fields are valid before publishing (required field, min/max character limit, etc.)",
"content-manager.history.document-action": "Content History",
"content-manager.history.page-title": "{contentType} history",
"content-manager.history.sidebar.title": "Versions",
"content-manager.history.sidebar.version-card.aria-label": "Version card",
Expand Down Expand Up @@ -954,6 +955,7 @@
"global.finish": "Finish",
"global.marketplace": "Marketplace",
"global.name": "Name",
"global.new": "New",
"global.none": "None",
"global.password": "Password",
"global.plugins": "Plugins",
Expand Down
6 changes: 5 additions & 1 deletion packages/core/admin/ee/admin/custom.d.ts
@@ -1,11 +1,12 @@
import { type StrapiTheme } from '@strapi/design-system';
import { type Attribute } from '@strapi/types';
import { type BaseEditor } from 'slate';
import { type HistoryEditor } from 'slate-history';
import { type ReactEditor } from 'slate-react';

import { type LinkEditor } from '../../admin/src/content-manager/pages/EditView/components/FormInputs/BlocksInput/plugins/withLinks';

import type { FeaturesConfig, Attribute } from '@strapi/types';

declare module 'styled-components' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface DefaultTheme extends StrapiTheme {}
Expand All @@ -27,6 +28,9 @@ declare global {
strapi: {
backendURL: string;
isEE: boolean;
future: {
isEnabled: (name: keyof FeaturesConfig['future']) => boolean;
};
features: {
SSO: 'sso';
AUDIT_LOGS: 'audit-logs';
Expand Down
3 changes: 2 additions & 1 deletion packages/core/types/src/modules/features.ts
@@ -1,6 +1,7 @@
export interface FeaturesConfig {
future?: {
future: {
remidej marked this conversation as resolved.
Show resolved Hide resolved
contentReleases?: boolean;
history?: boolean;
};
}

Expand Down