From 98704282ac1a5d89b6d823aa75db084e9f3e8f5f Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 1 Jul 2026 23:58:55 -0700 Subject: [PATCH] FN-7409: Restore board search reopen button Restore the board/list search affordance after users dismiss an active desktop search. - Keep the non-mobile open-search button available once a closed search has an empty query. - Cover empty-close and parent-cleared populated search flows in Header tests. - Add a patch changeset for the published Fusion package. Files changed: .changeset/restore-board-search-trigger.md | 7 ++++ packages/dashboard/app/components/Header.tsx | 13 +++++--- .../app/components/__tests__/Header.test.tsx | 38 ++++++++++++++++++---- 3 files changed, 46 insertions(+), 12 deletions(-) Fusion-Task-Id: FN-7409 Fusion-Task-Lineage: 9cb4ff80-8490-43be-8d18-a3f292603ecb Co-authored-by: Fusion (runfusion.ai) --- .changeset/restore-board-search-trigger.md | 7 ++++ packages/dashboard/app/components/Header.tsx | 13 ++++--- .../app/components/__tests__/Header.test.tsx | 38 +++++++++++++++---- 3 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 .changeset/restore-board-search-trigger.md diff --git a/.changeset/restore-board-search-trigger.md b/.changeset/restore-board-search-trigger.md new file mode 100644 index 0000000000..f10a105d3f --- /dev/null +++ b/.changeset/restore-board-search-trigger.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Restore the board search button after closing the search panel. +category: fix +dev: Keeps the desktop/tablet Header search reopen affordance visible after empty-query dismissal and parent clear. diff --git a/packages/dashboard/app/components/Header.tsx b/packages/dashboard/app/components/Header.tsx index a15231ece4..8db2fb5fe7 100644 --- a/packages/dashboard/app/components/Header.tsx +++ b/packages/dashboard/app/components/Header.tsx @@ -230,14 +230,17 @@ export function Header({ // Keep mobile search open if there's an active search query const shouldShowMobileSearch = isMobileSearchOpen || searchQuery.length > 0; - // Non-mobile search: toggled open OR has active query, but not if explicitly closed - const shouldShowNonMobileSearch = (isNonMobileSearchOpen || searchQuery.length > 0) && !isNonMobileSearchExplicitlyClosed; - // Show toggle when search is available, NOT currently shown, NOT explicitly closed, AND query is empty - const canShowNonMobileSearchToggle = (view === "board" || view === "list") && !isMobile && onSearchChange && !isNonMobileSearchExplicitlyClosed && searchQuery.length === 0; const canShowNonMobileSearch = (view === "board" || view === "list") && !isMobile && onSearchChange; + // Non-mobile search: toggled open OR has active query, but not if explicitly closed. + const shouldShowNonMobileSearch = (isNonMobileSearchOpen || searchQuery.length > 0) && !isNonMobileSearchExplicitlyClosed; + /* + FNXC:BoardSearch 2026-07-01-23:38: + Closing board/list search must suppress the populated floating panel until App clears searchQuery, then immediately restore the Open search affordance. Keep the explicit-close state out of the empty-query toggle gate so an open-but-empty dismissal cannot strand the header without a search trigger. + */ + const canShowNonMobileSearchToggle = Boolean(canShowNonMobileSearch && !shouldShowNonMobileSearch && searchQuery.length === 0); const showBoardBranchFilters = view === "board"; - // Reset explicit close flag when query becomes empty (so toggle reappears) + // Reset explicit close flag when query becomes empty (so active-query reopen behavior is ready for the next search). useEffect(() => { if (searchQuery === "") { setIsNonMobileSearchExplicitlyClosed(false); diff --git a/packages/dashboard/app/components/__tests__/Header.test.tsx b/packages/dashboard/app/components/__tests__/Header.test.tsx index 42d90e39da..ae41653c31 100644 --- a/packages/dashboard/app/components/__tests__/Header.test.tsx +++ b/packages/dashboard/app/components/__tests__/Header.test.tsx @@ -875,6 +875,7 @@ describe("Header", () => { expect(screen.getByPlaceholderText("Search tasks...")).toBeDefined(); fireEvent.click(screen.getByLabelText("Close search")); expect(screen.queryByPlaceholderText("Search tasks...")).toBeNull(); + expect(screen.getAllByTestId("desktop-header-search-btn")).toHaveLength(1); }); it("clears search query when close button is clicked", () => { @@ -885,6 +886,33 @@ describe("Header", () => { expect(onSearchChange).toHaveBeenCalledWith(""); }); + it("restores the board search open button after closing a populated query and parent clear", () => { + const onSearchChange = vi.fn(); + const { rerender } = renderHeader({ onSearchChange, view: "board", searchQuery: "blocked" }); + + expect(screen.getByDisplayValue("blocked")).toBeInTheDocument(); + expect(screen.queryByTestId("desktop-header-search-btn")).toBeNull(); + + fireEvent.click(screen.getByLabelText("Close search")); + expect(onSearchChange).toHaveBeenCalledWith(""); + expect(screen.queryByPlaceholderText("Search tasks...")).toBeNull(); + + mockMatchMedia("desktop"); + rerender( +
+ ); + + expect(screen.queryByPlaceholderText("Search tasks...")).toBeNull(); + expect(screen.getAllByRole("button", { name: "Open search" })).toHaveLength(1); + expect(screen.getAllByTestId("desktop-header-search-btn")).toHaveLength(1); + }); + it("keeps search open when searchQuery is non-empty", () => { renderHeader({ onSearchChange: vi.fn(), view: "board", searchQuery: "test" }); expect(screen.getByPlaceholderText("Search tasks...")).toBeDefined(); @@ -933,18 +961,14 @@ describe("Header", () => { expect(wrapper!.querySelector("header.header")).not.toBeNull(); }); - it("toggling search twice reopens the search (use close button to dismiss)", () => { + it("hides the open toggle while search is open and restores it after close", () => { renderHeader({ onSearchChange: vi.fn(), view: "board" }); fireEvent.click(screen.getByTestId("desktop-header-search-btn")); expect(screen.getByPlaceholderText("Search tasks...")).toBeDefined(); - // Second toggle click reopens search since first close was via toggle - // (toggle always opens, use close button to dismiss) - fireEvent.click(screen.getByTestId("desktop-header-search-btn")); - // Search stays open because toggle only opens - expect(screen.getByPlaceholderText("Search tasks...")).toBeDefined(); - // Use close button to dismiss + expect(screen.queryByTestId("desktop-header-search-btn")).toBeNull(); fireEvent.click(screen.getByLabelText("Close search")); expect(screen.queryByPlaceholderText("Search tasks...")).toBeNull(); + expect(screen.getAllByTestId("desktop-header-search-btn")).toHaveLength(1); }); it("supports search toggle flow on list view", () => {