mirror of
https://github.com/strapi/strapi.git
synced 2025-07-26 18:38:46 +00:00
enhancement: create dedicated preview page (#21965)
* enh: add preview page and navbar * chore: remove header * chore: update e2e tests * chore: remove getDocumentStatus export * fix: error state
This commit is contained in:
parent
c5ae9675f5
commit
6d1431fe2a
@ -226,5 +226,5 @@ const BackButton = React.forwardRef<HTMLAnchorElement, BackButtonProps>(({ disab
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export { BackButton, HistoryProvider };
|
export { BackButton, HistoryProvider, useHistory };
|
||||||
export type { BackButtonProps, HistoryProviderProps, HistoryContextValue, HistoryState };
|
export type { BackButtonProps, HistoryProviderProps, HistoryContextValue, HistoryState };
|
||||||
|
@ -37,6 +37,7 @@ export {
|
|||||||
} from './features/Notifications';
|
} from './features/Notifications';
|
||||||
export { useAppInfo, type AppInfoContextValue } from './features/AppInfo';
|
export { useAppInfo, type AppInfoContextValue } from './features/AppInfo';
|
||||||
export { type Permission, useAuth, type AuthContextValue } from './features/Auth';
|
export { type Permission, useAuth, type AuthContextValue } from './features/Auth';
|
||||||
|
export { useHistory } from './features/BackButton';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
|
@ -14,7 +14,6 @@ import { stringify } from 'qs';
|
|||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { NavLink, useNavigate, useParams, type To } from 'react-router-dom';
|
import { NavLink, useNavigate, useParams, type To } from 'react-router-dom';
|
||||||
|
|
||||||
import { COLLECTION_TYPES } from '../../constants/collections';
|
|
||||||
import { PERMISSIONS } from '../../constants/plugin';
|
import { PERMISSIONS } from '../../constants/plugin';
|
||||||
import { useHistoryContext } from '../pages/History';
|
import { useHistoryContext } from '../pages/History';
|
||||||
import { useRestoreVersionMutation } from '../services/historyVersion';
|
import { useRestoreVersionMutation } from '../services/historyVersion';
|
||||||
@ -48,13 +47,6 @@ export const VersionHeader = ({ headerId }: VersionHeaderProps) => {
|
|||||||
const getNextNavigation = (): To => {
|
const getNextNavigation = (): To => {
|
||||||
const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
|
const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
|
||||||
|
|
||||||
if (collectionType === COLLECTION_TYPES) {
|
|
||||||
return {
|
|
||||||
pathname: '..',
|
|
||||||
search: pluginsQueryParams,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pathname: '..',
|
pathname: '..',
|
||||||
search: pluginsQueryParams,
|
search: pluginsQueryParams,
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import {
|
import { useQueryParams, Page, createContext, useRBAC } from '@strapi/admin/strapi-admin';
|
||||||
useQueryParams,
|
|
||||||
Page,
|
|
||||||
createContext,
|
|
||||||
useRBAC,
|
|
||||||
BackButton,
|
|
||||||
} from '@strapi/admin/strapi-admin';
|
|
||||||
import { Box, Flex, FocusTrap, Main, Portal, Link } from '@strapi/design-system';
|
import { Box, Flex, FocusTrap, Main, Portal, Link } from '@strapi/design-system';
|
||||||
import { stringify } from 'qs';
|
import { stringify } from 'qs';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/* eslint-disable check-file/filename-naming-convention */
|
/* eslint-disable check-file/filename-naming-convention */
|
||||||
import { lazy } from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { type PathRouteProps } from 'react-router-dom';
|
import { type PathRouteProps } from 'react-router-dom';
|
||||||
|
|
||||||
const ProtectedHistoryPage = lazy(() =>
|
const ProtectedHistoryPage = React.lazy(() =>
|
||||||
import('./pages/History').then((mod) => ({ default: mod.ProtectedHistoryPage }))
|
import('./pages/History').then((mod) => ({ default: mod.ProtectedHistoryPage }))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ const EditViewPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Main paddingLeft={10} paddingRight={10}>
|
<Main paddingLeft={10} paddingRight={10}>
|
||||||
<Page.Title>{`${documentTitle}`}</Page.Title>
|
<Page.Title>{documentTitle}</Page.Title>
|
||||||
<Form
|
<Form
|
||||||
disabled={hasDraftAndPublished && status === 'published'}
|
disabled={hasDraftAndPublished && status === 'published'}
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
@ -228,7 +228,7 @@ const StatusTab = styled(Tabs.Trigger)`
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
* @description Returns the status of the document where it's latest state takes priority,
|
* @description Returns the status of the document where its latest state takes priority,
|
||||||
* this typically will be "published" unless a user has edited their draft in which we should
|
* this typically will be "published" unless a user has edited their draft in which we should
|
||||||
* display "modified".
|
* display "modified".
|
||||||
*/
|
*/
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { useClipboard, useNotification, useTracking } from '@strapi/admin/strapi-admin';
|
import { useQueryParams, useTracking } from '@strapi/admin/strapi-admin';
|
||||||
import { Button, Flex, IconButton } from '@strapi/design-system';
|
import { Button, Flex } from '@strapi/design-system';
|
||||||
import { Link as LinkIcon } from '@strapi/icons';
|
|
||||||
import { UID } from '@strapi/types';
|
import { UID } from '@strapi/types';
|
||||||
|
import { stringify } from 'qs';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@ -13,9 +13,14 @@ import type { PanelComponent } from '@strapi/content-manager/strapi-admin';
|
|||||||
|
|
||||||
const PreviewSidePanel: PanelComponent = ({ model, documentId, document }) => {
|
const PreviewSidePanel: PanelComponent = ({ model, documentId, document }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { toggleNotification } = useNotification();
|
|
||||||
const { copy } = useClipboard();
|
|
||||||
const { trackUsage } = useTracking();
|
const { trackUsage } = useTracking();
|
||||||
|
const [{ query }] = useQueryParams();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preview URL isn't used in this component, we just fetch it to know if preview is enabled
|
||||||
|
* for the content type. If it's not, the panel is not displayed. If it is, we display a link to
|
||||||
|
* /preview, and the URL will already be loaded in the RTK query cache.
|
||||||
|
*/
|
||||||
const { data, error } = useGetPreviewUrlQuery({
|
const { data, error } = useGetPreviewUrlQuery({
|
||||||
params: {
|
params: {
|
||||||
contentType: model as UID.ContentType,
|
contentType: model as UID.ContentType,
|
||||||
@ -31,20 +36,8 @@ const PreviewSidePanel: PanelComponent = ({ model, documentId, document }) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { url } = data.data;
|
|
||||||
|
|
||||||
const handleCopyLink = () => {
|
|
||||||
copy(url);
|
|
||||||
toggleNotification({
|
|
||||||
message: formatMessage({
|
|
||||||
id: 'content-manager.preview.copy.success',
|
|
||||||
defaultMessage: 'Copied preview link',
|
|
||||||
}),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
// TODO: delete this event and use willNavigate instead
|
||||||
trackUsage('willOpenPreview');
|
trackUsage('willOpenPreview');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,9 +48,8 @@ const PreviewSidePanel: PanelComponent = ({ model, documentId, document }) => {
|
|||||||
<Button
|
<Button
|
||||||
variant="tertiary"
|
variant="tertiary"
|
||||||
tag={Link}
|
tag={Link}
|
||||||
to={url}
|
to={{ pathname: 'preview', search: stringify(query, { encode: false }) }}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
target="_blank"
|
|
||||||
flex="auto"
|
flex="auto"
|
||||||
>
|
>
|
||||||
{formatMessage({
|
{formatMessage({
|
||||||
@ -65,16 +57,6 @@ const PreviewSidePanel: PanelComponent = ({ model, documentId, document }) => {
|
|||||||
defaultMessage: 'Open preview',
|
defaultMessage: 'Open preview',
|
||||||
})}
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
<IconButton
|
|
||||||
type="button"
|
|
||||||
label={formatMessage({
|
|
||||||
id: 'preview.copy.label',
|
|
||||||
defaultMessage: 'Copy preview link',
|
|
||||||
})}
|
|
||||||
onClick={handleCopyLink}
|
|
||||||
>
|
|
||||||
<LinkIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,200 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { Page, useQueryParams, useRBAC, createContext } from '@strapi/admin/strapi-admin';
|
||||||
|
import { Box, FocusTrap, Portal, Typography } from '@strapi/design-system';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { GetPreviewUrl } from '../../../../shared/contracts/preview';
|
||||||
|
import { COLLECTION_TYPES } from '../../constants/collections';
|
||||||
|
import { DocumentRBAC } from '../../features/DocumentRBAC';
|
||||||
|
import { type UseDocument, useDocument } from '../../hooks/useDocument';
|
||||||
|
import { useDocumentLayout } from '../../hooks/useDocumentLayout';
|
||||||
|
import { buildValidParams } from '../../utils/api';
|
||||||
|
import { useGetPreviewUrlQuery } from '../services/preview';
|
||||||
|
|
||||||
|
import type { UID } from '@strapi/types';
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------------------------------
|
||||||
|
* PreviewProvider
|
||||||
|
* -----------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
interface PreviewContextValue {
|
||||||
|
url: string;
|
||||||
|
mainField: string;
|
||||||
|
document: NonNullable<ReturnType<UseDocument>['document']>;
|
||||||
|
meta: NonNullable<ReturnType<UseDocument>['meta']>;
|
||||||
|
schema: NonNullable<ReturnType<UseDocument>['schema']>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [PreviewProvider, usePreviewContext] = createContext<PreviewContextValue>('PreviewPage');
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------------------------------
|
||||||
|
* PreviewPage
|
||||||
|
* -----------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
const PreviewPage = () => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
// Read all the necessary data from the URL to find the right preview URL
|
||||||
|
const {
|
||||||
|
slug: model,
|
||||||
|
id: documentId,
|
||||||
|
collectionType,
|
||||||
|
} = useParams<{
|
||||||
|
slug: UID.ContentType;
|
||||||
|
id: string;
|
||||||
|
collectionType: string;
|
||||||
|
}>();
|
||||||
|
const [{ query }] = useQueryParams<{
|
||||||
|
plugins?: Record<string, unknown>;
|
||||||
|
}>();
|
||||||
|
const params = React.useMemo(() => buildValidParams(query), [query]);
|
||||||
|
|
||||||
|
if (!collectionType) {
|
||||||
|
throw new Error('Could not find collectionType in url params');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!model) {
|
||||||
|
throw new Error('Could not find model in url params');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only collection types must have a documentId
|
||||||
|
if (collectionType === COLLECTION_TYPES && !documentId) {
|
||||||
|
throw new Error('Could not find documentId in url params');
|
||||||
|
}
|
||||||
|
|
||||||
|
const previewUrlResponse = useGetPreviewUrlQuery({
|
||||||
|
params: {
|
||||||
|
contentType: model,
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
documentId,
|
||||||
|
locale: params.locale,
|
||||||
|
status: params.status as GetPreviewUrl.Request['query']['status'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const documentResponse = useDocument({
|
||||||
|
model,
|
||||||
|
collectionType,
|
||||||
|
documentId,
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
|
||||||
|
const documentLayoutResponse = useDocumentLayout(model);
|
||||||
|
|
||||||
|
if (
|
||||||
|
documentResponse.isLoading ||
|
||||||
|
previewUrlResponse.isLoading ||
|
||||||
|
documentLayoutResponse.isLoading
|
||||||
|
) {
|
||||||
|
return <Page.Loading />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
previewUrlResponse.error ||
|
||||||
|
documentLayoutResponse.error ||
|
||||||
|
!documentResponse.document ||
|
||||||
|
!documentResponse.meta ||
|
||||||
|
!documentResponse.schema
|
||||||
|
) {
|
||||||
|
return <Page.Error />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!previewUrlResponse.data?.data?.url) {
|
||||||
|
return <Page.NoData />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Page.Title>
|
||||||
|
{formatMessage(
|
||||||
|
{
|
||||||
|
id: 'content-manager.preview.page-title',
|
||||||
|
defaultMessage: '{contentType} preview',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
contentType: documentLayoutResponse.edit.settings.displayName,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</Page.Title>
|
||||||
|
<PreviewProvider
|
||||||
|
url={previewUrlResponse.data.data.url}
|
||||||
|
mainField={documentLayoutResponse.edit.settings.mainField}
|
||||||
|
document={documentResponse.document}
|
||||||
|
meta={documentResponse.meta}
|
||||||
|
schema={documentResponse.schema}
|
||||||
|
>
|
||||||
|
<Typography>Preview will go here!</Typography>
|
||||||
|
</PreviewProvider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------------------------------
|
||||||
|
* ProtectedPreviewPage
|
||||||
|
* -----------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
const ProtectedPreviewPageImpl = () => {
|
||||||
|
const { slug: model } = useParams<{
|
||||||
|
slug: string;
|
||||||
|
}>();
|
||||||
|
const {
|
||||||
|
permissions = [],
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
} = useRBAC([{ action: 'plugin::content-manager.explorer.read', subject: model }]);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Page.Loading />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error || !model) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
height="100vh"
|
||||||
|
width="100vw"
|
||||||
|
position="fixed"
|
||||||
|
top={0}
|
||||||
|
left={0}
|
||||||
|
zIndex={2}
|
||||||
|
background="neutral0"
|
||||||
|
>
|
||||||
|
<Page.Error />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
height="100vh"
|
||||||
|
width="100vw"
|
||||||
|
position="fixed"
|
||||||
|
top={0}
|
||||||
|
left={0}
|
||||||
|
zIndex={2}
|
||||||
|
background="neutral0"
|
||||||
|
>
|
||||||
|
<Page.Protect permissions={permissions}>
|
||||||
|
{({ permissions }) => (
|
||||||
|
<DocumentRBAC permissions={permissions}>
|
||||||
|
<PreviewPage />
|
||||||
|
</DocumentRBAC>
|
||||||
|
)}
|
||||||
|
</Page.Protect>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProtectedPreviewPage = () => {
|
||||||
|
return (
|
||||||
|
<Portal>
|
||||||
|
<FocusTrap>
|
||||||
|
<ProtectedPreviewPageImpl />
|
||||||
|
</FocusTrap>
|
||||||
|
</Portal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { ProtectedPreviewPage, usePreviewContext };
|
21
packages/core/content-manager/admin/src/preview/routes.tsx
Normal file
21
packages/core/content-manager/admin/src/preview/routes.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* eslint-disable check-file/filename-naming-convention */
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import type { PathRouteProps } from 'react-router-dom';
|
||||||
|
|
||||||
|
const ProtectedPreviewPage = React.lazy(() =>
|
||||||
|
import('./pages/Preview').then((mod) => ({ default: mod.ProtectedPreviewPage }))
|
||||||
|
);
|
||||||
|
|
||||||
|
const routes: PathRouteProps[] = [
|
||||||
|
{
|
||||||
|
path: ':collectionType/:slug/:id/preview',
|
||||||
|
Component: ProtectedPreviewPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':collectionType/:slug/preview',
|
||||||
|
Component: ProtectedPreviewPage,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export { routes };
|
@ -5,6 +5,7 @@ import { Navigate, PathRouteProps, useParams } from 'react-router-dom';
|
|||||||
|
|
||||||
import { COLLECTION_TYPES, SINGLE_TYPES } from './constants/collections';
|
import { COLLECTION_TYPES, SINGLE_TYPES } from './constants/collections';
|
||||||
import { routes as historyRoutes } from './history/routes';
|
import { routes as historyRoutes } from './history/routes';
|
||||||
|
import { routes as previewRoutes } from './preview/routes';
|
||||||
|
|
||||||
const ProtectedEditViewPage = lazy(() =>
|
const ProtectedEditViewPage = lazy(() =>
|
||||||
import('./pages/EditView/EditViewPage').then((mod) => ({ default: mod.ProtectedEditViewPage }))
|
import('./pages/EditView/EditViewPage').then((mod) => ({ default: mod.ProtectedEditViewPage }))
|
||||||
@ -90,6 +91,7 @@ const routes: PathRouteProps[] = [
|
|||||||
Component: NoContentType,
|
Component: NoContentType,
|
||||||
},
|
},
|
||||||
...historyRoutes,
|
...historyRoutes,
|
||||||
|
...previewRoutes,
|
||||||
];
|
];
|
||||||
|
|
||||||
export { routes, CLONE_PATH, LIST_PATH };
|
export { routes, CLONE_PATH, LIST_PATH };
|
||||||
|
@ -233,6 +233,8 @@
|
|||||||
"popover.display-relations.label": "Display relations",
|
"popover.display-relations.label": "Display relations",
|
||||||
"preview.panel.title": "Preview",
|
"preview.panel.title": "Preview",
|
||||||
"preview.panel.button": "Open preview",
|
"preview.panel.button": "Open preview",
|
||||||
|
"preview.page-title": "{contentType} preview",
|
||||||
|
"preview.header.close": "Close preview",
|
||||||
"preview.copy.label": "Copy preview link",
|
"preview.copy.label": "Copy preview link",
|
||||||
"preview.copy.success": "Copied preview link",
|
"preview.copy.success": "Copied preview link",
|
||||||
"relation.add": "Add relation",
|
"relation.add": "Add relation",
|
||||||
|
@ -16,36 +16,15 @@ describeOnCondition(edition === 'EE')('Preview', () => {
|
|||||||
await page.waitForURL('/admin');
|
await page.waitForURL('/admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Preview button should appear for configured content types', async ({
|
test('Preview button should appear for configured content types', async ({ page, context }) => {
|
||||||
page,
|
|
||||||
context,
|
|
||||||
browser,
|
|
||||||
}) => {
|
|
||||||
// Open an edit view for a content type that has preview
|
// Open an edit view for a content type that has preview
|
||||||
await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
|
await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
|
||||||
await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
|
await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
|
||||||
await clickAndWait(page, page.getByRole('gridcell', { name: /west ham post match/i }));
|
await clickAndWait(page, page.getByRole('gridcell', { name: /west ham post match/i }));
|
||||||
|
|
||||||
// Copy the preview link
|
// Check that preview opens in its own page
|
||||||
await page.getByRole('button', { name: /copy preview link/i }).click();
|
await clickAndWait(page, page.getByRole('link', { name: /open preview/i }));
|
||||||
await findAndClose(page, 'Copied preview link');
|
await expect(page.getByText('Preview will go here!')).toBeVisible();
|
||||||
|
|
||||||
// Check that preview opens in a new tab
|
|
||||||
const newTabPromiseDraft = page.waitForEvent('popup');
|
|
||||||
await page.getByRole('link', { name: /open preview/i }).click();
|
|
||||||
const newTab = await newTabPromiseDraft;
|
|
||||||
expect(newTab.url()).toMatch(/^https:\/\/strapi\.io\/preview\/api::article\.article.*\/draft$/);
|
|
||||||
|
|
||||||
// Check that preview link reflects the publication status
|
|
||||||
await page.getByRole('button', { name: /publish/i }).click();
|
|
||||||
await findAndClose(page, 'Published document');
|
|
||||||
await page.getByRole('tab', { name: /published/i }).click();
|
|
||||||
const newTabPromisePublished = page.waitForEvent('popup');
|
|
||||||
await page.getByRole('link', { name: /open preview/i }).click();
|
|
||||||
const newTab2 = await newTabPromisePublished;
|
|
||||||
expect(newTab2.url()).toMatch(
|
|
||||||
/^https:\/\/strapi\.io\/preview\/api::article\.article.*\/published$/
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Preview button should not appear for content types without preview config', async ({
|
test('Preview button should not appear for content types without preview config', async ({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user