From 69590e70d5f70cce6888a93931f5ea9d235c99d2 Mon Sep 17 00:00:00 2001
From: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>
Date: Tue, 27 Aug 2024 12:04:59 +0100
Subject: [PATCH] feat(i18n): add bulk unpublish locales edit view action
(#21030)
* feat(i18n): add bulk unpublish locales edit view action
* fix(e2e): i18n bulk actions
* chore(e2e): skip for webkit
* chore: clean up
* fix(i18n): fixes and e2e test coverage for bulk locale unpublish
* fix(e2e): content manager edit view
* fix(content-manager): incorporate edit view notification fix
* chore(i18n): clean up
---
.../core/admin/admin/src/translations/en.json | 1 +
.../EditView/components/DocumentActions.tsx | 60 ++++-----
.../admin/src/translations/en.json | 5 +-
.../src/controllers/collection-types.ts | 4 +-
.../shared/contracts/collection-types.ts | 6 +-
.../src/components/BulkLocaleActionModal.tsx | 117 +++++++++++------
.../admin/src/components/CMHeaderActions.tsx | 120 +++++++++++++-----
packages/plugins/i18n/admin/src/index.ts | 2 +
.../i18n/admin/src/translations/en.json | 1 +
.../tests/content-manager/editview.spec.ts | 20 ++-
tests/e2e/tests/i18n/editview.spec.ts | 107 ++++++++++++++++
11 files changed, 323 insertions(+), 120 deletions(-)
diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json
index 84ec7458b5..8305cac6c2 100644
--- a/packages/core/admin/admin/src/translations/en.json
+++ b/packages/core/admin/admin/src/translations/en.json
@@ -553,6 +553,7 @@
"app.utils.published": "Published",
"app.utils.ready-to-publish": "Ready to publish",
"app.utils.ready-to-publish-changes": "Ready to publish changes",
+ "app.utils.ready-to-unpublish-changes": "Ready to unpublish",
"app.confirm.body": "Are you sure?",
"clearLabel": "Clear",
"coming.soon": "This content is currently under construction and will be back in a few weeks!",
diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
index 4f1b5725a7..8112db5cbf 100644
--- a/packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
+++ b/packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
@@ -18,7 +18,7 @@ import {
Menu,
ButtonProps,
} from '@strapi/design-system';
-import { CrossCircle, More, WarningCircle } from '@strapi/icons';
+import { Cross, More, WarningCircle } from '@strapi/icons';
import { useIntl } from 'react-intl';
import { useMatch, useNavigate } from 'react-router-dom';
import { styled, DefaultTheme } from 'styled-components';
@@ -582,29 +582,29 @@ const PublishAction: DocumentActionComponent = ({
}, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
React.useEffect(() => {
- if (documentId && !isListView) {
- // We don't want to call count draft relations if in the list view. There is no
- // use for the response.
- const fetchDraftRelationsCount = async () => {
- const { data, error } = await countDraftRelations({
- collectionType,
- model,
- documentId,
- params,
- });
-
- if (error) {
- throw error;
- }
-
- if (data) {
- setServerCountOfDraftRelations(data.data);
- }
- };
-
- fetchDraftRelationsCount();
+ if (!document || !document.documentId || isListView) {
+ return;
}
- }, [isListView, documentId, countDraftRelations, collectionType, model, params]);
+
+ const fetchDraftRelationsCount = async () => {
+ const { data, error } = await countDraftRelations({
+ collectionType,
+ model,
+ documentId,
+ params,
+ });
+
+ if (error) {
+ throw error;
+ }
+
+ if (data) {
+ setServerCountOfDraftRelations(data.data);
+ }
+ };
+
+ fetchDraftRelationsCount();
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
const isDocumentPublished =
(document?.[PUBLISHED_AT_ATTRIBUTE_NAME] ||
@@ -909,7 +909,7 @@ const UnpublishAction: DocumentActionComponent = ({
id: 'app.utils.unpublish',
defaultMessage: 'Unpublish',
}),
- icon: ,
+ icon: ,
onClick: async () => {
/**
* return if there's no id & we're in a collection type, or the status modified
@@ -1043,7 +1043,7 @@ const DiscardAction: DocumentActionComponent = ({
id: 'content-manager.actions.discard.label',
defaultMessage: 'Discard changes',
}),
- icon: ,
+ icon: ,
position: ['panel', 'table-row'],
variant: 'danger',
dialog: {
@@ -1077,16 +1077,6 @@ const DiscardAction: DocumentActionComponent = ({
DiscardAction.type = 'discard';
-/**
- * Because the icon system is completely broken, we have to do
- * this to remove the fill from the cog.
- */
-const StyledCrossCircle = styled(CrossCircle)`
- path {
- fill: currentColor;
- }
-`;
-
const DEFAULT_ACTIONS = [PublishAction, UpdateAction, UnpublishAction, DiscardAction];
export { DocumentActions, DocumentActionsMenu, DocumentActionButton, DEFAULT_ACTIONS };
diff --git a/packages/core/content-manager/admin/src/translations/en.json b/packages/core/content-manager/admin/src/translations/en.json
index 14c79cd77b..80a977b63e 100644
--- a/packages/core/content-manager/admin/src/translations/en.json
+++ b/packages/core/content-manager/admin/src/translations/en.json
@@ -63,6 +63,7 @@
"components.SettingsViewWrapper.pluginHeader.description.list-settings": "Define the settings of the list view.",
"components.SettingsViewWrapper.pluginHeader.title": "Configure the view — {name}",
"bulk-publish.already-published": "Already Published",
+ "bulk-unpublish.already-unpublished": "Already Unpublished",
"bulk-publish.modified": "Ready to publish changes",
"components.TableDelete.delete": "Delete all",
"components.TableDelete.deleteSelected": "Delete selected",
@@ -104,8 +105,8 @@
"containers.list.items": "{number} {number, plural, =0 {items} one {item} other {items}}",
"containers.list.table.row-actions": "Row actions",
"containers.list.selectedEntriesModal.title": "Publish entries",
- "containers.list.selectedEntriesModal.selectedCount": "{alreadyPublishedCount} {alreadyPublishedCount, plural, =0 {entries} one {entry} other {entries}} already published. {readyToPublishCount} {readyToPublishCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. {withErrorsCount} {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action.",
- "containers.list.selectedEntriesModal.publishedCount": "{publishedCount} {publishedCount, plural, =0 {entries} one {entry} other {entries}} published. {withErrorsCount} {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action.",
+ "containers.list.selectedEntriesModal.selectedCount.publish": "{publishedCount} {publishedCount, plural, =0 {entries} one {entry} other {entries}} already published. {draftCount} {draftCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. {withErrorsCount} {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action.",
+ "containers.list.selectedEntriesModal.selectedCount.unpublish": "{draftCount} {draftCount, plural, =0 {entries} one {entry} other {entries}} already unpublished. {publishedCount} {publishedCount, plural, =0 {entries} one {entry} other {entries}} ready to unpublish.",
"containers.list.autoCloneModal.header": "Duplicate",
"containers.list.autoCloneModal.title": "This entry can't be duplicated directly.",
"containers.list.autoCloneModal.description": "A new entry will be created with the same content, but you'll have to change the following fields to save it.",
diff --git a/packages/core/content-manager/server/src/controllers/collection-types.ts b/packages/core/content-manager/server/src/controllers/collection-types.ts
index 92506e7876..1d321b48e8 100644
--- a/packages/core/content-manager/server/src/controllers/collection-types.ts
+++ b/packages/core/content-manager/server/src/controllers/collection-types.ts
@@ -510,7 +510,9 @@ export default {
return ctx.forbidden();
}
- const { locale } = await getDocumentLocaleAndStatus(body, model);
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
+ allowMultipleLocales: true,
+ });
const entityPromises = documentIds.map((documentId: any) =>
documentManager.findLocales(documentId, model, { locale, isPublished: true })
diff --git a/packages/core/content-manager/shared/contracts/collection-types.ts b/packages/core/content-manager/shared/contracts/collection-types.ts
index e3491627db..7cf3291297 100644
--- a/packages/core/content-manager/shared/contracts/collection-types.ts
+++ b/packages/core/content-manager/shared/contracts/collection-types.ts
@@ -315,7 +315,6 @@ export declare namespace BulkPublish {
};
query: {
// If not provided, the default locale will be used
- // Otherwise specify the locales to publish of the documents
locale?: string | string[] | null;
};
}
@@ -340,7 +339,10 @@ export declare namespace BulkUnpublish {
body: {
documentIds: Modules.Documents.ID[];
};
- query: {};
+ query: {
+ // If not provided, the default locale will be used
+ locale?: string | string[] | null;
+ };
}
export interface Params {
diff --git a/packages/plugins/i18n/admin/src/components/BulkLocaleActionModal.tsx b/packages/plugins/i18n/admin/src/components/BulkLocaleActionModal.tsx
index 6520a29b68..e8cb521f8f 100644
--- a/packages/plugins/i18n/admin/src/components/BulkLocaleActionModal.tsx
+++ b/packages/plugins/i18n/admin/src/components/BulkLocaleActionModal.tsx
@@ -23,6 +23,7 @@ type Status = Modules.Documents.Params.PublicationStatus.Kind | 'modified';
interface EntryValidationTextProps {
status: Status;
validationErrors: FormErrors[string] | null;
+ action: 'bulk-publish' | 'bulk-unpublish';
}
interface TranslationMessage extends MessageDescriptor {
@@ -35,7 +36,11 @@ const isErrorMessageDescriptor = (object?: string | object): object is Translati
);
};
-const EntryValidationText = ({ status = 'draft', validationErrors }: EntryValidationTextProps) => {
+const EntryValidationText = ({
+ status = 'draft',
+ validationErrors,
+ action,
+}: EntryValidationTextProps) => {
const { formatMessage } = useIntl();
/**
@@ -86,42 +91,67 @@ const EntryValidationText = ({ status = 'draft', validationErrors }: EntryValida
);
}
- if (status === 'published') {
- return (
-
-
-
- {formatMessage({
+ const getStatusMessage = () => {
+ if (action === 'bulk-publish') {
+ if (status === 'published') {
+ return {
+ icon: ,
+ text: formatMessage({
id: 'content-manager.bulk-publish.already-published',
defaultMessage: 'Already Published',
- })}
-
-
- );
- }
-
- if (status === 'modified') {
- return (
-
-
-
- {formatMessage({
+ }),
+ textColor: 'success600',
+ fontWeight: 'bold',
+ };
+ } else if (status === 'modified') {
+ return {
+ icon: ,
+ text: formatMessage({
id: 'app.utils.ready-to-publish-changes',
defaultMessage: 'Ready to publish changes',
- })}
-
-
- );
- }
+ }),
+ };
+ } else {
+ return {
+ icon: ,
+ text: formatMessage({
+ id: 'app.utils.ready-to-publish',
+ defaultMessage: 'Ready to publish',
+ }),
+ };
+ }
+ } else {
+ if (status === 'draft') {
+ return {
+ icon: ,
+ text: formatMessage({
+ id: 'content-manager.bulk-unpublish.already-unpublished',
+ defaultMessage: 'Already Unpublished',
+ }),
+ textColor: 'success600',
+ fontWeight: 'bold',
+ };
+ } else {
+ return {
+ icon: ,
+ text: formatMessage({
+ id: 'app.utils.ready-to-unpublish-changes',
+ defaultMessage: 'Ready to unpublish',
+ }),
+ textColor: 'success600',
+ fontWeight: 'bold',
+ };
+ }
+ }
+ };
+
+ const { icon, text, textColor = 'success600', fontWeight = 'normal' } = getStatusMessage();
return (
-
-
- {formatMessage({
- id: 'app.utils.ready-to-publish',
- defaultMessage: 'Ready to publish',
- })}
+ {icon}
+
+ {text}
);
@@ -145,14 +175,15 @@ interface BulkLocaleActionModalProps {
}[];
localesMetadata: Locale[];
validationErrors?: FormErrors;
+ action: 'bulk-publish' | 'bulk-unpublish';
}
const BulkLocaleActionModal = ({
headers,
rows,
localesMetadata,
-
validationErrors = {},
+ action,
}: BulkLocaleActionModalProps) => {
const { formatMessage } = useIntl();
@@ -168,11 +199,11 @@ const BulkLocaleActionModal = ({
}, {});
const localesWithErrors = Object.keys(validationErrors);
- const alreadyPublishedCount = selectedRows.filter(
+ const publishedCount = selectedRows.filter(
({ locale }) => currentStatusByLocale[locale] === 'published'
).length;
- const readyToPublishCount = selectedRows.filter(
+ const draftCount = selectedRows.filter(
({ locale }) =>
(currentStatusByLocale[locale] === 'draft' ||
currentStatusByLocale[locale] === 'modified') &&
@@ -180,17 +211,25 @@ const BulkLocaleActionModal = ({
).length;
const withErrorsCount = localesWithErrors.length;
+ const messageId =
+ action === 'bulk-publish'
+ ? 'content-manager.containers.list.selectedEntriesModal.selectedCount.publish'
+ : 'content-manager.containers.list.selectedEntriesModal.selectedCount.unpublish';
+
+ const defaultMessage =
+ action === 'bulk-publish'
+ ? '{publishedCount} {publishedCount, plural, =0 {entries} one {entry} other {entries}} already published. {draftCount} {draftCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. {withErrorsCount} {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action.'
+ : '{draftCount} {draftCount, plural, =0 {entries} one {entry} other {entries}} already unpublished. {publishedCount} {publishedCount, plural, =0 {entries} one {entry} other {entries}} ready to unpublish.';
return formatMessage(
{
- id: 'content-manager.containers.list.selectedEntriesModal.selectedCount',
- defaultMessage:
- '{alreadyPublishedCount} {alreadyPublishedCount, plural, =0 {entries} one {entry} other {entries}} already published. {readyToPublishCount} {readyToPublishCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. {withErrorsCount} {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action.',
+ id: messageId,
+ defaultMessage,
},
{
withErrorsCount,
- readyToPublishCount,
- alreadyPublishedCount,
+ draftCount,
+ publishedCount,
b: BoldChunk,
}
);
@@ -243,7 +282,7 @@ const BulkLocaleActionModal = ({
-
+
{
+ action,
+}: ExtendedDocumentActionProps) => {
const baseLocale = baseDocument?.locale ?? null;
const [{ query }] = useQueryParams<{ status: 'draft' | 'published' }>();
const params = React.useMemo(() => buildValidParams(query), [query]);
- const isPublishedTab = query.status === 'published';
+ const isOnPublishedTab = query.status === 'published';
const { formatMessage } = useIntl();
const { hasI18n, canPublish } = useI18n();
@@ -270,7 +278,9 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] =
React.useState(false);
- const { publishMany: publishManyAction } = useDocumentActions();
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } =
+ useDocumentActions();
+
const {
document,
meta: documentMeta,
@@ -332,6 +342,7 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
return { locale, status };
});
+
rowsFromMeta.unshift({
locale: document.locale,
status: document.status,
@@ -355,16 +366,26 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
return [rowsFromMeta, errors];
}, [document, documentMeta?.availableLocales, validate]);
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
- if (
- selectedRow.status !== 'published' &&
- !Object.keys(validationErrors).includes(selectedRow.locale)
- ) {
+ const isBulkPublish = action === 'bulk-publish';
+ const localesForAction = selectedRows.reduce((acc: string[], selectedRow: LocaleStatus) => {
+ const isValidLocale =
+ // Validation errors are irrelevant if we are trying to unpublish
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale);
+
+ const shouldAddLocale = isBulkPublish
+ ? selectedRow.status !== 'published' && isValidLocale
+ : selectedRow.status !== 'draft' && isValidLocale;
+
+ if (shouldAddLocale) {
acc.push(selectedRow.locale);
}
+
return acc;
}, []);
+ // TODO skipping this for now as there is a bug with the draft relation count that will be worked on separately
+ // see https://www.notion.so/strapi/Count-draft-relations-56901b492efb45ab90d42fe975b32bd8?pvs=4
+ const enableDraftRelationsCount = false;
const {
data: draftRelationsCount = 0,
isLoading: isDraftRelationsLoading,
@@ -373,10 +394,10 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
{
model,
documentIds: [documentId!],
- locale: localesToPublish,
+ locale: localesForAction,
},
{
- skip: !documentId || localesToPublish.length === 0,
+ skip: !enableDraftRelationsCount || !documentId || localesForAction.length === 0,
}
);
@@ -410,7 +431,20 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
documentIds: [documentId],
params: {
...params,
- locale: localesToPublish,
+ locale: localesForAction,
+ },
+ });
+
+ setSelectedRows([]);
+ };
+
+ const unpublish = async () => {
+ await unpublishManyAction({
+ model,
+ documentIds: [documentId],
+ params: {
+ ...params,
+ locale: localesForAction,
},
});
@@ -420,17 +454,13 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
const handleAction = async () => {
if (draftRelationsCount > 0) {
setIsDraftRelationConfirmationOpen(true);
- } else {
+ } else if (isBulkPublish) {
await publish();
+ } else {
+ await unpublish();
}
};
- const isUnpublish = document?.status === 'published';
- if (isUnpublish) {
- // TODO: For now we still proceed so we have the bulk locale publish action in all cases
- console.warn(['I18N'], 'Bulk locale unpublish modal not implemented');
- }
-
if (isDraftRelationConfirmationOpen) {
return {
label: formatMessage({
@@ -480,18 +510,18 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
return {
label: formatMessage({
- id: getTranslation('CMEditViewBulkLocale.publish-title'),
- defaultMessage: 'Publish Multiple Locales',
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? 'publish' : 'unpublish'}-title`),
+ defaultMessage: `${isBulkPublish ? 'Publish' : 'Unpublish'} Multiple Locales`,
}),
- icon: ,
- disabled: isPublishedTab || canPublish.length === 0,
+ variant: isBulkPublish ? 'secondary' : 'danger',
+ icon: isBulkPublish ? : ,
+ disabled: isOnPublishedTab || canPublish.length === 0,
position: ['panel'],
- variant: 'secondary',
dialog: {
type: 'modal',
title: formatMessage({
- id: getTranslation('CMEditViewBulkLocale.publish-title'),
- defaultMessage: 'Publish Multiple Locales',
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? 'publish' : 'unpublish'}-title`),
+ defaultMessage: `${isBulkPublish ? 'Publish' : 'Unpublish'} Multiple Locales`,
}),
content: () => {
return (
@@ -509,6 +539,7 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
headers={headers}
rows={rows}
localesMetadata={localesMetadata as Locale[]}
+ action={action ?? 'bulk-publish'}
/>
);
@@ -517,13 +548,13 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
@@ -532,6 +563,20 @@ const BulkLocalePublishAction: DocumentActionComponent = ({
};
};
+/* -------------------------------------------------------------------------------------------------
+ * BulkLocalePublishAction
+ * -----------------------------------------------------------------------------------------------*/
+const BulkLocalePublishAction: DocumentActionComponent = (props: ExtendedDocumentActionProps) => {
+ return BulkLocaleAction({ action: 'bulk-publish', ...props });
+};
+
+/* -------------------------------------------------------------------------------------------------
+ * BulkLocaleUnpublishAction
+ * -----------------------------------------------------------------------------------------------*/
+const BulkLocaleUnpublishAction: DocumentActionComponent = (props: ExtendedDocumentActionProps) => {
+ return BulkLocaleAction({ action: 'bulk-unpublish', ...props });
+};
+
/**
* Because the icon system is completely broken, we have to do
* this to remove the fill from the cog.
@@ -542,4 +587,9 @@ const StyledTrash = styled(Trash)`
}
`;
-export { BulkLocalePublishAction, DeleteLocaleAction, LocalePickerAction };
+export {
+ BulkLocalePublishAction,
+ BulkLocaleUnpublishAction,
+ DeleteLocaleAction,
+ LocalePickerAction,
+};
diff --git a/packages/plugins/i18n/admin/src/index.ts b/packages/plugins/i18n/admin/src/index.ts
index 5d6ab2e181..1f62f41f92 100644
--- a/packages/plugins/i18n/admin/src/index.ts
+++ b/packages/plugins/i18n/admin/src/index.ts
@@ -4,6 +4,7 @@ import * as yup from 'yup';
import { CheckboxConfirmation } from './components/CheckboxConfirmation';
import {
BulkLocalePublishAction,
+ BulkLocaleUnpublishAction,
DeleteLocaleAction,
LocalePickerAction,
} from './components/CMHeaderActions';
@@ -79,6 +80,7 @@ export default {
// When enabled the bulk locale publish action should be the first action
// in 'More Document Actions' and therefore the third action in the array
actions.splice(2, 0, BulkLocalePublishAction);
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
return actions;
});
diff --git a/packages/plugins/i18n/admin/src/translations/en.json b/packages/plugins/i18n/admin/src/translations/en.json
index 07830e003a..52270b2e58 100644
--- a/packages/plugins/i18n/admin/src/translations/en.json
+++ b/packages/plugins/i18n/admin/src/translations/en.json
@@ -8,6 +8,7 @@
"CMEditViewCopyLocale.copy-text": "Fill in from another locale",
"CMEditViewCopyLocale.submit-text": "Yes, fill in",
"CMEditViewBulkLocale.publish-title": "Publish Multiple Locales",
+ "CMEditViewBulkLocale.unpublish-title": "Unpublish Multiple Locales",
"CMEditViewBulkLocale.status": "Status",
"CMEditViewBulkLocale.publication-status": "Publication Status",
"CMEditViewBulkLocale.draft-relation-warning": "Some locales are related to draft entries. Publishing them could leave broken links in your app.",
diff --git a/tests/e2e/tests/content-manager/editview.spec.ts b/tests/e2e/tests/content-manager/editview.spec.ts
index fb20882bd3..8a0f31aee9 100644
--- a/tests/e2e/tests/content-manager/editview.spec.ts
+++ b/tests/e2e/tests/content-manager/editview.spec.ts
@@ -137,7 +137,9 @@ test.describe('Edit View', () => {
await expect(page.getByRole('button', { name: 'More document actions' })).not.toBeDisabled();
await page.getByRole('button', { name: 'More document actions' }).click();
- await expect(page.getByRole('menuitem', { name: 'Unpublish' })).not.toBeDisabled();
+ await expect(
+ page.getByRole('menuitem', { name: 'Unpublish', exact: true })
+ ).not.toBeDisabled();
await expect(page.getByRole('menuitem', { name: 'Discard changes' })).toBeDisabled();
await page.keyboard.press('Escape'); // close the menu since we're not actioning on it atm.
@@ -330,8 +332,10 @@ test.describe('Edit View', () => {
await expect(page.getByRole('tab', { name: 'Published' })).not.toBeDisabled();
await page.getByRole('button', { name: 'More document actions' }).click();
- await expect(page.getByRole('menuitem', { name: 'Unpublish' })).not.toBeDisabled();
- await page.getByRole('menuitem', { name: 'Unpublish' }).click();
+ await expect(
+ page.getByRole('menuitem', { name: 'Unpublish', exact: true })
+ ).not.toBeDisabled();
+ await page.getByRole('menuitem', { name: 'Unpublish', exact: true }).click();
await findAndClose(page, 'Unpublished Document');
@@ -483,7 +487,9 @@ test.describe('Edit View', () => {
await expect(page.getByRole('button', { name: 'More document actions' })).not.toBeDisabled();
await page.getByRole('button', { name: 'More document actions' }).click();
- await expect(page.getByRole('menuitem', { name: 'Unpublish' })).not.toBeDisabled();
+ await expect(
+ page.getByRole('menuitem', { name: 'Unpublish', exact: true })
+ ).not.toBeDisabled();
await expect(page.getByRole('menuitem', { name: 'Discard changes' })).toBeDisabled();
await page.keyboard.press('Escape'); // close the menu since we're not actioning on it atm.
@@ -656,8 +662,10 @@ test.describe('Edit View', () => {
await expect(page.getByRole('tab', { name: 'Published' })).not.toBeDisabled();
await page.getByRole('button', { name: 'More document actions' }).click();
- await expect(page.getByRole('menuitem', { name: 'Unpublish' })).not.toBeDisabled();
- await page.getByRole('menuitem', { name: 'Unpublish' }).click();
+ await expect(
+ page.getByRole('menuitem', { name: 'Unpublish', exact: true })
+ ).not.toBeDisabled();
+ await page.getByRole('menuitem', { name: 'Unpublish', exact: true }).click();
await findAndClose(page, 'Unpublished Document');
diff --git a/tests/e2e/tests/i18n/editview.spec.ts b/tests/e2e/tests/i18n/editview.spec.ts
index 7f030345f9..0fbee91417 100644
--- a/tests/e2e/tests/i18n/editview.spec.ts
+++ b/tests/e2e/tests/i18n/editview.spec.ts
@@ -327,6 +327,113 @@ test.describe('Edit view', () => {
).toBeDisabled();
});
+ test('As a user I want to unpublish multiple locales of my document', async ({
+ page,
+ browser,
+ }) => {
+ if (browser.browserType().name() === 'webkit') {
+ // See DX-1550
+ return test.fixme();
+ }
+
+ const LIST_URL = /\/admin\/content-manager\/collection-types\/api::article.article(\?.*)?/;
+ const EDIT_URL =
+ /\/admin\/content-manager\/collection-types\/api::article.article\/[^/]+(\?.*)?/;
+
+ /**
+ * Navigate to our articles list-view where there will be one document already made in the `en` locale
+ */
+ await page.getByRole('link', { name: 'Content Manager' }).click();
+ await page.getByRole('link', { name: 'Article' }).click();
+ await page.waitForURL(LIST_URL);
+ await expect(page.getByRole('heading', { name: 'Article' })).toBeVisible();
+
+ /**
+ * Assert we're on the english locale and our document exists
+ */
+ await expect(page.getByRole('combobox', { name: 'Select a locale' })).toHaveText(
+ 'English (en)'
+ );
+ await expect(
+ page.getByRole('row', { name: 'Why I prefer football over soccer' })
+ ).toBeVisible();
+ await page.getByRole('row', { name: 'Why I prefer football over soccer' }).click();
+
+ /**
+ * Create a new spanish draft article
+ */
+ await page.waitForURL(EDIT_URL);
+ await expect(
+ page.getByRole('heading', { name: 'Why I prefer football over soccer' })
+ ).toBeVisible();
+
+ /**
+ * Publish the english article
+ */
+ await page.getByRole('button', { name: 'Publish' }).click();
+ await findAndClose(page, 'Success:Published');
+
+ await page.getByRole('combobox', { name: 'Locales' }).click();
+ await page.getByRole('option', { name: 'Spanish (es)' }).click();
+
+ /**
+ * Now we should be on a new document in the `es` locale
+ */
+ expect(new URL(page.url()).searchParams.get('plugins[i18n][locale]')).toEqual('es');
+ await expect(page.getByRole('heading', { name: 'Untitled' })).toBeVisible();
+
+ /**
+ * This is here because the `fill` method below doesn't immediately update the value
+ * in webkit.
+ */
+ if (browser.browserType().name() === 'webkit') {
+ await page.getByRole('textbox', { name: 'title' }).press('s');
+ await page.getByRole('textbox', { name: 'title' }).press('Delete');
+ }
+
+ await page.getByRole('textbox', { name: 'title' }).fill('Por qué prefiero el fútbol al fútbol');
+
+ /**
+ * Save the spanish draft
+ */
+ await page.getByRole('button', { name: 'Save' }).click();
+ await findAndClose(page, 'Success:Saved');
+
+ /**
+ * Publish the spanish article
+ */
+ await page.getByRole('button', { name: 'Publish' }).click();
+ await findAndClose(page, 'Success:Published');
+
+ /**
+ * Open the bulk locale unpublish modal
+ */
+ await page.getByText('More document actions').click();
+ await page.getByRole('menuitem', { name: 'Unpublish Multiple Locales', exact: true }).click();
+
+ // Select all locales, assert there are 2 entries ready to unpublish and unpublish them
+ await page
+ .getByRole('row', { name: 'Select all entries Name' })
+ .getByLabel('Select all entries')
+ .click();
+
+ /**
+ * Unpublish the articles
+ */
+ await expect(page.getByText('2 entries ready to unpublish')).toBeVisible();
+ await page
+ .getByLabel('Unpublish Multiple Locales')
+ .getByRole('button', { name: 'Unpublish' })
+ .click();
+
+ // Assert that all locales are now unpublished
+ await expect(page.getByRole('gridcell', { name: 'Draft' })).toHaveCount(2);
+
+ await expect(
+ page.getByLabel('Unpublish Multiple Locales').getByRole('button', { name: 'Unpublish' })
+ ).toBeDisabled();
+ });
+
interface ValidationType {
field: string;
initialValue: string;