diff --git a/packages/ui/src/features/canvas/components/ChannelNav.test.tsx b/packages/ui/src/features/canvas/components/ChannelNav.test.tsx
index fe2da703cb..c0a07f9162 100644
--- a/packages/ui/src/features/canvas/components/ChannelNav.test.tsx
+++ b/packages/ui/src/features/canvas/components/ChannelNav.test.tsx
@@ -74,9 +74,48 @@ describe("ChannelNav", () => {
const activity = screen.getByLabelText("Activity");
expect(activity).toBeEnabled();
+ expect(activity).not.toHaveAttribute("aria-haspopup");
await user.hover(activity);
await new Promise((resolve) => setTimeout(resolve, 400));
expect(screen.queryByText("Recent activity card")).not.toBeInTheDocument();
});
+
+ it("leaves no popover state on the bell after it navigates to Activity", async () => {
+ const user = userEvent.setup();
+ const { rerender } = render();
+ const bell = () => screen.getByLabelText("Activity");
+
+ await user.hover(bell());
+ await screen.findByText("Recent activity card", {}, { timeout: 1_000 });
+ await user.click(bell());
+ mocks.view = { type: "activity" };
+ rerender();
+
+ expect(screen.queryByText("Recent activity card")).not.toBeInTheDocument();
+ expect(bell()).not.toHaveAttribute("data-popup-open");
+ expect(bell()).not.toHaveAttribute("data-pressed");
+ });
+
+ it("neither resurfaces nor wedges the hover card once the bell has navigated", async () => {
+ const user = userEvent.setup();
+ const { rerender } = render();
+ const bell = () => screen.getByLabelText("Activity");
+
+ await user.hover(bell());
+ await user.click(bell());
+ mocks.view = { type: "activity" };
+ rerender();
+ await user.unhover(bell());
+
+ mocks.view = { type: "task-detail" };
+ rerender();
+ await new Promise((resolve) => setTimeout(resolve, 400));
+ expect(screen.queryByText("Recent activity card")).not.toBeInTheDocument();
+
+ await user.hover(bell());
+ expect(
+ await screen.findByText("Recent activity card", {}, { timeout: 1_000 }),
+ ).toBeInTheDocument();
+ });
});
diff --git a/packages/ui/src/features/canvas/components/ChannelNav.tsx b/packages/ui/src/features/canvas/components/ChannelNav.tsx
index fe76afeb19..a92e23a96b 100644
--- a/packages/ui/src/features/canvas/components/ChannelNav.tsx
+++ b/packages/ui/src/features/canvas/components/ChannelNav.tsx
@@ -39,7 +39,12 @@ import {
} from "@posthog/ui/router/navigationBridge";
import { useAppView } from "@posthog/ui/router/useAppView";
import { track } from "@posthog/ui/shell/analytics";
-import { type ComponentPropsWithRef, type ReactNode, useState } from "react";
+import {
+ type ComponentPropsWithRef,
+ type ReactElement,
+ type ReactNode,
+ useState,
+} from "react";
import { ActivityHoverCard } from "./ActivityHoverCard";
const INBOX_REFETCH_INTERVAL_MS = 60_000;
@@ -133,10 +138,51 @@ function NavButton({
);
}
+function ActivityHoverPopover({ trigger }: { trigger: ReactElement }) {
+ const [open, setOpen] = useState(false);
+
+ return (
+
+ event.preventBaseUIHandler()}
+ render={trigger}
+ />
+ {open && (
+ setOpen(false)} />
+ )}
+
+ );
+}
+
+function ActivityNavItem({
+ isActive,
+ unreadCount,
+ onNavigate,
+}: {
+ isActive: boolean;
+ unreadCount: number;
+ onNavigate: () => void;
+}) {
+ const bell = (
+ }
+ label="Activity"
+ isActive={isActive}
+ onClick={onNavigate}
+ badge={}
+ />
+ );
+
+ if (isActive) return bell;
+ return ;
+}
+
export function ChannelNav() {
const view = useAppView();
const loopsEnabled = useFeatureFlag(LOOPS_FLAG, import.meta.env.DEV);
- const [activityOpen, setActivityOpen] = useState(false);
const { counts } = useInboxAllReports({
ignoreFilters: true,
@@ -178,44 +224,11 @@ export function ChannelNav() {
}
/>
- setActivityOpen(!isActivity && open)}
- >
-
- }
- label="Activity"
- isActive={isActivity}
- onClick={() => {
- setActivityOpen(false);
- withTrack("activity", navigateToActivity)();
- }}
- badge={
-
- }
- />
- }
- />
- {!isActivity && activityOpen && (
- setActivityOpen(false)}
- />
- )}
-
+