diff --git a/services/ui-src/src/components/fields/DateField.tsx b/services/ui-src/src/components/fields/DateField.tsx index f11ca954..89b8fb7c 100644 --- a/services/ui-src/src/components/fields/DateField.tsx +++ b/services/ui-src/src/components/fields/DateField.tsx @@ -48,6 +48,12 @@ export const DateField = (props: PageElementProps) => { value={displayValue} hint={parsedHint} errorMessage={errorMessage} + /* + * Typescript hack. CmsdsDateField won't recognize + * passthrough props until @cmsgov/design-system^9.0.0 + * TODO: Unhack, to just `disabled={props.disabled}` + */ + {...{ disabled: props.disabled }} /> ); diff --git a/services/ui-src/src/components/pages/Dashboard/DashboardPage.test.tsx b/services/ui-src/src/components/pages/Dashboard/DashboardPage.test.tsx index 13920d72..32c9ccba 100644 --- a/services/ui-src/src/components/pages/Dashboard/DashboardPage.test.tsx +++ b/services/ui-src/src/components/pages/Dashboard/DashboardPage.test.tsx @@ -1,9 +1,14 @@ import { render, screen, waitFor } from "@testing-library/react"; import { DashboardPage } from "components"; -import { RouterWrappedComponent, mockUseStore } from "utils/testing/setupJest"; +import { + RouterWrappedComponent, + mockUseReadOnlyUserStore, + mockUseStore, +} from "utils/testing/setupJest"; import { useStore } from "utils"; import { getReportsForState } from "utils/api/requestMethods/report"; import { Report } from "types"; +import dashboardVerbiage from "verbiage/pages/dashboard"; window.HTMLElement.prototype.scrollIntoView = jest.fn(); @@ -46,7 +51,7 @@ const dashboardComponent = ( ); -describe("", () => { +describe("DashboardPage with state user", () => { beforeEach(() => jest.clearAllMocks()); it("should render an empty state when there are no reports", async () => { @@ -95,3 +100,22 @@ describe("", () => { expect(cellContent("Edited by")).toBe("Mock User"); }); }); + +describe("DashboardPage with Read only user", () => { + beforeEach(() => { + mockedUseStore.mockReturnValue(mockUseReadOnlyUserStore); + }); + it("should not render the Start Report button when user is read only", async () => { + (getReportsForState as jest.Mock).mockResolvedValueOnce([]); + + render(dashboardComponent); + await waitFor(() => { + expect(getReportsForState).toHaveBeenCalled(); + }); + + const startReportButton = screen.queryByText( + dashboardVerbiage.body.link.callToActionText + ); + expect(startReportButton).not.toBeInTheDocument(); + }); +}); diff --git a/services/ui-src/src/components/pages/Dashboard/DashboardPage.tsx b/services/ui-src/src/components/pages/Dashboard/DashboardPage.tsx index f9497167..d20ae899 100644 --- a/services/ui-src/src/components/pages/Dashboard/DashboardPage.tsx +++ b/services/ui-src/src/components/pages/Dashboard/DashboardPage.tsx @@ -25,7 +25,7 @@ import arrowLeftIcon from "assets/icons/arrows/icon_arrow_left_blue.png"; import { getReportsForState } from "utils/api/requestMethods/report"; export const DashboardPage = () => { - const { userIsAdmin } = useStore().user ?? {}; + const { userIsAdmin, userIsEndUser } = useStore().user ?? {}; const { reportType, state } = useParams(); const [isLoading, setIsLoading] = useState(true); const [reports, setReports] = useState([]); @@ -87,11 +87,13 @@ export const DashboardPage = () => { {!isLoading && } {!reports?.length && {body.empty}} - - - + {userIsEndUser && ( + + + + )} { diff --git a/services/ui-src/src/components/report/Page.test.tsx b/services/ui-src/src/components/report/Page.test.tsx index 76454dea..640d96ca 100644 --- a/services/ui-src/src/components/report/Page.test.tsx +++ b/services/ui-src/src/components/report/Page.test.tsx @@ -1,9 +1,12 @@ +import { + mockUseReadOnlyUserStore, + mockUseStore, +} from "utils/testing/setupJest"; import { useNavigate, useParams } from "react-router-dom"; import userEvent from "@testing-library/user-event"; import { render, screen } from "@testing-library/react"; import { ElementType, PageElement } from "types/report"; import { useStore } from "utils"; -import { mockUseStore } from "utils/testing/setupJest"; import { Page } from "./Page"; jest.mock("react-router-dom", () => ({ @@ -92,7 +95,27 @@ const elements: PageElement[] = [ }, ]; -describe("Page Component", () => { +const textFieldElement: PageElement[] = [ + { + type: ElementType.Textbox, + label: "labeled", + }, + { + type: ElementType.Radio, + label: "radio button", + value: [{ label: "radio choice 1", value: "1", checkedChildren: [] }], + }, +]; + +const dateFieldElement: PageElement[] = [ + { + type: ElementType.Date, + label: "date label", + helperText: "can you read this?", + }, +]; + +describe("Page Component with state user", () => { test.each(elements)("Renders all element types: %p", (element) => { const { container } = render(); expect(container).not.toBeEmptyDOMElement(); @@ -132,3 +155,21 @@ describe("Page Component", () => { expect(container).not.toBeEmptyDOMElement(); }); }); + +describe("Page Component with read only user", () => { + beforeEach(() => { + mockedUseStore.mockReturnValue(mockUseReadOnlyUserStore); + }); + test("text field and radio button should be disabled", () => { + render(); + const textField = screen.getByRole("textbox"); + const radioButton = screen.getByLabelText("radio choice 1"); + expect(textField).toBeDisabled(); + expect(radioButton).toBeDisabled(); + }); + test("date field should be disabled", () => { + render(); + const dateField = screen.getByRole("textbox"); + expect(dateField).toBeDisabled(); + }); +}); diff --git a/services/ui-src/src/components/report/Page.tsx b/services/ui-src/src/components/report/Page.tsx index 11a8cc26..e0691778 100644 --- a/services/ui-src/src/components/report/Page.tsx +++ b/services/ui-src/src/components/report/Page.tsx @@ -11,12 +11,14 @@ import { MeasureTableElement } from "./MeasureTable"; import { QualityMeasureTableElement } from "./QualityMeasureTable"; import { StatusTableElement } from "./StatusTable"; import { TextField, DateField, RadioField } from "components"; +import { useStore } from "utils"; interface Props { elements: PageElement[]; } export const Page = ({ elements }: Props) => { + const { userIsEndUser } = useStore().user || {}; const renderElement = (element: PageElement) => { const elementType = element.type; switch (elementType) { @@ -55,6 +57,7 @@ export const Page = ({ elements }: Props) => { formkey={buildFormKey(index)} key={index} element={element} + disabled={!userIsEndUser} /> ); }); diff --git a/services/ui-src/src/components/report/StatusTable.test.tsx b/services/ui-src/src/components/report/StatusTable.test.tsx index 72b49833..2819306a 100644 --- a/services/ui-src/src/components/report/StatusTable.test.tsx +++ b/services/ui-src/src/components/report/StatusTable.test.tsx @@ -3,6 +3,7 @@ import userEvent from "@testing-library/user-event"; import { StatusTableElement } from "./StatusTable"; import { MemoryRouter } from "react-router-dom"; import { useStore } from "utils"; +import { mockUseReadOnlyUserStore } from "utils/testing/setupJest"; jest.mock("utils", () => ({ useStore: jest.fn(), @@ -25,16 +26,18 @@ const report = { ], }; -describe("StatusTableElement", () => { +const mockPageMap = new Map(); +mockPageMap.set("root", 0); +mockPageMap.set("1", 1); +mockPageMap.set("2", 2); + +const mockedUseStore = useStore as jest.MockedFunction; + +describe("StatusTable with state user", () => { beforeEach(() => { jest.clearAllMocks(); - const mockPageMap = new Map(); - mockPageMap.set("root", 0); - mockPageMap.set("1", 1); - mockPageMap.set("2", 2); - - (useStore as unknown as jest.Mock).mockReturnValue({ + mockedUseStore.mockReturnValue({ pageMap: mockPageMap, report: report, }); @@ -97,3 +100,23 @@ describe("StatusTableElement", () => { expect(container.firstChild).toBeNull(); }); }); + +describe("StatusPage with Read only user", () => { + beforeEach(() => { + mockedUseStore.mockReturnValue({ + ...mockUseReadOnlyUserStore, + pageMap: mockPageMap, + report: report, + }); + }); + it("should not render the Submit QMS Report button when user is read only", async () => { + render( + + + + ); + + const submitButton = screen.queryByText("Submit QMS Report"); + expect(submitButton).not.toBeInTheDocument(); + }); +}); diff --git a/services/ui-src/src/components/report/StatusTable.tsx b/services/ui-src/src/components/report/StatusTable.tsx index 73a514b0..b051d763 100644 --- a/services/ui-src/src/components/report/StatusTable.tsx +++ b/services/ui-src/src/components/report/StatusTable.tsx @@ -19,7 +19,7 @@ import { TableStatusIcon } from "components/tables/TableStatusIcon"; import { reportBasePath } from "utils/other/routing"; export const StatusTableElement = () => { - const { pageMap, report } = useStore(); + const { pageMap, report, user } = useStore(); const { reportType, state, reportId } = useParams(); if (!pageMap) { @@ -95,12 +95,14 @@ export const StatusTableElement = () => { > Review PDF - + {user?.userIsEndUser && ( + + )} ); diff --git a/services/ui-src/src/utils/testing/setupJest.tsx b/services/ui-src/src/utils/testing/setupJest.tsx index d846b825..92e9bdce 100644 --- a/services/ui-src/src/utils/testing/setupJest.tsx +++ b/services/ui-src/src/utils/testing/setupJest.tsx @@ -250,6 +250,12 @@ export const mockUseAdminStore: HcbsUserState & AdminBannerState = { ...mockBannerStore, }; +export const mockUseReadOnlyUserStore: HcbsUserState & AdminBannerState = { + ...mockHelpDeskUserStore, + ...mockBannerStore, + ...mockReportStore, +}; + // ROUTER export const RouterWrappedComponent: React.FC = ({ children }) => (