Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ui): fix New Submission button display #794

Merged
merged 11 commits into from
Sep 27, 2024
6 changes: 0 additions & 6 deletions react-app/src/api/index.test.ts

This file was deleted.

65 changes: 42 additions & 23 deletions react-app/src/api/useGetCounties.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,18 @@
import { useQuery } from "@tanstack/react-query";
import { useGetUser } from "./useGetUser";
import { getUserStateCodes } from "@/utils";
import { FULL_CENSUS_STATES } from "shared-types";
import { useMemo } from "react";

const fetchPopulationData = async (stateString: string) => {
try {
if (stateString) {
const response = await fetch(
`https://api.census.gov/data/2019/pep/population?get=NAME&for=county:*&in=state:${stateString}`,
);
if (!response.ok) {
throw new Error("Failed to fetch county data");
}

const data = await response.json();
return data.slice(1).map((item) => item[0]);
}
return [];
} catch (error) {
console.error("Error fetching county data:", error);
throw error;
}
};

export const usePopulationData = (stateString: string) => {
const usePopulationData = (stateString: string) => {
return useQuery(
["populationData", stateString],
() => fetchPopulationData(stateString),
() =>
fetch(
`https://api.census.gov/data/2019/pep/population?get=NAME&for=county:*&in=state:${stateString}`,
)
.then((response) => response.json())
.then((population) => population.slice(1).map((item) => item[0])),
{
refetchOnWindowFocus: false,
refetchOnReconnect: false,
Expand All @@ -32,4 +21,34 @@ export const usePopulationData = (stateString: string) => {
);
};

export default usePopulationData;
export const useGetCounties = (): { label: string; value: string }[] => {
const { data: userData } = useGetUser();

const stateCodes = useMemo(
() => getUserStateCodes(userData?.user),
[userData],
);

const stateNumericCodesString = useMemo(
() =>
stateCodes
.map(
(code) =>
FULL_CENSUS_STATES.find((state) => state.value === code)?.code,
)
.filter((code): code is string => code !== undefined && code !== "00")
.join(","),
[stateCodes],
);

const { data: populationData = [] } = usePopulationData(
stateNumericCodesString,
);

return (
populationData.map((county) => {
const [label] = county.split(",");
return { label, value: county };
}) ?? []
);
};
77 changes: 49 additions & 28 deletions react-app/src/components/ActionForm/ActionForm.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { ReactElement } from "react";
import { MemoryRouter } from "react-router-dom";
import { render, waitFor } from "@testing-library/react";
import { waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, test, expect, vi } from "vitest";
import { ActionForm } from "./index";
Expand All @@ -9,6 +7,8 @@ import { attachmentArraySchemaOptional } from "shared-types";
import * as userPrompt from "@/components/ConfirmationDialog/userPrompt";
import * as banner from "@/components/Banner/banner";
import * as documentPoller from "@/utils/Poller/documentPoller";
import { renderForm } from "@/utils/test-helpers/renderForm";
import { isCmsReadonlyUser } from "shared-utils";

vi.mock("aws-amplify", async (importOriginal) => {
const actual = await importOriginal();
Expand All @@ -28,17 +28,12 @@ vi.mock("../../utils/Poller/DataPoller", () => {
};
});

const renderWithMemoryRouter = (ActionFormArg: ReactElement) =>
render(ActionFormArg, {
wrapper: ({ children }) => <MemoryRouter>{children}</MemoryRouter>,
});

const PROGRESS_REMINDER =
/If you leave this page, you will lose your progress on this form./;

describe("ActionForm", () => {
test("renders `breadcrumbText`", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand All @@ -56,7 +51,7 @@ describe("ActionForm", () => {
});

test("renders `title`", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand All @@ -74,7 +69,7 @@ describe("ActionForm", () => {
});

test("renders `attachments.faqLink`", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand All @@ -101,8 +96,34 @@ describe("ActionForm", () => {
);
});

test("doesn't render form if user access is denied", () => {
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
attachments: z.object({
other: z.object({
label: z.string().default("Other"),
files: attachmentArraySchemaOptional(),
}),
}),
})}
fields={() => null}
documentPollerArgs={{
property: () => "id",
documentChecker: () => true,
}}
attachments={{ faqLink: "hello-world-link" }}
conditionsDeterminingUserAccess={[isCmsReadonlyUser]}
breadcrumbText="Example Breadcrumb"
/>,
);

expect(queryByText("Action Form Title")).not.toBeInTheDocument();
});

test("renders `defaultValues` in appropriate input", () => {
const { queryByDisplayValue } = renderWithMemoryRouter(
const { queryByDisplayValue } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand All @@ -123,7 +144,7 @@ describe("ActionForm", () => {
});

test("renders `attachments.specialInstructions`", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand Down Expand Up @@ -153,7 +174,7 @@ describe("ActionForm", () => {
});

test("renders custom `promptOnLeavingForm` when clicking Cancel", async () => {
const { container } = renderWithMemoryRouter(
const { container } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand Down Expand Up @@ -189,7 +210,7 @@ describe("ActionForm", () => {
});

test("renders custom `promptPreSubmission` when clicking Submit", async () => {
const { container } = renderWithMemoryRouter(
const { container } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand Down Expand Up @@ -230,7 +251,7 @@ describe("ActionForm", () => {
test("calls `documentPoller` with `documentPollerArgs`", async () => {
const documentCheckerFunc = vi.fn();

const { container } = renderWithMemoryRouter(
const { container } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand Down Expand Up @@ -264,7 +285,7 @@ describe("ActionForm", () => {
test("calls `banner` with `bannerPostSubmission`", async () => {
const documentCheckerFunc = vi.fn();

const { container } = renderWithMemoryRouter(
const { container } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand Down Expand Up @@ -302,7 +323,7 @@ describe("ActionForm", () => {
});

test("renders all attachment properties within `attachments`", async () => {
const { queryAllByText } = renderWithMemoryRouter(
const { queryAllByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand Down Expand Up @@ -339,7 +360,7 @@ describe("ActionForm", () => {
});

test("renders Additional Information if `additionalInformation` is defined in schema", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand All @@ -359,7 +380,7 @@ describe("ActionForm", () => {
});

test("doesn't render Additional Information if `additionalInformation` is undefined in schema", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand All @@ -377,7 +398,7 @@ describe("ActionForm", () => {
});

test("renders Attachments if `attachments` is defined in schema", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand All @@ -403,7 +424,7 @@ describe("ActionForm", () => {
});

test("doesn't render Attachments if `attachments` is undefined in schema", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand All @@ -421,7 +442,7 @@ describe("ActionForm", () => {
});

test("renders ProgressReminder if schema has `attachments` property", () => {
const { queryAllByText } = renderWithMemoryRouter(
const { queryAllByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({
Expand All @@ -446,7 +467,7 @@ describe("ActionForm", () => {
});

test("renders ProgressReminder if `fields` property is defined", () => {
const { queryAllByText } = renderWithMemoryRouter(
const { queryAllByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand All @@ -469,7 +490,7 @@ describe("ActionForm", () => {
});

test("doesn't render ProgressReminder `fields` is undefined and `attachments` isn't defined in schema", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand All @@ -487,7 +508,7 @@ describe("ActionForm", () => {
});

test("renders default wrapper if `fieldsLayout` is undefined", () => {
const { queryAllByText } = renderWithMemoryRouter(
const { queryAllByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand All @@ -514,7 +535,7 @@ describe("ActionForm", () => {
});

test("renders `fieldsLayout`", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand All @@ -539,7 +560,7 @@ describe("ActionForm", () => {
});

test("renders `fieldsLayout` with correct `title`", () => {
const { queryByText } = renderWithMemoryRouter(
const { queryByText } = renderForm(
<ActionForm
title="Action Form Title"
schema={z.object({})}
Expand Down
24 changes: 22 additions & 2 deletions react-app/src/components/ActionForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,27 @@ import {
} from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import {
Navigate,
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
import { SlotAdditionalInfo } from "@/features";
import { getFormOrigin } from "@/utils";
import {
CheckDocumentFunction,
documentPoller,
} from "@/utils/Poller/documentPoller";
import { API } from "aws-amplify";
import { Authority } from "shared-types";
import { Authority, CognitoUserAttributes } from "shared-types";
import { ActionFormAttachments } from "./ActionFormAttachments";
import {
getAttachments,
getAdditionalInformation,
} from "./actionForm.utilities";
import { isStateUser } from "shared-utils";
import { useGetUser } from "@/api";

type EnforceSchemaProps<Shape extends z.ZodRawShape> = z.ZodObject<
Shape & {
Expand Down Expand Up @@ -81,6 +88,9 @@ type ActionFormProps<Schema extends SchemaWithEnforcableProps> = {
| ((values: z.TypeOf<Schema>) => string);
documentChecker: CheckDocumentFunction;
};
conditionsDeterminingUserAccess?: ((
user: CognitoUserAttributes | null,
) => boolean)[];
breadcrumbText: string;
};

Expand All @@ -105,11 +115,13 @@ export const ActionForm = <Schema extends SchemaWithEnforcableProps>({
promptPreSubmission,
documentPollerArgs,
attachments,
conditionsDeterminingUserAccess = [isStateUser],
breadcrumbText,
}: ActionFormProps<Schema>) => {
const { id, authority } = useParams<{ id: string; authority: Authority }>();
const { pathname } = useLocation();
const navigate = useNavigate();
const { data: userObj } = useGetUser();

const breadcrumbs = optionCrumbsFromPath(pathname, authority);

Expand Down Expand Up @@ -166,6 +178,14 @@ export const ActionForm = <Schema extends SchemaWithEnforcableProps>({
[attachmentsFromSchema, Fields, form],
);

const doesUserHaveAccessToForm = conditionsDeterminingUserAccess.some(
(condition) => condition(userObj.user),
);

if (doesUserHaveAccessToForm === false) {
return <Navigate to="/" replace />;
}

return (
<SimplePageContainer>
<BreadCrumbs
Expand Down
1 change: 0 additions & 1 deletion react-app/src/components/Context/index.ts

This file was deleted.

Loading
Loading