From 01230520d625462b95d843769306a60d6aa25d39 Mon Sep 17 00:00:00 2001 From: 13bfrancis <40218571+13bfrancis@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:57:50 -0400 Subject: [PATCH 01/15] initial markup for the breadcrumb component --- .../ui/src/components/BreadCrumb/BreadCrumb.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx diff --git a/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx b/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx new file mode 100644 index 0000000000..92ddd02469 --- /dev/null +++ b/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx @@ -0,0 +1,16 @@ +import { Link } from "react-router-dom"; + +type BreadCrumbProps = { + to: string; +}; + +export const BreadCrumb = ({ + to, + children, +}: React.PropsWithChildren) => { + return {children}; +}; + +export const BreadCrumbBar = ({ children }: React.PropsWithChildren) => { + return
{children}
; +}; From e23d34efd33e7cf93dcd02e4a920a37a9a202228 Mon Sep 17 00:00:00 2001 From: 13bfrancis <40218571+13bfrancis@users.noreply.github.com> Date: Thu, 5 Oct 2023 12:49:29 -0400 Subject: [PATCH 02/15] add unit tests --- src/services/ui/package.json | 5 +- .../components/BreadCrumb/BreadCrumb.test.tsx | 68 +++++++++++++++++++ .../src/components/BreadCrumb/BreadCrumb.tsx | 14 +++- .../ui/src/components/BreadCrumb/index.tsx | 1 + yarn.lock | 5 ++ 5 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 src/services/ui/src/components/BreadCrumb/BreadCrumb.test.tsx create mode 100644 src/services/ui/src/components/BreadCrumb/index.tsx diff --git a/src/services/ui/package.json b/src/services/ui/package.json index 1866deb91a..aefc1a8d71 100644 --- a/src/services/ui/package.json +++ b/src/services/ui/package.json @@ -45,8 +45,6 @@ "aws-amplify": "^5.2.5", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", - "shared-types": "*", - "shared-utils": "*", "date-fns": "^2.30.0", "export-to-csv": "^0.2.1", "file-saver": "^2.0.5", @@ -61,6 +59,8 @@ "react-loader-spinner": "^5.3.4", "react-router-dom": "^6.10.0", "react-select": "^5.7.4", + "shared-types": "*", + "shared-utils": "*", "tailwind-merge": "^1.14.0", "tailwindcss-animate": "^1.0.7", "uuid": "^9.0.0", @@ -71,6 +71,7 @@ "@tailwindcss/typography": "^0.5.10", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.5.1", "@types/lodash.debounce": "^4.0.7", "@types/node": "^20.4.2", "@types/react": "^18.0.28", diff --git a/src/services/ui/src/components/BreadCrumb/BreadCrumb.test.tsx b/src/services/ui/src/components/BreadCrumb/BreadCrumb.test.tsx new file mode 100644 index 0000000000..bcddeb9fd3 --- /dev/null +++ b/src/services/ui/src/components/BreadCrumb/BreadCrumb.test.tsx @@ -0,0 +1,68 @@ +import { describe, test, expect, beforeAll, beforeEach } from "vitest"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { BreadCrumb, BreadCrumbBar } from "./BreadCrumb"; +import { BrowserRouter, Route, Routes, useLocation } from "react-router-dom"; + +export const LocationDisplay = () => { + const location = useLocation(); + + return
{location.pathname}
; +}; + +describe("Bread Crumb Tests", () => { + describe("Bread Crumb Routing", () => { + test("Sucessfully navigate using breadcrumbs", async () => { + render( + <> + + + + + + + Click Me + + + , + { wrapper: BrowserRouter } + ); + + const user = userEvent.setup(); + + await user.click(screen.getByText(/click me/i)); + expect(screen.getByText("/test")).toBeInTheDocument(); + }); + }); + + describe("Bread Crumb Interations", async () => { + beforeEach(() => { + render( + + + Home + + + Test Dashboard + + + Test Item + + , + { + wrapper: BrowserRouter, + } + ); + }); + + test("active element is styled different", async () => { + const homeBreadCrumb = screen.getByText("Home"); + const dashboardBreadCrumb = screen.getByText("Test Dashboard"); + const itemBreadCrumb = screen.getByText("Test Item"); + + expect(homeBreadCrumb.classList.contains("underline")).toBeFalsy(); + expect(dashboardBreadCrumb.classList.contains("underline")).toBeFalsy(); + expect(itemBreadCrumb.classList.contains("underline")).toBeTruthy(); + }); + }); +}); diff --git a/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx b/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx index 92ddd02469..ae14b50bc2 100644 --- a/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx +++ b/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx @@ -1,14 +1,26 @@ import { Link } from "react-router-dom"; +import { twMerge } from "tailwind-merge"; +import clsx from "clsx"; type BreadCrumbProps = { to: string; + active?: boolean; }; export const BreadCrumb = ({ to, + active = false, children, }: React.PropsWithChildren) => { - return {children}; + const activeClass = "underline"; + return ( + + {children} + + ); }; export const BreadCrumbBar = ({ children }: React.PropsWithChildren) => { diff --git a/src/services/ui/src/components/BreadCrumb/index.tsx b/src/services/ui/src/components/BreadCrumb/index.tsx new file mode 100644 index 0000000000..a3b184a87e --- /dev/null +++ b/src/services/ui/src/components/BreadCrumb/index.tsx @@ -0,0 +1 @@ +export * from "./BreadCrumb"; diff --git a/yarn.lock b/yarn.lock index 816e10629f..2820344cad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7124,6 +7124,11 @@ "@testing-library/dom" "^9.0.0" "@types/react-dom" "^18.0.0" +"@testing-library/user-event@^14.5.1": + version "14.5.1" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" + integrity sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg== + "@tokenizer/token@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" From 36b1a7f2eaec6797febd8eb30b74f018880cf520 Mon Sep 17 00:00:00 2001 From: 13bfrancis <40218571+13bfrancis@users.noreply.github.com> Date: Thu, 5 Oct 2023 14:55:54 -0400 Subject: [PATCH 03/15] add initial breadcrumbs with light styles --- .../src/components/BreadCrumb/BreadCrumb.tsx | 30 ++++++++++++++----- src/services/ui/src/pages/welcome/index.tsx | 9 ++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx b/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx index ae14b50bc2..9a5ead3300 100644 --- a/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx +++ b/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx @@ -1,28 +1,44 @@ import { Link } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import clsx from "clsx"; +import { type ReactNode } from "react"; +import { ChevronRight } from "lucide-react"; type BreadCrumbProps = { to: string; active?: boolean; + showSeperator?: boolean; + seperator?: ReactNode; }; export const BreadCrumb = ({ to, + seperator = , + showSeperator = true, active = false, children, }: React.PropsWithChildren) => { const activeClass = "underline"; + return ( - - {children} - +
  • +
    {showSeperator && seperator}
    + +
    {children}
    + +
  • ); }; +export const BreadCrumbSeperator = () => ; + export const BreadCrumbBar = ({ children }: React.PropsWithChildren) => { - return
    {children}
    ; + return ( + + ); }; diff --git a/src/services/ui/src/pages/welcome/index.tsx b/src/services/ui/src/pages/welcome/index.tsx index 0657618f77..7a4dcffc8c 100644 --- a/src/services/ui/src/pages/welcome/index.tsx +++ b/src/services/ui/src/pages/welcome/index.tsx @@ -5,6 +5,8 @@ import { QueryClient } from "@tanstack/react-query"; import { getUser } from "@/api/useGetUser"; import { Link } from "react-router-dom"; import { Button } from "@/components/Inputs"; +import { BreadCrumb, BreadCrumbBar } from "@/components/BreadCrumb"; +import { ChevronRight } from "lucide-react"; export const loader = (queryClient: QueryClient) => { return async () => { @@ -33,6 +35,13 @@ export const Welcome = () => { {/* End Hero Section */} {/* Two Column Main Layout */}
    + + + Submission Type + + SPA Type + Medicaid SPA Type +

    State Users

    From 20d49ef6d1095cae98ef969a5da0f5fabb02fe44 Mon Sep 17 00:00:00 2001 From: 13bfrancis <40218571+13bfrancis@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:42:56 -0400 Subject: [PATCH 04/15] rough draft breadcrumbs --- .../eslint-config-custom-server/index.js | 3 +- .../eslint-config-custom-server/package.json | 3 +- src/packages/eslint-config-custom/index.js | 3 +- .../eslint-config-custom/package.json | 3 +- .../src/components/BreadCrumb/BreadCrumb.tsx | 64 +++++++++++++++---- .../BreadCrumb/bread-crumb-config.ts | 57 +++++++++++++++++ .../ui/src/components/Cards/OptionCard.tsx | 8 ++- .../ui/src/components/Layout/index.tsx | 1 + .../ui/src/pages/create/create-options.tsx | 3 + yarn.lock | 47 +++++++++++++- 10 files changed, 171 insertions(+), 21 deletions(-) create mode 100644 src/services/ui/src/components/BreadCrumb/bread-crumb-config.ts diff --git a/src/packages/eslint-config-custom-server/index.js b/src/packages/eslint-config-custom-server/index.js index b815eb46fb..44d29b045f 100644 --- a/src/packages/eslint-config-custom-server/index.js +++ b/src/packages/eslint-config-custom-server/index.js @@ -10,10 +10,11 @@ module.exports = { ecmaVersion: "latest", sourceType: "module", }, - plugins: ["@typescript-eslint"], + plugins: ["@typescript-eslint", "eslint-plugin-tsdoc"], rules: { "linebreak-style": ["error", "unix"], quotes: ["error", "double"], semi: ["error", "always"], + "tsdoc/syntax": "warn", }, }; diff --git a/src/packages/eslint-config-custom-server/package.json b/src/packages/eslint-config-custom-server/package.json index f24c67d7e8..daa8eb9b92 100644 --- a/src/packages/eslint-config-custom-server/package.json +++ b/src/packages/eslint-config-custom-server/package.json @@ -7,6 +7,7 @@ "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.59.0", "@typescript-eslint/parser": "^5.59.0", - "eslint": "^8.38.0" + "eslint": "^8.38.0", + "eslint-plugin-tsdoc": "^0.2.17" } } diff --git a/src/packages/eslint-config-custom/index.js b/src/packages/eslint-config-custom/index.js index 07bc51e5d8..5dbd78bc37 100644 --- a/src/packages/eslint-config-custom/index.js +++ b/src/packages/eslint-config-custom/index.js @@ -14,12 +14,13 @@ module.exports = { ecmaVersion: "latest", sourceType: "module", }, - plugins: ["react", "@typescript-eslint"], + plugins: ["react", "@typescript-eslint", "eslint-plugin-tsdoc"], rules: { "linebreak-style": ["error", "unix"], quotes: ["error", "double"], semi: ["error", "always"], "react/react-in-jsx-scope": 0, + "tsdoc/syntax": "warn", }, settings: { react: { diff --git a/src/packages/eslint-config-custom/package.json b/src/packages/eslint-config-custom/package.json index e2b75dfd16..14d066bbdb 100644 --- a/src/packages/eslint-config-custom/package.json +++ b/src/packages/eslint-config-custom/package.json @@ -8,7 +8,8 @@ "@typescript-eslint/eslint-plugin": "^5.59.0", "@typescript-eslint/parser": "^5.59.0", "eslint": "^8.38.0", - "eslint-plugin-react": "^7.32.2" + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-tsdoc": "^0.2.17" }, "publishConfig": { "access": "public" diff --git a/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx b/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx index 9a5ead3300..fced8c18e8 100644 --- a/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx +++ b/src/services/ui/src/components/BreadCrumb/BreadCrumb.tsx @@ -1,8 +1,41 @@ import { Link } from "react-router-dom"; -import { twMerge } from "tailwind-merge"; -import clsx from "clsx"; import { type ReactNode } from "react"; import { ChevronRight } from "lucide-react"; +import { BreadCrumbConfig } from "./bread-crumb-config"; + +type BreadCrumbsProps = { + options: BreadCrumbConfig[]; +}; + +export const BreadCrumbs = ({ options }: BreadCrumbsProps) => { + const defaultBreadCrumb = options.find((option) => option.default); + + return ( + + {defaultBreadCrumb && ( + + {defaultBreadCrumb.displayText} + + )} + {/* After this we map over the config and check to see if the breadcrumb needs to be displayed. Proper route paths are important here. It should be hierarchical */} + {options + .filter((option) => !option.default) + .filter((option) => window.location.href.includes(option.to)) + .toSorted((option, prevOption) => option.order - prevOption.order) + .map(({ displayText, to }, index, optionsArray) => { + return ( + + {displayText} + + ); + })} + + ); +}; type BreadCrumbProps = { to: string; @@ -15,20 +48,19 @@ export const BreadCrumb = ({ to, seperator = , showSeperator = true, - active = false, + active = true, children, }: React.PropsWithChildren) => { - const activeClass = "underline"; - return (
  • -
    {showSeperator && seperator}
    - -
    {children}
    - + {showSeperator && {seperator}} + + {active && ( + + {children} + + )} + {!active && {children}}
  • ); }; @@ -37,8 +69,12 @@ export const BreadCrumbSeperator = () => ; export const BreadCrumbBar = ({ children }: React.PropsWithChildren) => { return ( -