From 7eec66836be106d484ee1df5303b3b92993b6fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Flor=C3=AAncio?= Date: Fri, 12 Jun 2026 09:50:35 +0100 Subject: [PATCH] fix: hide sidebar footer urls on hover (#1348) --- .../app-sidebar/app-sidebar-footer.test.tsx | 102 ++++++++++++++++++ .../app-sidebar/app-sidebar-footer.tsx | 16 +-- .../components/task/passthrough-terminal.tsx | 2 +- apps/web/e2e/helpers/api-client.ts | 36 +++++++ .../tests/layout/plan-panel-indicator.spec.ts | 51 +++++++-- apps/web/e2e/tests/pr/pr-status-badge.spec.ts | 79 ++++++++++++-- .../pr/pr-watcher-dockview-layout.spec.ts | 76 ++++++++++--- .../tests/terminal/mobile-terminal-helpers.ts | 11 +- .../terminal/mobile-terminal-keybar.spec.ts | 28 +++-- .../terminal/mobile-terminal-scroll.spec.ts | 21 ++-- 10 files changed, 351 insertions(+), 71 deletions(-) create mode 100644 apps/web/components/app-sidebar/app-sidebar-footer.test.tsx diff --git a/apps/web/components/app-sidebar/app-sidebar-footer.test.tsx b/apps/web/components/app-sidebar/app-sidebar-footer.test.tsx new file mode 100644 index 0000000000..0c42338e63 --- /dev/null +++ b/apps/web/components/app-sidebar/app-sidebar-footer.test.tsx @@ -0,0 +1,102 @@ +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import { TooltipProvider } from "@kandev/ui/tooltip"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + routerPush: vi.fn(), + toggleSettingsMode: vi.fn(), +})); + +const state = { + workspaces: { activeId: "ws-1" as string | null }, + appSidebar: { settingsMode: false }, + toggleAppSidebarSettingsMode: mocks.toggleSettingsMode, +}; + +let officeEnabled = false; + +vi.mock("next/navigation", () => ({ + useRouter: () => ({ push: mocks.routerPush }), +})); + +vi.mock("@/components/state-provider", () => ({ + useAppStore: (selector: (s: typeof state) => unknown) => selector(state), +})); + +vi.mock("@/hooks/domains/features/use-feature", () => ({ + useFeature: () => officeEnabled, +})); + +vi.mock("@/hooks/use-release-notes", () => ({ + useReleaseNotes: () => ({ + unseenEntries: [], + latestVersion: "0.0.0", + hasUnseen: false, + dialogOpen: false, + openDialog: vi.fn(), + closeDialog: vi.fn(), + hasNotes: false, + showTopbarButton: false, + }), +})); + +vi.mock("@/components/improve-kandev-dialog", () => ({ + ImproveKandevDialog: () => null, +})); + +vi.mock("@/components/release-notes/release-notes-dialog", () => ({ + ReleaseNotesDialog: () => null, +})); + +vi.mock("@/components/theme-toggle", () => ({ + ThemeToggle: () => , +})); + +import { AppSidebarFooter } from "./app-sidebar-footer"; + +function renderFooter() { + return render( + + + , + ); +} + +describe("AppSidebarFooter", () => { + beforeEach(() => { + officeEnabled = false; + state.appSidebar.settingsMode = false; + mocks.routerPush.mockClear(); + mocks.toggleSettingsMode.mockClear(); + }); + + afterEach(() => cleanup()); + + it("renders navigation icons as buttons so hover does not expose link URLs", () => { + officeEnabled = true; + + renderFooter(); + + const statsButton = screen.getByRole("button", { name: "Stats" }); + const officeButton = screen.getByRole("button", { name: "Office" }); + + expect(statsButton).toBeTruthy(); + expect(officeButton).toBeTruthy(); + expect(statsButton.getAttribute("href")).toBeNull(); + expect(officeButton.getAttribute("href")).toBeNull(); + expect(screen.queryByRole("link", { name: "Stats" })).toBeNull(); + expect(screen.queryByRole("link", { name: "Office" })).toBeNull(); + }); + + it("navigates from the Stats and Office footer buttons", () => { + officeEnabled = true; + + renderFooter(); + + fireEvent.click(screen.getByRole("button", { name: "Stats" })); + fireEvent.click(screen.getByRole("button", { name: "Office" })); + + expect(mocks.routerPush).toHaveBeenNthCalledWith(1, "/stats"); + expect(mocks.routerPush).toHaveBeenNthCalledWith(2, "/office"); + }); +}); diff --git a/apps/web/components/app-sidebar/app-sidebar-footer.tsx b/apps/web/components/app-sidebar/app-sidebar-footer.tsx index 84870e4532..873daf7678 100644 --- a/apps/web/components/app-sidebar/app-sidebar-footer.tsx +++ b/apps/web/components/app-sidebar/app-sidebar-footer.tsx @@ -1,7 +1,6 @@ "use client"; import { useState } from "react"; -import Link from "next/link"; import { useRouter } from "next/navigation"; import { IconBuildings, @@ -31,7 +30,6 @@ type FooterIconButtonProps = { label: string; collapsed: boolean; onClick?: () => void; - href?: string; badge?: boolean; testId?: string; /** Toggle state: rotates the icon a half-turn (spins back out when cleared). */ @@ -43,7 +41,6 @@ function FooterIconButton({ label, collapsed, onClick, - href, badge, testId, active, @@ -68,14 +65,9 @@ function FooterIconButton({ ); - const trigger = href ? ( - - ) : ( + const trigger = (