From 09f6fc81c86c40f085b4f0e2d891772a854785c2 Mon Sep 17 00:00:00 2001 From: mohanakatari119-bit Date: Tue, 5 May 2026 22:41:31 +0530 Subject: [PATCH 1/2] fix: add mobile hamburger menu to header Signed-off-by: mohanakatari119-bit --- .../src/components/layout/header.test.tsx | 49 +++++++++++++++++++ .../src/components/layout/header.tsx | 44 ++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/ecosystem-explorer/src/components/layout/header.test.tsx b/ecosystem-explorer/src/components/layout/header.test.tsx index 365c276b..954e2cb5 100644 --- a/ecosystem-explorer/src/components/layout/header.test.tsx +++ b/ecosystem-explorer/src/components/layout/header.test.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { MemoryRouter } from "react-router-dom"; import { describe, it, expect } from "vitest"; import { Header } from "./header"; @@ -67,4 +68,52 @@ describe("Header", () => { const homeLink = screen.getByRole("link", { name: /otel explorer/i }); expect(homeLink).toHaveAttribute("href", "/"); }); + + it("shows hamburger button", () => { + render( + +
+ + ); + + expect(screen.getByRole("button", { name: /open menu/i })).toBeInTheDocument(); + }); + + it("toggles mobile menu open and closed", async () => { + const user = userEvent.setup(); + render( + +
+ + ); + + const toggleButton = screen.getByRole("button", { name: /open menu/i }); + expect(toggleButton).toHaveAttribute("aria-expanded", "false"); + + await user.click(toggleButton); + + expect(screen.getByRole("button", { name: /close menu/i })).toHaveAttribute( + "aria-expanded", + "true" + ); + expect(screen.getByRole("navigation", { name: /mobile main/i })).toBeInTheDocument(); + }); + + it("closes mobile menu when a nav link is clicked", async () => { + const user = userEvent.setup(); + render( + +
+ + ); + + await user.click(screen.getByRole("button", { name: /open menu/i })); + expect(screen.getByRole("navigation", { name: /mobile main/i })).toBeInTheDocument(); + + const mobileNav = screen.getByRole("navigation", { name: /mobile main/i }); + const javaAgentLink = mobileNav.querySelector("a"); + await user.click(javaAgentLink!); + + expect(screen.queryByRole("navigation", { name: /mobile main/i })).not.toBeInTheDocument(); + }); }); diff --git a/ecosystem-explorer/src/components/layout/header.tsx b/ecosystem-explorer/src/components/layout/header.tsx index 7cf06143..30abfedf 100644 --- a/ecosystem-explorer/src/components/layout/header.tsx +++ b/ecosystem-explorer/src/components/layout/header.tsx @@ -13,10 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { useState } from "react"; +import { Menu, X } from "lucide-react"; import { Link } from "react-router-dom"; import { OtelLogo } from "@/components/icons/otel-logo"; export function Header() { + const [menuOpen, setMenuOpen] = useState(false); + return (
@@ -24,7 +28,7 @@ export function Header() { OTel Explorer -
+ {menuOpen && ( + + )}
); } From 4a7b1a5a40eb6d588d70788fa37a8f73cafbb0cc Mon Sep 17 00:00:00 2001 From: mohanakatari119-bit Date: Wed, 6 May 2026 14:07:13 +0530 Subject: [PATCH 2/2] fix(header): address Copilot review suggestions --- .../src/components/layout/header.test.tsx | 5 +- .../src/components/layout/header.tsx | 72 +++++++++---------- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/ecosystem-explorer/src/components/layout/header.test.tsx b/ecosystem-explorer/src/components/layout/header.test.tsx index 954e2cb5..f6f10c1f 100644 --- a/ecosystem-explorer/src/components/layout/header.test.tsx +++ b/ecosystem-explorer/src/components/layout/header.test.tsx @@ -110,8 +110,9 @@ describe("Header", () => { await user.click(screen.getByRole("button", { name: /open menu/i })); expect(screen.getByRole("navigation", { name: /mobile main/i })).toBeInTheDocument(); - const mobileNav = screen.getByRole("navigation", { name: /mobile main/i }); - const javaAgentLink = mobileNav.querySelector("a"); + const javaAgentLink = screen.getAllByRole("link", { name: /java agent/i }).find( + (el) => el.closest("nav[aria-label='Mobile main']") + ); await user.click(javaAgentLink!); expect(screen.queryByRole("navigation", { name: /mobile main/i })).not.toBeInTheDocument(); diff --git a/ecosystem-explorer/src/components/layout/header.tsx b/ecosystem-explorer/src/components/layout/header.tsx index 30abfedf..7d225f88 100644 --- a/ecosystem-explorer/src/components/layout/header.tsx +++ b/ecosystem-explorer/src/components/layout/header.tsx @@ -13,13 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Menu, X } from "lucide-react"; -import { Link } from "react-router-dom"; +import { Link, useLocation } from "react-router-dom"; import { OtelLogo } from "@/components/icons/otel-logo"; +const NAV_ITEMS = [ + { to: "/java-agent", label: "Java Agent" }, + { to: "/collector", label: "Collector" }, +] as const; + export function Header() { const [menuOpen, setMenuOpen] = useState(false); + const location = useLocation(); + + useEffect(() => { + setMenuOpen(false); + }, [location.pathname]); return (
@@ -29,18 +39,15 @@ export function Header() { OTel Explorer - {menuOpen && ( -
); }