[#32] added app list and user icon to menubar

This commit is contained in:
Camille Moussu
2025-08-18 18:10:28 +02:00
committed by Benoit TELLIER
parent 4b93d7dea9
commit aa498ac91f
7 changed files with 170 additions and 20 deletions
+1
View File
@@ -17,6 +17,7 @@
.env.development.js
.env.test.js
.env.production.js
appList.js
npm-debug.log*
yarn-debug.log*
+55 -3
View File
@@ -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(<Menubar />);
(window as any).appList = [
{ name: "Twake", link: "/twake", icon: "twake.svg" },
{ name: "Calendar", link: "/calendar", icon: "calendar.svg" },
];
renderWithProviders(<Menubar />, 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(<Menubar />, 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(<Menubar />, 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(<Menubar />, preloadedState);
expect(screen.getByTestId("AppsIcon")).toBeInTheDocument();
});
it("opens popover when clicking AppsIcon", () => {
(window as any).appList = [{ name: "test", icon: "test", link: "test" }];
renderWithProviders(<Menubar />, 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(<Menubar />, preloadedState);
const appsButton = screen.getByRole("button");
fireEvent.click(appsButton);
const twakeLink = screen.getByRole("link", { name: /test/i });
expect(twakeLink).toHaveAttribute("href", "test");
});
});
+7
View File
@@ -0,0 +1,7 @@
var appList = [
{
name: "Twake",
link: "/twake",
icon: "https://twake-chat.com/tild3037-6365-4134-b838-383030366263__twake-chat-logo.svg",
},
];
+1
View File
@@ -32,6 +32,7 @@
/>
<title>React App</title>
<script src="<%= assetPrefix %>/.env.js"></script>
<script src="<%= assetPrefix %>/appList.js"></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
+18 -6
View File
@@ -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;
}
+87 -10
View File
@@ -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 | HTMLElement>(null);
const dispatch = useAppDispatch();
if (!user) {
dispatch(push("/"));
}
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
return (
<header className="menubar">
<MainTitle/>
<div className="menubar-item search-bar">
<p>big search bar</p>
</div>
<div className="menubar-item">
<p>Account stuff</p>
</div>
</header>
<>
<header className="menubar">
<MainTitle />
<div className="menubar-item search-bar">
<p>big search bar</p>
</div>
<div className="menubar-item">
{applist.length > 0 && (
<IconButton onClick={handleOpen}>
<AppsIcon />
</IconButton>
)}
<Avatar
sx={{ bgcolor: stringToColor(user.family_name) }}
sizes="large"
>
{user.name[0]}
{user.family_name[0]}
</Avatar>
</div>
</header>
<Popover
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<div className="app-grid">
{applist.map((prop: AppIconProps) => (
<AppIcon prop={prop} />
))}
</div>
</Popover>
</>
);
}
export function MainTitle() {
@@ -25,3 +87,18 @@ export function MainTitle() {
</div>
);
}
function AppIcon({ prop }: { prop: AppIconProps }) {
return (
<a
key={prop.name}
href={prop.link}
style={{ textDecoration: "none", color: "inherit" }}
>
<div>
<img src={prop.icon} alt={prop.name} />
<p>{prop.name}</p>
</div>
</a>
);
}
+1 -1
View File
@@ -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);