From 7733bfb248e3596d7822ad0fca289ae92d36ac40 Mon Sep 17 00:00:00 2001 From: Loris Van Katwijk Date: Fri, 10 May 2024 11:59:12 +0200 Subject: [PATCH] feat: App title in the header and current app selected in the Drawer --- src/components/applications/JobMonitor.tsx | 10 +---- src/components/applications/UserDashboard.tsx | 7 +--- src/components/ui/ApplicationHeader.tsx | 31 ++++++++++++++++ src/components/ui/DrawerItem.tsx | 4 ++ src/hooks/application.tsx | 37 +++++++++++++++++++ src/hooks/theme.tsx | 12 ++++++ test/e2e/jobMonitor.cy.ts | 2 +- test/unit-tests/UserDashboard.test.tsx | 19 ++++++++++ 8 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 src/components/ui/ApplicationHeader.tsx create mode 100644 src/hooks/application.tsx diff --git a/src/components/applications/JobMonitor.tsx b/src/components/applications/JobMonitor.tsx index 09c2fa6f..2bd7930b 100644 --- a/src/components/applications/JobMonitor.tsx +++ b/src/components/applications/JobMonitor.tsx @@ -1,22 +1,16 @@ "use client"; import * as React from "react"; -import CssBaseline from "@mui/material/CssBaseline"; -import { Box } from "@mui/material"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; import { JobDataTable } from "../ui/JobDataTable"; -import Dashboard from "../layout/Dashboard"; -import { useMUITheme } from "@/hooks/theme"; +import ApplicationHeader from "@/components/ui/ApplicationHeader"; /** * Build the Job Monitor application * @returns Job Monitor content */ export default function JobMonitor() { - const theme = useMUITheme(); - return (
-

Job Monitor

+
); diff --git a/src/components/applications/UserDashboard.tsx b/src/components/applications/UserDashboard.tsx index 57b93774..c64b2ec0 100644 --- a/src/components/applications/UserDashboard.tsx +++ b/src/components/applications/UserDashboard.tsx @@ -1,12 +1,8 @@ "use client"; import * as React from "react"; -import CssBaseline from "@mui/material/CssBaseline"; -import { Box } from "@mui/material"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; import { useOidcAccessToken } from "@axa-fr/react-oidc"; -import Dashboard from "../layout/Dashboard"; import { useOIDCContext } from "@/hooks/oidcConfiguration"; -import { useMUITheme } from "@/hooks/theme"; +import ApplicationHeader from "@/components/ui/ApplicationHeader"; /** * Build the User Dashboard page @@ -22,6 +18,7 @@ export default function UserDashboard() { return (
+

Hello {accessTokenPayload["preferred_username"]}

To start with, select an application in the side bar

diff --git a/src/components/ui/ApplicationHeader.tsx b/src/components/ui/ApplicationHeader.tsx new file mode 100644 index 00000000..fa853358 --- /dev/null +++ b/src/components/ui/ApplicationHeader.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { Stack, Typography } from "@mui/material"; +import { useApplicationTitle } from "@/hooks/application"; + +/** + * Application Header component with the application title and type + * @param type the type of the application + * @returns the application header + */ +export default function ApplicationHeader({ type }: { type: string }) { + const appTitle = useApplicationTitle(); + return ( +
+ + {appTitle && ( + + {appTitle} + + )} + + {type} + + +
+ ); +} diff --git a/src/components/ui/DrawerItem.tsx b/src/components/ui/DrawerItem.tsx index 71f11bb8..f35dbfa2 100644 --- a/src/components/ui/DrawerItem.tsx +++ b/src/components/ui/DrawerItem.tsx @@ -23,6 +23,7 @@ import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; import { ThemeProvider } from "@/contexts/ThemeProvider"; import { useMUITheme } from "@/hooks/theme"; import { useSearchParamsUtils } from "@/hooks/searchParamsUtils"; +import { useApplicationId } from "@/hooks/application"; /** * Represents a drawer item component. @@ -50,6 +51,8 @@ export default function DrawerItem({ // Represents the closest edge to the mouse cursor const [closestEdge, setClosestEdge]: any = useState(null); + const appId = useApplicationId(); + useEffect(() => { if (!dragRef.current || !handleRef.current) return; const element = dragRef.current; @@ -148,6 +151,7 @@ export default function DrawerItem({ onClick={() => setParam("appId", id)} sx={{ pl: 2, borderRadius: 2, pr: 1 }} ref={dragRef} + selected={appId === id} > diff --git a/src/hooks/application.tsx b/src/hooks/application.tsx new file mode 100644 index 00000000..28c40c3a --- /dev/null +++ b/src/hooks/application.tsx @@ -0,0 +1,37 @@ +import { useContext, useMemo } from "react"; +import { useSearchParamsUtils } from "@/hooks/searchParamsUtils"; +import { ApplicationsContext } from "@/contexts/ApplicationsProvider"; + +/** + * Custom hook to access the application id from the URL + * @returns the application id + */ +export function useApplicationId() { + const { getParam } = useSearchParamsUtils(); + + return useMemo(() => { + return getParam("appId"); + }, [getParam]); +} + +/** + * Custom hook to access the application title based on the application id + * @returns the application title + */ +export function useApplicationTitle() { + const [sections] = useContext(ApplicationsContext); + const appId = useApplicationId(); + + return useMemo(() => { + if (!sections || !appId) return null; + + const app = sections.reduce( + (acc, section) => { + if (acc) return acc; + return section.items.find((app) => app.id === appId); + }, + undefined as { title: string } | undefined, + ); + return app?.title; + }, [sections, appId]); +} diff --git a/src/hooks/theme.tsx b/src/hooks/theme.tsx index 03ce9a0d..bf0bd645 100644 --- a/src/hooks/theme.tsx +++ b/src/hooks/theme.tsx @@ -170,6 +170,18 @@ export const useMUITheme = () => { }, }, }, + MuiListItemButton: { + styleOverrides: { + root: { + "&.Mui-selected, &.Mui-selected:hover": { + backgroundColor: + muiTheme.palette.mode === "light" + ? lighten(grey[200], 0.2) + : darken(grey[800], 0.2), + }, + }, + }, + }, }; return muiTheme; diff --git a/test/e2e/jobMonitor.cy.ts b/test/e2e/jobMonitor.cy.ts index 6ca25e6f..658b62d9 100644 --- a/test/e2e/jobMonitor.cy.ts +++ b/test/e2e/jobMonitor.cy.ts @@ -23,7 +23,7 @@ describe("Job Monitor", () => { }); it("should render the drawer", () => { - cy.get("h2").contains("Job Monitor").should("be.visible"); + cy.get("header").contains("Job Monitor").should("be.visible"); }); it("should handle filter addition", () => { diff --git a/test/unit-tests/UserDashboard.test.tsx b/test/unit-tests/UserDashboard.test.tsx index 9ef6e16a..a184711d 100644 --- a/test/unit-tests/UserDashboard.test.tsx +++ b/test/unit-tests/UserDashboard.test.tsx @@ -15,6 +15,25 @@ jest.mock("@axa-fr/react-oidc", () => ({ useOidcAccessToken: jest.fn(), })); +jest.mock("jsoncrush", () => ({ + crush: jest.fn().mockImplementation((data) => `crushed-${data}`), + uncrush: jest.fn().mockImplementation((data) => data.replace("crushed-", "")), +})); + +const params = new URLSearchParams(); + +jest.mock("next/navigation", () => { + return { + usePathname: () => ({ + pathname: "", + }), + useRouter: () => ({ + push: jest.fn(), + }), + useSearchParams: () => params, + }; +}); + describe("", () => { it("renders not authenticated message when accessTokenPayload is not defined", () => { (useOidc as jest.Mock).mockReturnValue({ isAuthenticated: false });