From 1c6fe544e27abc3b875a8d1f133ffd7b590645ac Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 21 Jun 2026 11:35:17 -0700 Subject: [PATCH] FN-6846: move header search after workflow slot Move the desktop and tablet search toggle to follow the workflow portal slot.\n\n- Reorder the non-mobile header search button after the workflow portal mount point.\n- Preserve the mobile search trigger position and behavior.\n- Add header tests covering empty, populated, tablet, and mobile slot behavior.\n\nFiles changed:\n packages/dashboard/app/components/Header.tsx | 29 ++++++++-------\n .../app/components/__tests__/Header.test.tsx | 42 ++++++++++++++++++++--\n 2 files changed, 56 insertions(+), 15 deletions(-) Fusion-Task-Id: FN-6846 Fusion-Task-Lineage: 294100b1-76ae-4c46-b4b3-e4c83e4a55f8 --- packages/dashboard/app/components/Header.tsx | 29 +++++++------ .../app/components/__tests__/Header.test.tsx | 42 ++++++++++++++++++- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/packages/dashboard/app/components/Header.tsx b/packages/dashboard/app/components/Header.tsx index d94315375a..fe3f76ec4c 100644 --- a/packages/dashboard/app/components/Header.tsx +++ b/packages/dashboard/app/components/Header.tsx @@ -928,19 +928,6 @@ export function Header({ )} - {/* Desktop/Tablet Search Toggle - show icon when search is available but hidden */} - {canShowNonMobileSearchToggle && ( - - )} - {/* Usage button on mobile when mobile bottom nav is active */} {isMobile && hideFullNav && onOpenUsage && ( + )} + {/* View Toggle - always inline, even on mobile */} {!hideFullNav && !hideHeaderViewNav && onChangeView && (
diff --git a/packages/dashboard/app/components/__tests__/Header.test.tsx b/packages/dashboard/app/components/__tests__/Header.test.tsx index 55ba44aca3..844a3a1ba3 100644 --- a/packages/dashboard/app/components/__tests__/Header.test.tsx +++ b/packages/dashboard/app/components/__tests__/Header.test.tsx @@ -1076,6 +1076,39 @@ describe("Header", () => { expect(screen.getByTestId("desktop-header-search-btn")).toBeDefined(); }); + it("renders the desktop search toggle after the empty workflow portal slot", () => { + renderHeader({ onSearchChange: vi.fn(), onChangeView: noop, view: "board", leftSidebarNavActive: true }, "desktop"); + const workflowSlot = screen.getByTestId("header-workflow-slot"); + const searchToggle = screen.getByTestId("desktop-header-search-btn"); + + expect(screen.getAllByTestId("desktop-header-search-btn")).toHaveLength(1); + expect(workflowSlot.compareDocumentPosition(searchToggle) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + }); + + it("keeps the desktop search toggle after a populated workflow portal slot", () => { + renderHeader({ onSearchChange: vi.fn(), onChangeView: noop, view: "board", leftSidebarNavActive: true }, "desktop"); + const workflowSlot = screen.getByTestId("header-workflow-slot"); + const workflowSwitcher = document.createElement("button"); + workflowSwitcher.type = "button"; + workflowSwitcher.dataset.testid = "mock-workflow-switcher"; + workflowSwitcher.textContent = "Coding workflow"; + workflowSlot.appendChild(workflowSwitcher); + const searchToggle = screen.getByTestId("desktop-header-search-btn"); + + expect(screen.getAllByTestId("desktop-header-search-btn")).toHaveLength(1); + expect(workflowSlot.compareDocumentPosition(searchToggle) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + expect(workflowSwitcher.compareDocumentPosition(searchToggle) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + }); + + it("keeps the tablet search toggle after the workflow portal slot", () => { + renderHeader({ onSearchChange: vi.fn(), onChangeView: noop, view: "board", leftSidebarNavActive: true }, "tablet"); + const workflowSlot = screen.getByTestId("header-workflow-slot"); + const searchToggle = screen.getByTestId("desktop-header-search-btn"); + + expect(screen.getAllByTestId("desktop-header-search-btn")).toHaveLength(1); + expect(workflowSlot.compareDocumentPosition(searchToggle) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); + }); + it("does not render search toggle when view is 'agents'", () => { renderHeader({ onSearchChange: vi.fn(), view: "agents" }); expect(screen.queryByTestId("desktop-header-search-btn")).toBeNull(); @@ -1371,9 +1404,14 @@ describe("Header", () => { it("can open mobile search when mobileNavEnabled is true", () => { renderHeader({ view: "board", searchQuery: "", onSearchChange: vi.fn(), onChangeView: noop }, "mobile"); // Should show the trigger button - expect(screen.getByTestId("mobile-header-search-btn")).toBeDefined(); - // Expanded search should not be visible initially + const mobileSearchTrigger = screen.getByTestId("mobile-header-search-btn"); + expect(mobileSearchTrigger).toBeDefined(); + expect(screen.queryByTestId("header-workflow-slot")).toBeNull(); + expect(screen.queryByTestId("desktop-header-search-btn")).toBeNull(); + // Expanded search should not be visible initially, then opens from the unchanged mobile trigger. expect(screen.queryByPlaceholderText("Search tasks...")).toBeNull(); + fireEvent.click(mobileSearchTrigger); + expect(screen.getByPlaceholderText("Search tasks...")).toBeDefined(); }); it("closes mobile search and clears query when close button clicked with mobileNavEnabled", () => {