From 37a31a0bf99f379f24fb4ba0bbd4e58b449dabca Mon Sep 17 00:00:00 2001 From: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com> Date: Fri, 27 Feb 2026 12:30:39 -0500 Subject: [PATCH] fix: search filter for agent component (#11939) * Fix search filter for agent component * refactor and add tests --- .../compute-section-visibility.test.ts | 201 ++++++++++++++++++ .../helpers/compute-section-visibility.ts | 48 +++++ .../components/flowSidebarComponent/index.tsx | 25 +-- 3 files changed, 260 insertions(+), 14 deletions(-) create mode 100644 src/frontend/src/pages/FlowPage/components/flowSidebarComponent/helpers/__tests__/compute-section-visibility.test.ts create mode 100644 src/frontend/src/pages/FlowPage/components/flowSidebarComponent/helpers/compute-section-visibility.ts diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/helpers/__tests__/compute-section-visibility.test.ts b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/helpers/__tests__/compute-section-visibility.test.ts new file mode 100644 index 0000000000..ca0b7272c5 --- /dev/null +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/helpers/__tests__/compute-section-visibility.test.ts @@ -0,0 +1,201 @@ +import { + computeSectionVisibility, + type SectionVisibilityInput, +} from "../compute-section-visibility"; + +describe("computeSectionVisibility", () => { + const baseInput: SectionVisibilityInput = { + enableNewSidebar: true, + activeSection: "components", + hasSearchInput: false, + hasCoreComponents: true, + hasMcpComponents: false, + hasBundleItems: false, + }; + + describe("Components tab (no search)", () => { + it("should show components when on components tab with core components", () => { + const result = computeSectionVisibility(baseInput); + + expect(result.showComponents).toBe(true); + expect(result.showMcp).toBe(false); + expect(result.showBundles).toBe(false); + expect(result.isMcpTabActive).toBe(false); + }); + + it("should not show components when on components tab without core components", () => { + const result = computeSectionVisibility({ + ...baseInput, + hasCoreComponents: false, + }); + + expect(result.showComponents).toBe(false); + }); + }); + + describe("MCP tab (no search)", () => { + it("should show MCP and hide components when on MCP tab", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "mcp", + }); + + expect(result.showMcp).toBe(true); + expect(result.isMcpTabActive).toBe(true); + }); + + it("should not show bundles when on MCP tab without search", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "mcp", + hasBundleItems: true, + }); + + expect(result.showBundles).toBe(false); + }); + }); + + describe("Bundles tab (no search)", () => { + it("should show bundles when on bundles tab with bundle items", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "bundles", + hasBundleItems: true, + }); + + expect(result.showBundles).toBe(true); + expect(result.showComponents).toBe(false); + expect(result.showMcp).toBe(false); + }); + }); + + describe("Search active", () => { + it("should show components during search when core components match", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "search", + hasSearchInput: true, + hasCoreComponents: true, + }); + + expect(result.showComponents).toBe(true); + expect(result.isMcpTabActive).toBe(false); + }); + + it("should show MCP during search when MCP components match", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "search", + hasSearchInput: true, + hasMcpComponents: true, + }); + + expect(result.showMcp).toBe(true); + expect(result.isMcpTabActive).toBe(false); + }); + + it("should show bundles during search when bundle items match", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "search", + hasSearchInput: true, + hasBundleItems: true, + }); + + expect(result.showBundles).toBe(true); + }); + + // Regression: searching for "Agent" with MCP servers configured should + // show both core components AND MCP results, not hide core components. + // See: https://github.com/langflow-ai/langflow/pull/11513 + it("should show BOTH components and MCP when search matches both", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "search", + hasSearchInput: true, + hasCoreComponents: true, + hasMcpComponents: true, + }); + + expect(result.showComponents).toBe(true); + expect(result.showMcp).toBe(true); + expect(result.isMcpTabActive).toBe(false); + }); + + it("should show all sections when search matches everything", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "search", + hasSearchInput: true, + hasCoreComponents: true, + hasMcpComponents: true, + hasBundleItems: true, + }); + + expect(result.showComponents).toBe(true); + expect(result.showMcp).toBe(true); + expect(result.showBundles).toBe(true); + expect(result.isMcpTabActive).toBe(false); + }); + + it("should not show components during search when no core components match", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "search", + hasSearchInput: true, + hasCoreComponents: false, + hasMcpComponents: true, + }); + + expect(result.showComponents).toBe(false); + expect(result.showMcp).toBe(true); + }); + }); + + describe("isMcpTabActive", () => { + it("should be true only when on MCP tab with new sidebar enabled", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "mcp", + }); + + expect(result.isMcpTabActive).toBe(true); + }); + + it("should be false when searching even if MCP has results", () => { + const result = computeSectionVisibility({ + ...baseInput, + activeSection: "search", + hasSearchInput: true, + hasMcpComponents: true, + }); + + expect(result.isMcpTabActive).toBe(false); + }); + + it("should be false when new sidebar is disabled", () => { + const result = computeSectionVisibility({ + ...baseInput, + enableNewSidebar: false, + activeSection: "mcp", + }); + + expect(result.isMcpTabActive).toBe(false); + }); + }); + + describe("Legacy sidebar (ENABLE_NEW_SIDEBAR=false)", () => { + it("should always show components and bundles", () => { + const result = computeSectionVisibility({ + ...baseInput, + enableNewSidebar: false, + hasCoreComponents: false, + hasBundleItems: false, + }); + + expect(result.showComponents).toBe(true); + expect(result.showBundles).toBe(true); + expect(result.showMcp).toBe(false); + }); + }); +}); diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/helpers/compute-section-visibility.ts b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/helpers/compute-section-visibility.ts new file mode 100644 index 0000000000..72afa2e9c7 --- /dev/null +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/helpers/compute-section-visibility.ts @@ -0,0 +1,48 @@ +export interface SectionVisibilityInput { + enableNewSidebar: boolean; + activeSection: string; + hasSearchInput: boolean; + hasCoreComponents: boolean; + hasMcpComponents: boolean; + hasBundleItems: boolean; +} + +export interface SectionVisibilityOutput { + showComponents: boolean; + showBundles: boolean; + showMcp: boolean; + isMcpTabActive: boolean; +} + +export function computeSectionVisibility( + input: SectionVisibilityInput, +): SectionVisibilityOutput { + const { + enableNewSidebar, + activeSection, + hasSearchInput, + hasCoreComponents, + hasMcpComponents, + hasBundleItems, + } = input; + + const showComponents = + (enableNewSidebar && + hasCoreComponents && + (activeSection === "components" || activeSection === "search")) || + (hasSearchInput && hasCoreComponents && enableNewSidebar) || + !enableNewSidebar; + + const showBundles = + (hasBundleItems && enableNewSidebar && activeSection === "bundles") || + (hasSearchInput && hasBundleItems && enableNewSidebar) || + !enableNewSidebar; + + const showMcp = + (enableNewSidebar && activeSection === "mcp") || + (hasSearchInput && hasMcpComponents && enableNewSidebar); + + const isMcpTabActive = enableNewSidebar && activeSection === "mcp"; + + return { showComponents, showBundles, showMcp, isMcpTabActive }; +} diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx index 3390bdbb0b..320fd16a74 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx @@ -51,6 +51,7 @@ import { applyComponentFilter } from "./helpers/apply-component-filter"; import { applyEdgeFilter } from "./helpers/apply-edge-filter"; import { applyLegacyFilter } from "./helpers/apply-legacy-filter"; import { combinedResultsFn } from "./helpers/combined-results"; +import { computeSectionVisibility } from "./helpers/compute-section-visibility"; import { filteredDataFn } from "./helpers/filtered-data"; import { normalizeString } from "./helpers/normalize-string"; import sensitiveSort from "./helpers/sensitive-sort"; @@ -582,19 +583,15 @@ export function FlowSidebarComponent({ isLoading }: FlowSidebarComponentProps) { filterType !== undefined || getFilterComponent !== ""; - const showComponents = - (ENABLE_NEW_SIDEBAR && - hasCoreComponents && - (activeSection === "components" || activeSection === "search")) || - (hasSearchInput && hasCoreComponents && ENABLE_NEW_SIDEBAR) || - !ENABLE_NEW_SIDEBAR; - const showBundles = - (hasBundleItems && ENABLE_NEW_SIDEBAR && activeSection === "bundles") || - (hasSearchInput && hasBundleItems && ENABLE_NEW_SIDEBAR) || - !ENABLE_NEW_SIDEBAR; - const showMcp = - (ENABLE_NEW_SIDEBAR && activeSection === "mcp") || - (hasSearchInput && hasMcpComponents && ENABLE_NEW_SIDEBAR); + const { showComponents, showBundles, showMcp, isMcpTabActive } = + computeSectionVisibility({ + enableNewSidebar: ENABLE_NEW_SIDEBAR, + activeSection, + hasSearchInput, + hasCoreComponents, + hasMcpComponents, + hasBundleItems, + }); const [category, component] = getFilterComponent?.split(".") ?? ["", ""]; @@ -668,7 +665,7 @@ export function FlowSidebarComponent({ isLoading }: FlowSidebarComponentProps) { <> {hasResults ? ( <> - {showComponents && !showMcp && ( + {showComponents && !isMcpTabActive && (