Skip to content

Commit

Permalink
feat(initial submission): Waiver & SPA options, Landing pages for ext…
Browse files Browse the repository at this point in the history
…ernal app submissions (#145)

* Set up routing for spa submission secondary options

* Wire up landing pages and routing

* Populate landing page content and styles

* fix button text

* touchups

* Set up link to anchor to proper FAQ section

* Grammar fix

* Add waiver options

* Wire up routing for waiver options

* Fix b-cap waiver options

* Prettier code

* Text updates re:testing
  • Loading branch information
Kevin Haube authored Oct 4, 2023
1 parent 001bfff commit 9d00dbc
Show file tree
Hide file tree
Showing 15 changed files with 600 additions and 180 deletions.
Binary file added src/services/ui/public/images/logos/macpro.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/services/ui/public/images/logos/mmdl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
113 changes: 59 additions & 54 deletions src/services/ui/src/components/Cards/OptionCard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,63 @@
import {describe, test, expect, beforeEach} from "vitest";
import {render, screen} from "@testing-library/react";
import {OptionCard, OptionFieldset} from "@/components/Cards/OptionCard";
import {ROUTES} from "@/routes";
import {BrowserRouter} from "react-router-dom";
import { describe, test, expect, beforeEach } from "vitest";
import { render, screen } from "@testing-library/react";
import { OptionCard, OptionFieldset } from "@/components/Cards/OptionCard";
import { ROUTES } from "@/routes";
import { BrowserRouter } from "react-router-dom";

describe("OptionCard Component System", () => {
describe("OptionFieldset", () => {
beforeEach(() => {
render(
<OptionFieldset legend={"Test Legend"}>
Testing rendering of children
</OptionFieldset>);
});
test("legend prop populates legend element in fieldset", () => {
expect(screen.getByRole("group", { name: "Test Legend" })).toBeInTheDocument();
});
test("renders children", () => {
expect(screen.getByText("Testing rendering of children")).toBeInTheDocument();
});
describe("OptionFieldset", () => {
beforeEach(() => {
render(
<OptionFieldset legend={"Test Legend"}>
Testing rendering of children
</OptionFieldset>
);
});
describe("OptionCard", () => {
const renderOptionCard = (altBg: boolean) => {
render(
<BrowserRouter>
<OptionCard
linkTo={ROUTES.HOME}
title={"Test Card Title"}
description={"Test Card Description"}
altBg={altBg}
/>
</BrowserRouter>
);
};
test("default background is white", () => {
renderOptionCard(false);
const innerWrapper = screen.getByTestId("card-inner-wrapper");
expect(innerWrapper.className.includes("bg-white")).toBeTruthy();
expect(innerWrapper.className.includes("bg-slate-100")).toBeFalsy();
});
test("option for alternate background color", () => {
renderOptionCard(true);
const innerWrapper = screen.getByTestId("card-inner-wrapper");
expect(innerWrapper.className.includes("bg-slate-100")).toBeTruthy();
expect(innerWrapper.className.includes("bg-white")).toBeFalsy();
});
test("title is rendered as an h3 and styled", () => {
renderOptionCard(false);
const header = screen.getByRole("heading", {level: 3});
expect(header).toHaveTextContent("Test Card Title");
expect(header).toHaveClass("text-lg text-sky-600 font-bold my-2");
});
test("description is rendered", () => {
renderOptionCard(false);
expect(screen.getByText("Test Card Description")).toBeInTheDocument();
});
test("legend prop populates legend element in fieldset", () => {
expect(
screen.getByRole("group", { name: "Test Legend" })
).toBeInTheDocument();
});
});
test("renders children", () => {
expect(
screen.getByText("Testing rendering of children")
).toBeInTheDocument();
});
});
describe("OptionCard", () => {
const renderOptionCard = (altBg: boolean) => {
render(
<BrowserRouter>
<OptionCard
linkTo={ROUTES.HOME}
title={"Test Card Title"}
description={"Test Card Description"}
altBg={altBg}
/>
</BrowserRouter>
);
};
test("default background is white", () => {
renderOptionCard(false);
const innerWrapper = screen.getByTestId("card-inner-wrapper");
expect(innerWrapper.className.includes("bg-white")).toBeTruthy();
expect(innerWrapper.className.includes("bg-slate-100")).toBeFalsy();
});
test("option for alternate background color", () => {
renderOptionCard(true);
const innerWrapper = screen.getByTestId("card-inner-wrapper");
expect(innerWrapper.className.includes("bg-slate-100")).toBeTruthy();
expect(innerWrapper.className.includes("bg-white")).toBeFalsy();
});
test("title is rendered as an h3 and styled", () => {
renderOptionCard(false);
const header = screen.getByRole("heading", { level: 3 });
expect(header).toHaveTextContent("Test Card Title");
expect(header).toHaveClass("text-lg text-sky-600 font-bold my-2");
});
test("description is rendered", () => {
renderOptionCard(false);
expect(screen.getByText("Test Card Description")).toBeInTheDocument();
});
});
});
87 changes: 43 additions & 44 deletions src/services/ui/src/components/Cards/OptionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,56 @@
import React, {PropsWithChildren, ReactNode} from "react";
import {Link} from "react-router-dom";
import {ROUTES} from "@/routes";
import {ChevronRight} from "lucide-react";
import {CardWithTopBorder} from "@/components";
import React, { PropsWithChildren, ReactNode } from "react";
import { Link } from "react-router-dom";
import { ROUTES } from "@/routes";
import { ChevronRight } from "lucide-react";
import { CardWithTopBorder } from "@/components";

export type OptionCardFieldsetProps = PropsWithChildren<{
legend: string;
legend: string;
}>;
export type MACFieldsetOption = {
title: string,
description: ReactNode,
linkTo: ROUTES | string;
altBg?: boolean;
title: string;
description: ReactNode;
linkTo: ROUTES | string;
altBg?: boolean;
};
/** A fieldset for nesting {@link OptionCard} with MACCard styling */
export const OptionFieldset = ({
children,
legend,
children,
legend,
}: OptionCardFieldsetProps) => {
return (
<section className="max-w-3xl mx-auto mb-6">
<fieldset>
<legend className="text-2xl font-medium py-8">{legend}</legend>
<CardWithTopBorder>
{children}
</CardWithTopBorder>
</fieldset>
</section>
);
return (
<section className="max-w-3xl mx-auto mb-6">
<fieldset>
<legend className="text-2xl font-medium py-8">{legend}</legend>
<CardWithTopBorder>{children}</CardWithTopBorder>
</fieldset>
</section>
);
};
/** An element for use in options lists that lead to a destination, such as
* the new submission options found in {@link NewSubmissionInitialOptions} */
export const OptionCard = ({
title,
description,
linkTo,
altBg = false
title,
description,
linkTo,
altBg = false,
}: MACFieldsetOption) => {
return (
<label>
<Link to={linkTo}>
<div data-testid={"card-inner-wrapper"} className={`flex items-center justify-between gap-6 px-6 py-4 ${altBg ? "bg-slate-100" : "bg-white"} hover:bg-sky-100`}>
<div>
<h3 className="text-lg text-sky-600 font-bold my-2">
{title}
</h3>
<p className="my-2 text-slate-600">
{description}
</p>
</div>
<ChevronRight className="text-sky-600 w-8 h-8" />
</div>
</Link>
</label>
);
};
return (
<label>
<Link to={linkTo} relative={"path"}>
<div
data-testid={"card-inner-wrapper"}
className={`flex items-center justify-between gap-6 px-6 py-4 ${
altBg ? "bg-slate-100" : "bg-white"
} hover:bg-sky-100`}
>
<div>
<h3 className="text-lg text-sky-600 font-bold my-2">{title}</h3>
<p className="my-2 text-slate-600">{description}</p>
</div>
<ChevronRight className="text-sky-600 w-8 h-8" />
</div>
</Link>
</label>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PropsWithChildren } from "react";

export const SimplePageContainer = ({ children }: PropsWithChildren) => (
<div className="max-w-screen-xl mx-auto px-4 lg:px-8">{children}</div>
);
1 change: 1 addition & 0 deletions src/services/ui/src/components/Container/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SimplePageContainer } from "./SimplePageContainer";
1 change: 1 addition & 0 deletions src/services/ui/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from "./AdditionalInfo";
export * from "./AttachmentsList";
export * from "./Cards";
export * from "./Chip";
export * from "./Container";
export * from "./DetailsSection";
export * from "./ErrorAlert";
export * from "./HowItWorks";
Expand Down
133 changes: 92 additions & 41 deletions src/services/ui/src/pages/create/create-options.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,107 @@
import React, {PropsWithChildren} from "react";
import {MACFieldsetOption, OptionCard, OptionFieldset} from "@/components/Cards/OptionCard";
import {AUTHORITY_OPTIONS, SPA_OPTIONS} from "@/pages/create/options";
import {
MACFieldsetOption,
OptionCard,
OptionFieldset,
} from "@/components/Cards/OptionCard";
import {
AUTHORITY_OPTIONS,
B4_WAIVER_OPTIONS,
B_WAIVER_OPTIONS,
BCAP_WAIVER_OPTIONS,
CHIP_SPA_OPTIONS,
MEDICAID_SPA_OPTIONS,
SPA_OPTIONS,
WAIVER_OPTIONS,
} from "@/pages/create/options";
import { SimplePageContainer } from "@/components";

/** I didn't see a page container already present in the repo, so to better standardize
* we can extract this and use it on more pages where currently each page has its own. (KH) */
export const SimplePageContainer = ({ children }: PropsWithChildren) => (
<div className="max-w-screen-xl mx-auto px-4 lg:px-8">
{children}
</div>
);
/** Can be removed once page title bar with back nav is integrated */
export const SimplePageTitle = ({ title }: { title: string }) => (
<div className="flex items-center justify-between my-4">
<h1 className="text-xl">{title}</h1>
</div>
<div className="flex items-center justify-between my-4">
<h1 className="text-xl">{title}</h1>
</div>
);

export type OptionData = Omit<MACFieldsetOption, "altBg">
export type OptionData = Omit<MACFieldsetOption, "altBg">;
type OptionsPageProps = {
options: OptionData[]
title: string
fieldsetLegend: string
}
options: OptionData[];
title: string;
fieldsetLegend: string;
};
/** A page for rendering an array of {@link OptionData} */
const OptionsPage = ({ options, title, fieldsetLegend }: OptionsPageProps) => {
return (
<SimplePageContainer>
<SimplePageTitle title={title}/>
<OptionFieldset legend={fieldsetLegend}>
{options.map((opt, idx) =>
<OptionCard
key={idx}
{...opt}
altBg={idx % 2 === 1} // Even number option cards show altBg
/>
)}
</OptionFieldset>
</SimplePageContainer>
);
return (
<SimplePageContainer>
<SimplePageTitle title={title} />
<OptionFieldset legend={fieldsetLegend}>
{options.map((opt, idx) => (
<OptionCard
key={idx}
{...opt}
altBg={idx % 2 === 1} // Even number option cards show altBg
/>
))}
</OptionFieldset>
</SimplePageContainer>
);
};
/** Initial set of options for a New Submission */
export const NewSubmissionInitialOptions = () => (
<OptionsPage
title="Submission Type"
fieldsetLegend="Select a Submission Type."
options={AUTHORITY_OPTIONS}
/>
<OptionsPage
title="Submission Type"
fieldsetLegend="Select a Submission Type."
options={AUTHORITY_OPTIONS}
/>
);
/** Choice between the main SPA types when creating a New Submission */
export const SPASubmissionOptions = () => (
<OptionsPage
title="SPA Type"
fieldsetLegend="Select a SPA type to start your submission"
options={SPA_OPTIONS} />
<OptionsPage
title="SPA Type"
fieldsetLegend="Select a SPA type to start your submission"
options={SPA_OPTIONS}
/>
);
/** Sub-choices for Medicaid SPAs */
export const MedicaidSPASubmissionOptions = () => (
<OptionsPage
title="Medicaid SPA Type"
fieldsetLegend="Select a Medicaid SPA type to create your submission"
options={MEDICAID_SPA_OPTIONS}
/>
);
/** Sub-choices for CHIP SPAs */
export const ChipSPASubmissionOptions = () => (
<OptionsPage
title="CHIP SPA Type"
fieldsetLegend="Select a CHIP SPA type to create your submission"
options={CHIP_SPA_OPTIONS}
/>
);
export const WaiverSubmissionOptions = () => (
<OptionsPage
title="Waiver Action Type"
fieldsetLegend="Select a Waiver type to start your submission."
options={WAIVER_OPTIONS}
/>
);
export const BWaiverSubmissionOptions = () => (
<OptionsPage
title="1915(b) Waiver Action Type"
fieldsetLegend="Select a 1915(b) Waiver type for your submission."
options={B_WAIVER_OPTIONS}
/>
);
export const B4WaiverSubmissionOptions = () => (
<OptionsPage
title="1915(b)(4) FFS Selective Contracting Waiver Authority"
fieldsetLegend="Select a Waiver type to start your submission."
options={B4_WAIVER_OPTIONS}
/>
);
export const BCapWaiverSubmissionOptions = () => (
<OptionsPage
title="1915(b) Comprehensive (Capitated) Waiver Authority"
fieldsetLegend="Select a Waiver type to start your submission."
options={BCAP_WAIVER_OPTIONS}
/>
);
Loading

0 comments on commit 9d00dbc

Please sign in to comment.