diff --git a/ee/apps/den-web/app/(den)/dashboard/_components/org-dashboard-shell.tsx b/ee/apps/den-web/app/(den)/dashboard/_components/org-dashboard-shell.tsx index f8a389df38..b84aa3250a 100644 --- a/ee/apps/den-web/app/(den)/dashboard/_components/org-dashboard-shell.tsx +++ b/ee/apps/den-web/app/(den)/dashboard/_components/org-dashboard-shell.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import { usePathname } from "next/navigation"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { BarChart3, ChevronDown, @@ -321,6 +321,7 @@ export function OrgDashboardShell({ children }: { children: React.ReactNode }) { const [switcherOpen, setSwitcherOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false); const [profilePromptDismissed, setProfilePromptDismissed] = useState(false); + const switcherTriggerRef = useRef(null); const isSingleOrgMode = runtimeConfigLoaded && runtimeConfig.orgMode === "single_org"; const { query: switcherQuery, @@ -333,6 +334,32 @@ export function OrgDashboardShell({ children }: { children: React.ReactNode }) { showSearch: showSwitcherSearch, } = useOrgListWindow(orgDirectory, 20); + useEffect(() => { + if (!switcherOpen) return; + + function handlePointerDown(event: PointerEvent) { + const clickedInsideSwitcher = event.composedPath().some( + (target) => target instanceof Element && target.hasAttribute("data-workspace-switcher-root"), + ); + if (!clickedInsideSwitcher) { + setSwitcherOpen(false); + } + } + + function handleKeyDown(event: KeyboardEvent) { + if (event.key !== "Escape") return; + setSwitcherOpen(false); + switcherTriggerRef.current?.focus(); + } + + document.addEventListener("pointerdown", handlePointerDown); + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("pointerdown", handlePointerDown); + document.removeEventListener("keydown", handleKeyDown); + }; + }, [switcherOpen]); + if (orgSelectionRequired) { return ( ) : ( -
+
{switcherOpen ? ( -
-
+
+

{user?.email ?? "OpenWork user"}

diff --git a/ee/apps/den-web/tests/org-dashboard-shell-layout.test.ts b/ee/apps/den-web/tests/org-dashboard-shell-layout.test.ts index cb3c4e6e2b..eb265e01dd 100644 --- a/ee/apps/den-web/tests/org-dashboard-shell-layout.test.ts +++ b/ee/apps/den-web/tests/org-dashboard-shell-layout.test.ts @@ -13,4 +13,16 @@ describe("OrgDashboardShell layout", () => { expect(source).toMatch(/
/); expect(source).toMatch(/
/); }); + + test("bounds the workspace switcher and dismisses only outside pointer input", () => { + const source = readFileSync(shellPath, "utf8"); + + expect(source).toContain("grid-cols-[minmax(0,1fr)]"); + expect(source).toContain("max-w-[calc(100vw-1.5rem)]"); + expect(source).toContain('document.addEventListener("pointerdown", handlePointerDown)'); + expect(source).toContain('data-workspace-switcher-root=""'); + expect(source).toContain("event.composedPath().some("); + expect(source).toContain('target.hasAttribute("data-workspace-switcher-root")'); + expect(source).toContain("if (!clickedInsideSwitcher)"); + }); });