From c521ae44030396172bdd647681def4c18fff2528 Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Sat, 11 Apr 2026 11:17:09 +0000 Subject: [PATCH] feat(nexus): add TopStrip composite for layout overhaul (phase 8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 48px sticky top strip per docs/specs/2026-04-11-nexus-layout-overhaul.md §4.2. Composes ModeBreadcrumb (left) + CmdKButton and GlobalMicButton (right) inside a
landmark. Completes the frame component set for Phase 8. The next task (task 6) rewrites Layout.tsx to mount IconRail + TopStrip as the new global chrome and delete the old sidebar/ChatPanel/PropertiesPanel/ BreadcrumbBar combination. Part of Phase 8 of the Nexus layout overhaul (task 5 of 7). Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/src/components/frame/TopStrip.test.tsx | 76 +++++++++++++++++++++++ ui/src/components/frame/TopStrip.tsx | 26 ++++++++ 2 files changed, 102 insertions(+) create mode 100644 ui/src/components/frame/TopStrip.test.tsx create mode 100644 ui/src/components/frame/TopStrip.tsx diff --git a/ui/src/components/frame/TopStrip.test.tsx b/ui/src/components/frame/TopStrip.test.tsx new file mode 100644 index 00000000..3c6d06dd --- /dev/null +++ b/ui/src/components/frame/TopStrip.test.tsx @@ -0,0 +1,76 @@ +// @vitest-environment jsdom + +import { act } from "react"; +import { createRoot } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { MemoryRouter } from "@/lib/router"; +import { TopStrip } from "./TopStrip"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +// Same stub as IconRail / ModeBreadcrumb tests — @/lib/router's Link +// (and useLocation consumers) trigger CompanyContext resolution. +vi.mock("@/context/CompanyContext", () => ({ + useCompany: () => ({ + companies: [], + selectedCompany: null, + selectedCompanyId: null, + setSelectedCompanyId: () => {}, + selectionSource: null, + loading: false, + }), +})); + +describe("TopStrip", () => { + let container: HTMLDivElement; + let root: ReturnType | null = null; + + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + root = null; + }); + + afterEach(() => { + if (root) { + act(() => { root!.unmount(); }); + root = null; + } + if (container.parentNode) container.remove(); + }); + + function render(pathname: string) { + root = createRoot(container); + act(() => { + root!.render( + + + + ); + }); + } + + it("renders the ModeBreadcrumb with derived segments", () => { + render("/NEX/assistant"); + const segment = container.querySelector("[data-testid='mode-breadcrumb-segment']"); + expect(segment?.textContent?.trim()).toBe("ASSISTANT"); + }); + + it("renders the CmdK button", () => { + render("/NEX/assistant"); + expect(container.querySelector("button[aria-label='Open command palette']")).not.toBeNull(); + }); + + it("renders the global mic button", () => { + render("/NEX/assistant"); + expect(container.querySelector("button[aria-label='Voice']")).not.toBeNull(); + }); + + it("is wrapped in a header element for landmark semantics", () => { + render("/NEX/assistant"); + const header = container.querySelector("header"); + expect(header).not.toBeNull(); + expect(header?.getAttribute("aria-label")).toBe("Top bar"); + }); +}); diff --git a/ui/src/components/frame/TopStrip.tsx b/ui/src/components/frame/TopStrip.tsx new file mode 100644 index 00000000..3ebe5193 --- /dev/null +++ b/ui/src/components/frame/TopStrip.tsx @@ -0,0 +1,26 @@ +import { CmdKButton } from "./CmdKButton"; +import { GlobalMicButton } from "./GlobalMicButton"; +import { ModeBreadcrumb } from "./ModeBreadcrumb"; + +/** + * TopStrip — the 48px top bar composed into the global frame in Layout.tsx + * (Task 6). Renders the mode breadcrumb on the left and the ⌘K + global + * mic buttons on the right. Charcoal bottom border, pure black background, + * sticky at the top of the main column. + * + * Per docs/specs/2026-04-11-nexus-layout-overhaul.md §4.2. + */ +export function TopStrip() { + return ( +
+ +
+ + +
+
+ ); +}