Skip to content

Commit

Permalink
CMDCT-4065: Read Only View (#72)
Browse files Browse the repository at this point in the history
Co-authored-by: benmartin-coforma <[email protected]>
  • Loading branch information
angelaco11 and benmartin-coforma authored Dec 12, 2024
1 parent 046c7fb commit f5510fe
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 24 deletions.
6 changes: 6 additions & 0 deletions services/ui-src/src/components/fields/DateField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
/>
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -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();

Expand Down Expand Up @@ -46,7 +51,7 @@ const dashboardComponent = (
</RouterWrappedComponent>
);

describe("<DashboardPage />", () => {
describe("DashboardPage with state user", () => {
beforeEach(() => jest.clearAllMocks());

it("should render an empty state when there are no reports", async () => {
Expand Down Expand Up @@ -95,3 +100,22 @@ describe("<DashboardPage />", () => {
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();
});
});
14 changes: 8 additions & 6 deletions services/ui-src/src/components/pages/Dashboard/DashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Report[]>([]);
Expand Down Expand Up @@ -87,11 +87,13 @@ export const DashboardPage = () => {
<Flex sx={sx.bodyBox} gap="2rem" flexDirection="column">
{!isLoading && <DashboardTable reports={reports} />}
{!reports?.length && <Text variant="tableEmpty">{body.empty}</Text>}
<Flex justifyContent="center">
<Button onClick={() => openAddEditReportModal()} type="submit">
{body.link.callToActionText}
</Button>
</Flex>
{userIsEndUser && (
<Flex justifyContent="center">
<Button onClick={() => openAddEditReportModal()} type="submit">
{body.link.callToActionText}
</Button>
</Flex>
)}
</Flex>
<AddEditReportModal
activeState={state!}
Expand Down
1 change: 1 addition & 0 deletions services/ui-src/src/components/report/Elements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface PageElementProps {
element: PageElement;
index?: number;
formkey: string;
disabled?: boolean;
}

export const headerElement = (props: PageElementProps) => {
Expand Down
45 changes: 43 additions & 2 deletions services/ui-src/src/components/report/Page.test.tsx
Original file line number Diff line number Diff line change
@@ -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", () => ({
Expand Down Expand Up @@ -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(<Page elements={[element]} />);
expect(container).not.toBeEmptyDOMElement();
Expand Down Expand Up @@ -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(<Page elements={textFieldElement} />);
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(<Page elements={dateFieldElement} />);
const dateField = screen.getByRole("textbox");
expect(dateField).toBeDisabled();
});
});
3 changes: 3 additions & 0 deletions services/ui-src/src/components/report/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -55,6 +57,7 @@ export const Page = ({ elements }: Props) => {
formkey={buildFormKey(index)}
key={index}
element={element}
disabled={!userIsEndUser}
/>
);
});
Expand Down
37 changes: 30 additions & 7 deletions services/ui-src/src/components/report/StatusTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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<typeof useStore>;

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,
});
Expand Down Expand Up @@ -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(
<MemoryRouter>
<StatusTableElement />
</MemoryRouter>
);

const submitButton = screen.queryByText("Submit QMS Report");
expect(submitButton).not.toBeInTheDocument();
});
});
16 changes: 9 additions & 7 deletions services/ui-src/src/components/report/StatusTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -95,12 +95,14 @@ export const StatusTableElement = () => {
>
Review PDF
</Button>
<Button
// onClick={() => SetPageIndex(parentPage.index - 1)}
alignSelf="flex-end"
>
Submit QMS Report
</Button>
{user?.userIsEndUser && (
<Button
// onClick={() => SetPageIndex(parentPage.index - 1)}
alignSelf="flex-end"
>
Submit QMS Report
</Button>
)}
</Stack>
</>
);
Expand Down
6 changes: 6 additions & 0 deletions services/ui-src/src/utils/testing/setupJest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => (
Expand Down

0 comments on commit f5510fe

Please sign in to comment.