Skip to content

Commit

Permalink
Confirm publish modal (#2401)
Browse files Browse the repository at this point in the history
* task: open confirm publish modal on publish click

* task: update save and publish flow

* task: do not take into consideraation seconds when checking for publish status

* task: update tests

* task: conditionally render componet

* task: set dialog width
  • Loading branch information
finnar-bin authored Dec 26, 2023
1 parent c38ecd1 commit 96642e2
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 26 deletions.
38 changes: 19 additions & 19 deletions cypress/e2e/content/actions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,27 +57,27 @@ describe("Actions in content editor", () => {
cy.get("[data-cy=toast]").contains("Saved a new ");
});

// TODO: get publishing working in Dev environment
it.skip("Publishes an item", () => {
cy.get("#PublishButton").click();
cy.contains("Published version", { timeout: 5000 }).should("exist");
cy.get("#PublishButton").should("be.disabled");
// TODO: fix race condition
// TODO: fix isScheduled/isPublished race condition so it never appears as isScheduled here
// cy.get("#PublishScheduleButton").should("be.disabled");
it("Publishes an item", () => {
cy.getBySelector("PublishButton").click();
cy.getBySelector("ConfirmPublishModal").should("exist");
cy.getBySelector("ConfirmPublishButton").click();

cy.intercept("GET", "**/publishings").as("publish");
cy.wait("@publish");

cy.getBySelector("ContentPublishedIndicator").should("exist");
});

// TODO: get publishing working in Dev environment
it.skip("Unpublishes an item", () => {
// go to Content Tab
cy.get("[data-cy=SubApp]").click();
cy.get("article.Unpublish").click();
// TODO: fix race condition so unpublish will not be disabled
// cy.get("#UnpublishItemButton").should.not("be.disabled");
cy.get("#UnpublishItemButton").click({ force: true });
cy.contains("Unpublished Item", {
timeout: 5000,
}).should("exist");
it("Unpublishes an item", () => {
cy.getBySelector("ContentPublishedIndicator").should("exist");
cy.getBySelector("PublishMenuButton").click();
cy.getBySelector("UnpublishContentButton").click({ force: true });
cy.getBySelector("ConfirmUnpublishButton").click();

cy.intercept("GET", "**/publishings").as("publish");
cy.wait("@publish");

cy.getBySelector("PublishButton").should("exist");
});

// TODO: fix race condition so schedule publish will work
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
Typography,
Box,
Stack,
} from "@mui/material";
import CloudUploadRoundedIcon from "@mui/icons-material/CloudUploadRounded";

type ConfirmPublishModal = {
contentTitle: string;
onCancel: () => void;
onConfirm: () => void;
};
export const ConfirmPublishModal = ({
contentTitle,
onCancel,
onConfirm,
}: ConfirmPublishModal) => {
return (
<Dialog
open
data-cy="ConfirmPublishModal"
PaperProps={{ sx: { width: 480 } }}
>
<DialogTitle component="div" sx={{ pb: 1 }}>
<Stack
height={40}
width={40}
bgcolor="green.100"
borderRadius="50%"
justifyContent="center"
alignItems="center"
marginBottom={1.5}
>
<CloudUploadRoundedIcon color="success" />
</Stack>
<Box>
<Typography fontWeight={700} variant="h5" display="inline">
Publish Content Item:
</Typography>{" "}
<Typography variant="h5" display="inline">
{contentTitle}?
</Typography>
</Box>
</DialogTitle>
<DialogContent>
<Typography variant="body2" color="text.secondary">
This will make the item immediately available on all of your
platforms. You can always unpublish this item later if needed.
</Typography>
</DialogContent>
<DialogActions>
<Button variant="text" color="inherit" onClick={onCancel}>
Cancel
</Button>
<Button
variant="contained"
color="success"
sx={{ color: "common.white" }}
onClick={onConfirm}
data-cy="ConfirmPublishButton"
>
Publish Item
</Button>
</DialogActions>
</Dialog>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from "../../../../../../../../shell/services/instance";
import { useHistory, useParams } from "react-router";
import { useEffect, useState } from "react";

import {
ContentItem,
Publishing,
Expand All @@ -42,6 +43,7 @@ import { ScheduleFlyout } from "../Header/ItemVersioning/ScheduleFlyout";
import { UnpublishDialog } from "./UnpublishDialog";
import { usePermission } from "../../../../../../../../shell/hooks/use-permissions";
import { ContentItemWithDirtyAndPublishing } from ".";
import { ConfirmPublishModal } from "./ConfirmPublishModal";

const ITEM_STATES = {
dirty: "dirty",
Expand Down Expand Up @@ -72,6 +74,8 @@ export const ItemEditHeaderActions = ({
const [scheduledPublishDialogOpen, setScheduledPublishDialogOpen] =
useState(false);
const [scheduleAfterSave, setScheduleAfterSave] = useState(false);
const [isConfirmPublishModalOpen, setIsConfirmPublishModalOpen] =
useState(false);
const item = useSelector(
(state: AppState) =>
state.content[itemZUID] as ContentItemWithDirtyAndPublishing
Expand Down Expand Up @@ -104,7 +108,7 @@ export const ItemEditHeaderActions = ({
setPublishAfterSave(true);
onSave();
} else {
handlePublish();
setIsConfirmPublishModalOpen(true);
}
});

Expand Down Expand Up @@ -162,7 +166,7 @@ export const ItemEditHeaderActions = ({
activePublishing?.version !== item?.meta.version &&
!saving
) {
handlePublish();
setIsConfirmPublishModalOpen(true);
setPublishAfterSave(false);
}
}, [item, publishAfterSave, saving, activePublishing]);
Expand Down Expand Up @@ -284,13 +288,14 @@ export const ItemEditHeaderActions = ({
setPublishAfterSave(true);
onSave();
} else {
handlePublish();
setIsConfirmPublishModalOpen(true);
}
}}
loading={publishing || publishAfterSave || isFetching}
color="success"
variant="contained"
id="PublishButton"
data-cy="PublishButton"
>
{itemState === ITEM_STATES.dirty ? "Save & Publish" : "Publish"}
</LoadingButton>
Expand All @@ -311,7 +316,13 @@ export const ItemEditHeaderActions = ({
</Button>
</ButtonGroup>
) : (
<Box display="flex" alignItems="center" pl="10px" pr="4px">
<Box
data-cy="ContentPublishedIndicator"
display="flex"
alignItems="center"
pl="10px"
pr="4px"
>
<Box display="flex" gap={1} alignItems="center">
<CheckCircleRounded fontSize="small" color="success" />
<Typography
Expand All @@ -324,6 +335,7 @@ export const ItemEditHeaderActions = ({
</Typography>
</Box>
<IconButton
data-cy="PublishMenuButton"
size="small"
onClick={(e) => {
setPublishMenu(e.currentTarget);
Expand Down Expand Up @@ -389,7 +401,7 @@ export const ItemEditHeaderActions = ({
setScheduleAfterSave={setScheduleAfterSave}
setUnpublishDialogOpen={setUnpublishDialogOpen}
setScheduledPublishDialogOpen={setScheduledPublishDialogOpen}
handlePublish={handlePublish}
handlePublish={() => setIsConfirmPublishModalOpen(true)}
/>
{unpublishDialogOpen && (
<UnpublishDialog
Expand All @@ -405,6 +417,16 @@ export const ItemEditHeaderActions = ({
dispatch={dispatch}
toggleOpen={() => setScheduledPublishDialogOpen(false)}
/>
{isConfirmPublishModalOpen && (
<ConfirmPublishModal
contentTitle={item?.web?.metaTitle}
onCancel={() => setIsConfirmPublishModalOpen(false)}
onConfirm={() => {
setIsConfirmPublishModalOpen(false);
handlePublish();
}}
/>
)}
</>
);
};
Expand Down Expand Up @@ -471,6 +493,9 @@ const PublishingMenu = ({

onClose();
}}
data-cy={
itemState === ITEM_STATES.published ? "UnpublishContentButton" : ""
}
>
<ListItemIcon>
{itemState === ITEM_STATES.dirty ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const UnpublishDialog = ({
Cancel
</Button>
<LoadingButton
data-cy="ConfirmUnpublishButton"
variant="contained"
color="error"
aria-label="Delete Button"
Expand Down
4 changes: 2 additions & 2 deletions src/shell/store/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -860,8 +860,8 @@ function parsePublishState(records) {
If `publishAt` is set but unpublishAt is not you only need to determine if `nowGMT` is after `publishAt`. a.k.a is it live
If both `publishAt` and `unpublishAt` are set you need determine all 3 states.
**/
const nowGMT = moment.utc();
const publishAtGMT = moment(record.publishAt);
const nowGMT = moment.utc().startOf("minute");
const publishAtGMT = moment(record.publishAt).startOf("minute");
const unpublishAtGMT = moment(record.unpublishAt);

// Current time is before publishAt so it is scheduled
Expand Down

0 comments on commit 96642e2

Please sign in to comment.