From aa498ac91f05d9107d420d40c0cd3a61549f7141 Mon Sep 17 00:00:00 2001 From: Camille Moussu Date: Mon, 18 Aug 2025 18:10:28 +0200 Subject: [PATCH] [#32] added app list and user icon to menubar --- .gitignore | 1 + __test__/components/Menubar.test.tsx | 58 ++++++++++++++++- public/appList.example.js | 7 ++ public/index.html | 1 + src/components/Menubar/Menubar.css | 24 +++++-- src/components/Menubar/Menubar.tsx | 97 +++++++++++++++++++++++++--- src/features/Events/EventDisplay.tsx | 2 +- 7 files changed, 170 insertions(+), 20 deletions(-) create mode 100644 public/appList.example.js diff --git a/.gitignore b/.gitignore index b6b95a5..29a5d7e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ .env.development.js .env.test.js .env.production.js +appList.js npm-debug.log* yarn-debug.log* diff --git a/__test__/components/Menubar.test.tsx b/__test__/components/Menubar.test.tsx index b207021..b13d8fe 100644 --- a/__test__/components/Menubar.test.tsx +++ b/__test__/components/Menubar.test.tsx @@ -1,13 +1,65 @@ -import React from "react"; -import { screen } from "@testing-library/react"; +import { fireEvent, screen } from "@testing-library/react"; import "@testing-library/jest-dom"; import { Menubar } from "../../src/components/Menubar/Menubar"; import { renderWithProviders } from "../utils/Renderwithproviders"; describe("Calendar App Component Display Tests", () => { + const preloadedState = { + user: { + userData: { + sub: "test", + email: "test@test.com", + family_name: "Doe", + name: "John", + sid: "mockSid", + openpaasId: "667037022b752d0026472254", + }, + }, + }; test("renders the Menubar component", () => { - renderWithProviders(); + (window as any).appList = [ + { name: "Twake", link: "/twake", icon: "twake.svg" }, + { name: "Calendar", link: "/calendar", icon: "calendar.svg" }, + ]; + renderWithProviders(, preloadedState); const navbarElement = screen.getByText("Twake"); expect(navbarElement).toBeInTheDocument(); }); + it("renders the main title", () => { + (window as any).appList = [{ name: "test", icon: "test", link: "test" }]; + renderWithProviders(, preloadedState); + expect(screen.getByText(/Twake/i)).toBeInTheDocument(); + expect(screen.getByText(/Calendar/i)).toBeInTheDocument(); + }); + + it("shows avatar with user initials", () => { + (window as any).appList = [{ name: "test", icon: "test", link: "test" }]; + renderWithProviders(, preloadedState); + expect(screen.getByText("JD")).toBeInTheDocument(); + }); + + it("shows AppsIcon when applist is not empty", () => { + (window as any).appList = [{ name: "test", icon: "test", link: "test" }]; + renderWithProviders(, preloadedState); + expect(screen.getByTestId("AppsIcon")).toBeInTheDocument(); + }); + + it("opens popover when clicking AppsIcon", () => { + (window as any).appList = [{ name: "test", icon: "test", link: "test" }]; + renderWithProviders(, preloadedState); + const appsButton = screen.getByRole("button"); + fireEvent.click(appsButton); + expect(screen.getByText("Twake")).toBeInTheDocument(); + expect(screen.getByText("Calendar")).toBeInTheDocument(); + }); + + it("renders app icons as links", () => { + (window as any).appList = [{ name: "test", icon: "test", link: "test" }]; + renderWithProviders(, preloadedState); + const appsButton = screen.getByRole("button"); + fireEvent.click(appsButton); + + const twakeLink = screen.getByRole("link", { name: /test/i }); + expect(twakeLink).toHaveAttribute("href", "test"); + }); }); diff --git a/public/appList.example.js b/public/appList.example.js new file mode 100644 index 0000000..3a7a28d --- /dev/null +++ b/public/appList.example.js @@ -0,0 +1,7 @@ +var appList = [ + { + name: "Twake", + link: "/twake", + icon: "https://twake-chat.com/tild3037-6365-4134-b838-383030366263__twake-chat-logo.svg", + }, +]; diff --git a/public/index.html b/public/index.html index 6c49f1e..23a8b8d 100644 --- a/public/index.html +++ b/public/index.html @@ -32,6 +32,7 @@ /> React App + diff --git a/src/components/Menubar/Menubar.css b/src/components/Menubar/Menubar.css index 6996e9f..aafc6df 100644 --- a/src/components/Menubar/Menubar.css +++ b/src/components/Menubar/Menubar.css @@ -73,10 +73,22 @@ left: 887px; } -.menu-tools { - width: 383.20001220703125; - height: 49; - top: 16px; - left: 1511px; - gap: 24px; +.app-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; + padding: 16px; + justify-items: center; + align-items: center; +} + +.app-grid img { + width: 48px; + height: 48px; +} + +.app-grid p { + margin-top: 6px; + font-size: 14px; + text-align: center; } diff --git a/src/components/Menubar/Menubar.tsx b/src/components/Menubar/Menubar.tsx index b8bccef..8846723 100644 --- a/src/components/Menubar/Menubar.tsx +++ b/src/components/Menubar/Menubar.tsx @@ -1,17 +1,79 @@ -import React from "react"; +import React, { useState } from "react"; import logo from "../../static/images/calendar.svg"; +import AppsIcon from "@mui/icons-material/Apps"; import "./Menubar.css"; +import { useAppDispatch, useAppSelector } from "../../app/hooks"; +import { stringToColor } from "../../features/Events/EventDisplay"; +import { Avatar, IconButton, Popover } from "@mui/material"; +import { push } from "redux-first-history"; + +export type AppIconProps = { + name: string; + link: string; + icon: string; +}; + export function Menubar() { + const user = useAppSelector((state) => state.user.userData); + const applist: AppIconProps[] = (window as any).appList; + const [anchorEl, setAnchorEl] = useState(null); + const dispatch = useAppDispatch(); + + if (!user) { + dispatch(push("/")); + } + const handleOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); return ( -
- -
-

big search bar

-
-
-

Account stuff

-
-
+ <> +
+ +
+

big search bar

+
+ +
+ {applist.length > 0 && ( + + + + )} + + {user.name[0]} + {user.family_name[0]} + +
+
+ +
+ {applist.map((prop: AppIconProps) => ( + + ))} +
+
+ ); } export function MainTitle() { @@ -25,3 +87,18 @@ export function MainTitle() { ); } + +function AppIcon({ prop }: { prop: AppIconProps }) { + return ( + +
+ {prop.name} +

{prop.name}

+
+
+ ); +} diff --git a/src/features/Events/EventDisplay.tsx b/src/features/Events/EventDisplay.tsx index 182aac3..777115d 100644 --- a/src/features/Events/EventDisplay.tsx +++ b/src/features/Events/EventDisplay.tsx @@ -314,7 +314,7 @@ function formatDate(date: Date) { }); } -function stringToColor(string: string) { +export function stringToColor(string: string) { let hash = 0; for (let i = 0; i < string.length; i++) { hash = string.charCodeAt(i) + ((hash << 5) - hash);