diff --git a/src/components/DataSubmissions/DataSubmissionSummary.test.tsx b/src/components/DataSubmissions/DataSubmissionSummary.test.tsx new file mode 100644 index 00000000..927246ab --- /dev/null +++ b/src/components/DataSubmissions/DataSubmissionSummary.test.tsx @@ -0,0 +1,313 @@ +import "@testing-library/jest-dom"; +import "jest-axe/extend-expect"; +import { render, fireEvent, waitFor } from "@testing-library/react"; +import { act } from 'react-dom/test-utils'; +import { BrowserRouter } from "react-router-dom"; +import { FC, useMemo } from "react"; +import { axe } from "jest-axe"; +import DataSubmissionSummary from "./DataSubmissionSummary"; +import HistoryIconMap from "./DataSubmissionIconMap"; + +type Props = { + dataSubmission: object; +}; + +const BaseComponent: FC = ({ dataSubmission = {} }: Props) => { + const value = useMemo( + () => ({ + dataSubmission: dataSubmission as Submission, + }), + [dataSubmission] + ); + + return ( + + + + ); +}; + +describe("DataSubmissionSummary Accessibility Tests", () => { + it("has no accessibility violations when there are review comments", async () => { + const dataSubmission = { + history: [ + { + reviewComment: "This is a review comment", + }, + ], + }; + + const { container } = render( + + ); + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); +}); + +describe("DataSubmissionSummary Review Comments Dialog Tests", () => { + it("renders the Review Comments button if there is a review comment", () => { + const dataSubmission = { + history: [ + { + reviewComment: "This is a review comment", + }, + ], + }; + + const { getByText } = render( + + ); + expect(getByText("Review Comments")).toBeVisible(); + }); + + it("shows the correct content in the Review Comments dialog", async () => { + const dataSubmission = { + history: [ + { + status: "Rejected", + reviewComment: "This is the most recent review comment", + dateTime: "2023-11-30T11:26:01Z", + }, + ], + }; + + const { getByText } = render( + + ); + + act(() => { + fireEvent.click(getByText("Review Comments")); + }); + + await waitFor(() => { + expect(getByText(/This is the most recent review comment/)).toBeVisible(); + }); + }); + + it("only shows the review comment for the latest 'Rejected' submission, ignoring other statuses", async () => { + const dataSubmission = { + history: [ + { + status: "Rejected", + reviewComment: "This is a rejected comment", + dateTime: "2023-11-29T11:26:01Z", + }, + { + status: "Submitted", + reviewComment: "Admin Submit - This should not be displayed", + dateTime: "2023-11-30T11:26:01Z", + }, + ], + }; + + const { getByText } = render( + + ); + + act(() => { + fireEvent.click(getByText("Review Comments")); + }); + + await waitFor(() => { + expect(getByText(/This is a rejected comment/)).toBeVisible(); + expect(() => getByText(/This should not be displayed/)).toThrow(); + }); + }); + + it("closes the Review Comments dialog with the close button", async () => { + const dataSubmission = { + history: [ + { + status: "Rejected", + reviewComment: "Comment for closing test", + dateTime: "2023-11-30T11:26:01Z", + }, + ], + }; + + const { getByText, queryByText } = render( + + ); + + act(() => { + fireEvent.click(getByText("Review Comments")); + }); + await waitFor(() => expect(getByText("Comment for closing test")).toBeVisible()); + + act(() => { + fireEvent.click(getByText("Close")); + }); + await waitFor(() => expect(queryByText("Comment for closing test")).not.toBeInTheDocument()); + }); + + it("closes the Review Comments dialog with the close icon button", async () => { + const dataSubmission = { + history: [ + { + status: "Rejected", + reviewComment: "Another comment for close icon test", + dateTime: "2023-11-30T11:26:01Z", + }, + ], + }; + + const { getByText, queryByText, getByTestId } = render( + + ); + + act(() => { + fireEvent.click(getByText("Review Comments")); + }); + await waitFor(() => expect(getByText("Another comment for close icon test")).toBeVisible()); + + const closeButton = getByTestId("review-comments-dialog-close-icon-button"); + act(() => { + fireEvent.click(closeButton); + }); + await waitFor(() => expect(queryByText("Another comment for close icon test")).not.toBeInTheDocument()); + }); +}); + +describe("DataSubmissionSummary History Dialog Tests", () => { + it("renders the Full History button if there are historical events", () => { + const dataSubmission = { + history: [{ dateTime: "2023-11-23T14:26:01Z" }], + }; + + const { getByText } = render( + + ); + expect(getByText("Full History")).toBeVisible(); + }); + + it("renders the history events correctly in the dialog", async () => { + const dataSubmission: RecursivePartial = { + history: [ + { dateTime: "2023-11-30T11:26:01Z", status: "Submitted" }, + { dateTime: "2023-11-25T10:00:00Z", status: "In Progress" }, + ], + }; + + const { getByText } = render( + + ); + + act(() => { + fireEvent.click(getByText("Full History")); + }); + + await waitFor(() => { + expect(getByText("SUBMITTED")).toBeVisible(); + expect(getByText("IN PROGRESS")).toBeVisible(); + }); + }); + + it("renders the modal and displays history events in descending order", async () => { + const dataSubmission = { + history: [ + { dateTime: "2023-01-02T10:00:00Z", status: "In Progress" }, + { dateTime: "2023-01-01T10:00:00Z", status: "New" }, + { dateTime: "2023-01-03T10:00:00Z", status: "Submitted" }, + { dateTime: "2023-01-04T10:00:00Z", status: "Rejected" }, + { dateTime: "2023-01-05T10:00:00Z", status: "In Progress" }, + { dateTime: "2023-01-06T10:00:00Z", status: "Submitted" }, + { dateTime: "2023-01-07T10:00:00Z", status: "Released" }, + { dateTime: "2023-01-08T10:00:00Z", status: "Completed" }, + { dateTime: "2023-01-09T10:00:00Z", status: "Archived" }, + ], + }; + + const { getAllByTestId, getByText } = render(); + + act(() => { + fireEvent.click(getByText("Full History")); + }); + + const elements = getAllByTestId("history-item"); + expect(elements[0]).toHaveTextContent(/ARCHIVED/i); + expect(elements[0]).toHaveTextContent("1/9/2023"); + expect(elements[1]).toHaveTextContent(/COMPLETED/i); + expect(elements[1]).toHaveTextContent("1/8/2023"); + expect(elements[2]).toHaveTextContent(/RELEASED/i); + expect(elements[2]).toHaveTextContent("1/7/2023"); + expect(elements[8]).toHaveTextContent(/NEW/i); + expect(elements[8]).toHaveTextContent("1/1/2023"); + }); + + it("closes the History dialog with the close button", async () => { + const dataSubmission = { + history: [{ dateTime: "2023-11-30T11:26:01Z", status: "Submitted" }], + }; + + const { getByText, queryByTestId } = render(); + + act(() => { + fireEvent.click(getByText("Full History")); + }); + await waitFor(() => expect(queryByTestId("history-dialog")).toBeVisible()); + + act(() => { + fireEvent.click(queryByTestId("history-dialog-close")); + }); + await waitFor(() => expect(queryByTestId("history-dialog")).not.toBeInTheDocument()); + }); + + it("sorts the historical events by date in descending order", async () => { + const dataSubmission = { + history: [ + { dateTime: "2023-11-20T10:00:00Z", status: "New" }, + { dateTime: "2023-11-22T10:00:00Z", status: "In Progress" }, + { dateTime: "2023-11-24T10:00:00Z", status: "Submitted" } + ], + }; + + const { getByText, getAllByTestId } = render(); + + act(() => { + fireEvent.click(getByText("Full History")); + }); + + await waitFor(() => { + const items = getAllByTestId("history-item-date"); + expect(new Date(items[0].textContent).getTime()).toBeGreaterThan(new Date(items[1].textContent).getTime()); + expect(new Date(items[1].textContent).getTime()).toBeGreaterThan(new Date(items[2].textContent).getTime()); + }); + }); + + it("renders only the most recent event with an icon", () => { + const dataSubmission = { + history: [ + { dateTime: "2023-11-24T01:25:45Z", status: "Rejected" }, + { dateTime: "2023-11-22T15:36:01Z", status: "Completed" }, + ], + }; + + const { getByTestId, getByText } = render(); + + act(() => { + fireEvent.click(getByText("Full History")); + }); + + expect(getByTestId("history-item-0-icon")).toBeVisible(); + expect(() => getByTestId("history-item-1-icon")).toThrow(); + }); + + it.each(Object.entries(HistoryIconMap))("renders the correct icon for the status %s", (status, svg) => { + const dataSubmission = { + history: [{ dateTime: "2023-11-24T01:25:45Z", status }], + }; + + const { getByTestId, getByText } = render(); + + act(() => { + fireEvent.click(getByText("Full History")); + }); + + const icon = getByTestId("history-item-0-icon"); + + expect(icon).toBeVisible(); + expect(icon).toHaveAttribute("alt", `${status} icon`); + expect(icon).toHaveAttribute("src", svg); + }); +}); diff --git a/src/components/DataSubmissions/DataSubmissionSummary.tsx b/src/components/DataSubmissions/DataSubmissionSummary.tsx index 443d5545..18b20ba6 100644 --- a/src/components/DataSubmissions/DataSubmissionSummary.tsx +++ b/src/components/DataSubmissions/DataSubmissionSummary.tsx @@ -6,7 +6,7 @@ import { Typography, styled, } from "@mui/material"; -import { FC, useEffect, useRef, useState } from "react"; +import { FC, useEffect, useMemo, useRef, useState } from "react"; import SubmissionHeaderProperty, { StyledValue, } from "./SubmissionHeaderProperty"; @@ -14,11 +14,13 @@ import Tooltip from "./Tooltip"; import { ReactComponent as EmailIconSvg } from "../../assets/icons/email_icon.svg"; import HistoryDialog from "../Shared/HistoryDialog"; import DataSubmissionIconMap from "./DataSubmissionIconMap"; +import ReviewCommentsDialog from "../Shared/ReviewCommentsDialog"; +import { SortHistory } from "../../utils"; const StyledSummaryWrapper = styled("div")(() => ({ borderRadius: "8px 8px 0px 0px", textWrap: "nowrap", - padding: "25px 21px 59px 48px", + padding: "21px 21px 31px 48px", })); const StyledSubmissionTitle = styled(Typography)(() => ({ @@ -42,35 +44,71 @@ const StyledSubmissionStatus = styled(Typography)(() => ({ minHeight: "30px", })); -const StyledHistoryButton = styled(Button)(() => ({ +const StyledButtonWrapper = styled(Stack)(() => ({ + flexDirection: "column", + justifyContent: "flex-start", + alignItems: "flex-start", + gap: "9px", + paddingLeft: "5px", +})); + +const StyledReviewCommentsButton = styled(Button)(() => ({ "&.MuiButton-root": { + minWidth: "168px", marginTop: "16px", + padding: "11px 10px", + border: "1px solid #B3B3B3", + color: "#BE4511", + fontFamily: "'Nunito', 'Rubik', sans-serif", + fontSize: "16px", + fontStyle: "normal", + fontWeight: 700, + lineHeight: "17px", + letterSpacing: "0.32px", + "&:hover": { + backgroundColor: "#FFF", + borderColor: "#B3B3B3", + color: "#BE4511", + }, + "&:disabled": { + backgroundColor: "#FFF", + borderColor: "#B3B3B3", + color: "#B1B1B1", + fontWeight: 700 + } + } +})); + +const StyledHistoryButton = styled(Button)(() => ({ + "&.MuiButton-root": { + minWidth: "168px", marginBottom: "10px", - padding: "10px 20px", - border: "1px solid #004A80", + padding: "11px 20px", + border: "1px solid #B3B3B3", color: "#004A80", - fontFamily: "'Nunito Sans', 'Rubik', sans-serif", + fontFamily: "'Nunito', 'Rubik', sans-serif", fontSize: "16px", fontStyle: "normal", fontWeight: 700, lineHeight: "17px", letterSpacing: "0.32px", - textTransform: "none", "&:hover": { - backgroundColor: "#1A5874", - borderColor: "#DDE6EF", - color: "#DDE6EF", + backgroundColor: "#FFF", + borderColor: "#B3B3B3", + color: "#004A80", }, } })); const StyledSectionDivider = styled(Divider)(() => ({ "&.MuiDivider-root": { + display: "flex", + alignSelf: "flex-start", width: "2px", - height: "114px", + height: "159px", background: "#6CACDA", marginLeft: "44px", - marginTop: "8px", + marginTop: "9px", alignSelft: "flex-end" }, })); @@ -122,7 +160,14 @@ type Props = { const DataSubmissionSummary: FC = ({ dataSubmission }) => { const [historyDialogOpen, setHistoryDialogOpen] = useState(false); + const [reviewCommentsDialogOpen, setReviewCommentsDialogOpen] = useState(false); const [hasEllipsis, setHasEllipsis] = useState(false); + const lastReview = useMemo( + () => SortHistory(dataSubmission?.history).find( + (h: HistoryBase) => h.status === "Rejected" && h.reviewComment?.length > 0 + ), + [dataSubmission] + ); const textRef = useRef(null); useEffect(() => { @@ -146,6 +191,14 @@ const DataSubmissionSummary: FC = ({ dataSubmission }) => { setHistoryDialogOpen(false); }; + const handleOnReviewCommentsDialogOpen = () => { + setReviewCommentsDialogOpen(true); + }; + + const handleOnReviewCommentsDialogClose = () => { + setReviewCommentsDialogOpen(false); + }; + const getHistoryTextColorFromStatus = (status: SubmissionStatus) => { let color: string; switch (status) { @@ -172,17 +225,26 @@ const DataSubmissionSummary: FC = ({ dataSubmission }) => { > STATUS - + {dataSubmission?.status} - - Full History - + + + Review Comments + + + Full History + + @@ -259,6 +321,12 @@ const DataSubmissionSummary: FC = ({ dataSubmission }) => { iconMap={DataSubmissionIconMap} getTextColor={getHistoryTextColorFromStatus} /> + ); }; diff --git a/src/components/Shared/HistoryDialog.tsx b/src/components/Shared/HistoryDialog.tsx index 849934ff..a9040a92 100644 --- a/src/components/Shared/HistoryDialog.tsx +++ b/src/components/Shared/HistoryDialog.tsx @@ -209,7 +209,7 @@ const HistoryDialog = ({ open={open} onClose={onClose} scroll="body" - data-testid="status-bar-history-dialog" + data-testid="history-dialog" {...rest} > @@ -221,7 +221,7 @@ const HistoryDialog = ({ {sortedHistory?.map(({ status, dateTime }, index) => ( @@ -229,10 +229,25 @@ const HistoryDialog = ({ - + {FormatDate(dateTime, "M/D/YYYY", "N/A")} - + {status?.toString()?.toUpperCase()} {index === 0 && iconMap && iconMap[status] && ( @@ -240,7 +255,7 @@ const HistoryDialog = ({ {`${status} )} @@ -252,12 +267,12 @@ const HistoryDialog = ({ onClose()} variant="outlined" size="large" aria-label="Close dialog" - data-testid="status-bar-dialog-close" + data-testid="history-dialog-close" > Close diff --git a/src/components/Shared/ReviewCommentsDialog.test.tsx b/src/components/Shared/ReviewCommentsDialog.test.tsx new file mode 100644 index 00000000..84e76dd5 --- /dev/null +++ b/src/components/Shared/ReviewCommentsDialog.test.tsx @@ -0,0 +1,178 @@ +import "@testing-library/jest-dom"; +import "jest-axe/extend-expect"; + +import { ThemeProvider } from "@mui/material"; +import { CSSProperties } from "react"; +import { BrowserRouter } from "react-router-dom"; +import { render, fireEvent, waitFor } from "@testing-library/react"; +import { act } from "react-dom/test-utils"; +import { axe } from "jest-axe"; +import ReviewCommentsDialog from "./ReviewCommentsDialog"; +import theme from "../../theme"; + +type Props = { + open: boolean; + status?: T; + lastReview: HistoryBase; + title: string; + getColorScheme?: (status: T) => CSSProperties; + onClose?: () => void; +}; + +const BaseComponent = ({ + open, + status, + lastReview, + title, + getColorScheme, + onClose, +}: Props) => ( + + + + + +); + +const mockLastReview: HistoryBase = { + dateTime: "2023-01-01T00:00:00Z", + reviewComment: "This is a mock comment", + status: undefined, + userID: "" +}; + +describe("ReviewCommentsDialog Accessibility Tests", () => { + it("has no base accessibility violations", async () => { + const data = { + open: true, + title: "", + lastReview: { + status: undefined, + reviewComment: "", + dateTime: "", + userID: "", + } + }; + + const { container } = render(); + const results = await axe(container); + + expect(results).toHaveNoViolations(); + }); + + it("has no accessibility violations when there are review comments", async () => { + const data = { + open: true, + title: "Title", + lastReview: mockLastReview + }; + + const { container } = render(); + const results = await axe(container); + + expect(results).toHaveNoViolations(); + }); +}); + +describe("ReviewCommentsDialog Tests", () => { + it("renders the dialog with review comments correctly", () => { + const data = { + open: true, + title: "", + lastReview: mockLastReview, + onClose: () => {} + }; + + const { getByText } = render(); + + expect(getByText(/Review Comments/)).toBeInTheDocument(); + expect(getByText(mockLastReview.reviewComment)).toBeInTheDocument(); + }); + + it("provides the unformatted review date as a title attribute", () => { + const data = { + open: true, + title: "", + lastReview: mockLastReview, + onClose: () => {} + }; + const { getByText } = render(); + + expect(getByText(/Based on submission from/)).toHaveAttribute( + "title", + mockLastReview.dateTime + ); + }); + + it("closes the dialog when the close button is clicked", async () => { + const mockClose = jest.fn(); + const data = { + open: true, + title: "", + lastReview: mockLastReview, + onClose: mockClose + }; + + const { getByTestId } = render(); + + act(() => { + fireEvent.click(getByTestId("review-comments-dialog-close")); + }); + + await waitFor(() => expect(mockClose).toHaveBeenCalled()); + }); + + it("does not render the dialog when open is false", () => { + const data = { + open: false, + title: "", + lastReview: mockLastReview, + onClose: () => {} + }; + + const { queryByTestId, queryByText } = render(); + + expect(queryByTestId("review-comments-dialog")).not.toBeInTheDocument(); + expect(queryByText("Review Comments")).not.toBeInTheDocument(); + }); + + it("renders the title passed through prop", () => { + const customTitle = "Custom Dialog Title"; + const data = { + open: true, + title: customTitle, + lastReview: mockLastReview, + onClose: () => {}, + }; + + const { getByText } = render(); + expect(getByText(customTitle)).toBeInTheDocument(); + }); + + it("has correct status passed through prop", () => { + const mockGetColorScheme = jest.fn().mockImplementation((status) => ({ + color: status === "Approved" ? "#0D6E87" : "#E25C22", + background: status === "Approved" ? "#CDEAF0" : "#FFDBCB", + })); + + const data = { + open: true, + title: "", + status: "Approved", + lastReview: mockLastReview, + onClose: () => {}, + getColorScheme: mockGetColorScheme, + }; + + render(); + + expect(mockGetColorScheme).toHaveBeenCalledWith("Approved"); + }); +}); diff --git a/src/components/Shared/ReviewCommentsDialog.tsx b/src/components/Shared/ReviewCommentsDialog.tsx new file mode 100644 index 00000000..7ef84c32 --- /dev/null +++ b/src/components/Shared/ReviewCommentsDialog.tsx @@ -0,0 +1,169 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + styled, +} from "@mui/material"; +import { CSSProperties } from "react"; +import { FormatDate } from "../../utils"; +import { ReactComponent as CloseIconSvg } from "../../assets/icons/close_icon.svg"; + +const StyledDialog = styled(Dialog, { + shouldForwardProp: (prop) => prop !== "status" && prop !== "getColorScheme" +})<{ + status: unknown; + getColorScheme:(status: unknown) => CSSProperties; +}>(({ status, getColorScheme }) => ({ + "& .MuiDialog-paper": { + borderRadius: "8px", + border: "2px solid", + borderColor: + getColorScheme && status ? getColorScheme(status).color : "#E25C22", + background: "linear-gradient(0deg, #F2F6FA 0%, #F2F6FA 100%), #2E4D7B", + boxShadow: "0px 4px 45px 0px rgba(0, 0, 0, 0.40)", + padding: "22px 28px 24px", + width: "730px", + }, +})); + +const StyledCloseDialogButton = styled(IconButton)(() => ({ + position: "absolute", + right: "21px", + top: "11px", + padding: "10px", + "& svg": { + color: "#44627C", + }, +})); + +const StyledDialogTitle = styled(DialogTitle)({ + paddingBottom: "0", +}); + +const StyledPreTitle = styled("p")({ + color: "#929292", + fontSize: "13px", + fontFamily: "Nunito Sans", + lineHeight: "27px", + letterSpacing: "0.5px", + textTransform: "uppercase", + margin: "0", +}); + +const StyledTitle = styled("p", { + shouldForwardProp: (prop) => prop !== "status" && prop !== "getColorScheme" +})<{ + status: unknown; + getColorScheme:(status: unknown) => CSSProperties; +}>(({ status, getColorScheme }) => ({ + color: getColorScheme && status ? getColorScheme(status).color : "#E25C22", + fontSize: "35px", + fontFamily: "Nunito Sans", + fontWeight: "900", + lineHeight: "30px", + margin: "0", +})); + +const StyledDialogContent = styled(DialogContent)({ + marginBottom: "11px", + maxHeight: "230px", + overflowY: "auto", + overflowX: "hidden", + whiteSpace: "pre-line", + overflowWrap: "break-word", +}); + +const StyledSubTitle = styled("p")({ + color: "#453D3D", + fontSize: "14px", + fontFamily: "Public Sans", + fontWeight: "700", + lineHeight: "20px", + letterSpacing: "0.14px", + textTransform: "uppercase", + marginTop: "60px", + marginBottom: "19px", +}); + +const StyledCloseButton = styled(Button)({ + minWidth: "137px", + padding: "10px", + fontFamily: "'Nunito', 'Rubik', sans-serif", + fontSize: "16px", + fontStyle: "normal", + lineHeight: "24px", + letterSpacing: "0.32px", + textTransform: "none", + alignSelf: "center", + margin: "auto", +}); + +type Props = { + open: boolean; + status?: T; + lastReview: HistoryBase; + title: string; + getColorScheme?: (status: T) => CSSProperties; + onClose?: () => void; +}; + +const ReviewCommentsDialog = ({ + open, + status, + lastReview, + title, + getColorScheme, + onClose, +}: Props) => ( + onClose?.()} + maxWidth={false} + status={status} + getColorScheme={getColorScheme} + data-testid="review-comments-dialog" + > + + + + + {title} + + Review Comments + + + {`Based on submission from ${FormatDate( + lastReview?.dateTime, + "M/D/YYYY", + "N/A" + )}:`} + + + {lastReview?.reviewComment} + + onClose?.()} + variant="contained" + color="info" + aria-label="Close dialog" + data-testid="review-comments-dialog-close" + > + Close + + + +); + +export default ReviewCommentsDialog; diff --git a/src/components/StatusBar/StatusBar.test.tsx b/src/components/StatusBar/StatusBar.test.tsx index 1be56c99..bfd0366d 100644 --- a/src/components/StatusBar/StatusBar.test.tsx +++ b/src/components/StatusBar/StatusBar.test.tsx @@ -14,13 +14,8 @@ import { import StatusBar from './StatusBar'; import StatusApproved from '../../assets/history/submissionRequest/StatusApproved.svg'; import StatusRejected from '../../assets/history/submissionRequest/StatusRejected.svg'; -import New from '../../assets/history/submissionRequest/SubmissionRequestNew.svg'; -import Approved from '../../assets/history/submissionRequest/Approved.svg'; -import Rejected from '../../assets/history/submissionRequest/Rejected.svg'; -import Submitted from '../../assets/history/submissionRequest/SubmissionRequestSubmitted.svg'; -import UnderReview from '../../assets/history/submissionRequest/UnderReview.svg'; -import InProgress from '../../assets/history/submissionRequest/InProgress.svg'; import { FormatDate } from "../../utils"; +import { HistoryIconMap } from '../../assets/history/submissionRequest'; type Props = { data: object; @@ -200,7 +195,7 @@ describe("StatusBar > Comments Modal Tests", () => { fireEvent.click(getByText("Review Comments")); }); - expect(getByTestId("status-bar-review-dialog")).toBeVisible(); + expect(getByTestId("review-comments-dialog")).toBeVisible(); }); it("renders the most recent comment by date", async () => { @@ -218,7 +213,7 @@ describe("StatusBar > Comments Modal Tests", () => { fireEvent.click(getByText("Review Comments")); }); - expect(getByTestId("status-bar-review-dialog")).toBeVisible(); + expect(getByTestId("review-comments-dialog")).toBeVisible(); expect(getByText(/BASED ON SUBMISSION FROM 11\/30\/2019:/i)).toBeVisible(); expect(getByText(data.history[2].reviewComment)).toBeVisible(); }); @@ -239,7 +234,7 @@ describe("StatusBar > Comments Modal Tests", () => { fireEvent.click(getByText("Review Comments")); }); - expect(getByTestId("status-bar-review-dialog")).toBeVisible(); + expect(getByTestId("review-comments-dialog")).toBeVisible(); expect(getByText(/BASED ON SUBMISSION FROM 12\/30\/2023:/i)).toBeVisible(); expect(getByText(data.history[1].reviewComment)).toBeVisible(); }); @@ -260,7 +255,7 @@ describe("StatusBar > Comments Modal Tests", () => { fireEvent.click(getByText("Review Comments")); }); - expect(getByTestId("status-bar-review-dialog")).toBeVisible(); + expect(getByTestId("review-comments-dialog")).toBeVisible(); expect(getByText(/BASED ON SUBMISSION FROM 11\/26\/2023:/i)).toBeVisible(); expect(getByText(data.history[1].reviewComment)).toBeVisible(); }); @@ -290,13 +285,13 @@ describe("StatusBar > Comments Modal Tests", () => { fireEvent.click(getByText("Review Comments")); }); - expect(queryByTestId("status-bar-review-dialog")).toBeVisible(); + expect(queryByTestId("review-comments-dialog")).toBeVisible(); act(() => { - fireEvent.click(queryByTestId("status-bar-dialog-close")); + fireEvent.click(queryByTestId("review-comments-dialog-close")); }); - await waitFor(() => expect(queryByTestId("status-bar-review-dialog")).toBeNull()); + await waitFor(() => expect(queryByTestId("review-comments-dialog")).toBeNull()); }); }); @@ -367,8 +362,7 @@ describe("StatusBar > History Modal Tests", () => { expect(() => getByTestId("status-bar-history-item-1-icon")).toThrow(); }); - const statusesWithIcons = [["New", New], ["Submitted", Submitted], ["Approved", Approved], ["Rejected", Rejected], ["In Review", UnderReview], ["In Progress", InProgress]]; - it.each(statusesWithIcons)("renders the correct icon for the status %s", (status, svg) => { + it.each(Object.entries(HistoryIconMap))("renders the correct icon for the status %s", (status, svg) => { const data = { history: [{ dateTime: "2023-11-24T01:25:45Z", status }], }; diff --git a/src/components/StatusBar/components/HistorySection.tsx b/src/components/StatusBar/components/HistorySection.tsx index c12afc7c..63a670b7 100644 --- a/src/components/StatusBar/components/HistorySection.tsx +++ b/src/components/StatusBar/components/HistorySection.tsx @@ -61,9 +61,9 @@ const StyledButton = styled(Button)({ letterSpacing: "0.32px", textTransform: "none", "&:hover": { - backgroundColor: "#1A5874", - borderColor: "#DDE6EF", - color: "#DDE6EF", + backgroundColor: "#FFF", + borderColor: "#004A80", + color: "#004A80", }, } }); diff --git a/src/components/StatusBar/components/StatusSection.tsx b/src/components/StatusBar/components/StatusSection.tsx index 1f130e7f..62f4b8ec 100644 --- a/src/components/StatusBar/components/StatusSection.tsx +++ b/src/components/StatusBar/components/StatusSection.tsx @@ -1,16 +1,10 @@ -import { CSSProperties, FC, useState } from "react"; -import { - Avatar, - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, -} from "@mui/material"; +import { CSSProperties, FC, useMemo, useState } from "react"; +import { Avatar, Button } from "@mui/material"; import { styled } from "@mui/material/styles"; import { useFormContext } from "../../Contexts/FormContext"; import { StatusIconMap } from "../../../assets/history/submissionRequest"; -import { FormatDate, SortHistory } from "../../../utils"; +import { SortHistory } from "../../../utils"; +import ReviewCommentsDialog from "../../Shared/ReviewCommentsDialog"; /** * Returns the styling for a component based on the Questionnaire Status @@ -46,7 +40,10 @@ const StyledAvatar = styled(Avatar)({ height: "39px", }); -const StyledStatus = styled("span")<{ status: ApplicationStatus, leftGap: boolean }>(({ status, leftGap }) => ({ +const StyledStatus = styled("span")<{ + status: ApplicationStatus; + leftGap: boolean; +}>(({ status, leftGap }) => ({ fontWeight: "600", fontSize: "16px", fontFamily: "Public Sans", @@ -54,87 +51,21 @@ const StyledStatus = styled("span")<{ status: ApplicationStatus, leftGap: boolea marginLeft: !leftGap ? "6px !important" : null, marginRight: "10px !important", letterSpacing: "0.32px", - color: getColorScheme(status).color -})); - -const StyledButton = styled(Button)<{ status: ApplicationStatus }>(({ status }) => ({ - ...getColorScheme(status), - fontWeight: "700", - borderRadius: "8px", - border: "0 !important", - textTransform: "none", - width: "165px", - lineHeight: "19px", - padding: "10px 20px 10px 20px", -})); - -const StyledDialog = styled(Dialog)<{ status: ApplicationStatus }>(({ status }) => ({ - "& .MuiDialog-paper": { - borderRadius: "8px", - border: "2px solid", - borderColor: getColorScheme(status).color, - background: "linear-gradient(0deg, #F2F6FA 0%, #F2F6FA 100%), #2E4D7B", - boxShadow: "0px 4px 45px 0px rgba(0, 0, 0, 0.40)", - padding: "28px 24px", - width: "730px", - }, -})); - -const StyledDialogTitle = styled(DialogTitle)({ - paddingBottom: "0", -}); - -const StyledPreTitle = styled("p")({ - color: "#929292", - fontSize: "13px", - fontFamily: "Nunito Sans", - lineHeight: "27px", - letterSpacing: "0.5px", - textTransform: "uppercase", - margin: "0", -}); - -const StyledTitle = styled("p")<{ status: ApplicationStatus }>(({ status }) => ({ color: getColorScheme(status).color, - fontSize: "35px", - fontFamily: "Nunito Sans", - fontWeight: "900", - lineHeight: "30px", - margin: "0", })); -const StyledDialogContent = styled(DialogContent)({ - marginBottom: "22px", - maxHeight: "230px", - overflowY: "auto", - overflowX: "hidden", - whiteSpace: "pre-line", - overflowWrap: "break-word", -}); - -const StyledSubTitle = styled("p")({ - color: "#453D3D", - fontSize: "14px", - fontFamily: "Public Sans", - fontWeight: "700", - lineHeight: "20px", - letterSpacing: "0.14px", - textTransform: "uppercase", - marginTop: "60px", -}); - -const StyledCloseButton = styled(Button)({ - fontWeight: "700", - borderRadius: "8px", - textTransform: "none", - color: "#000", - borderColor: "#000", - margin: "0 auto", - minWidth: "128px", - "&:hover": { - borderColor: "#000", - }, -}); +const StyledButton = styled(Button)<{ status: ApplicationStatus }>( + ({ status }) => ({ + ...getColorScheme(status), + fontWeight: "700", + borderRadius: "8px", + border: "0 !important", + textTransform: "none", + width: "165px", + lineHeight: "19px", + padding: "10px 20px 10px 20px", + }) +); /** * Status Bar Application Status Section @@ -147,8 +78,11 @@ const StatusSection: FC = () => { } = useFormContext(); const [open, setOpen] = useState(false); - const [lastReview] = useState( - SortHistory(history).find((h: HistoryEvent) => h.reviewComment) + const lastReview = useMemo( + () => SortHistory(history).find( + (h: HistoryEvent) => h.reviewComment?.length > 0 + ), + [history] ); return ( @@ -181,40 +115,14 @@ const StatusSection: FC = () => { > Review Comments - setOpen(false)} - maxWidth={false} status={status} - data-testid="status-bar-review-dialog" - > - - CRDC Submission Request - Review Comments - - {`Based on submission from ${FormatDate( - lastReview?.dateTime, - "M/D/YYYY", - "N/A" - )}:`} - - - - {lastReview?.reviewComment} - - - setOpen(false)} - variant="outlined" - size="large" - aria-label="Close dialog" - data-testid="status-bar-dialog-close" - > - Close - - - + lastReview={lastReview} + title="CRDC Submission Request" + getColorScheme={getColorScheme} + /> )}