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 = (