diff --git a/designer/client/cypress/e2e/search.cy.ts b/designer/client/cypress/e2e/search.cy.ts index 2ecc7f16e17..b454ead3942 100644 --- a/designer/client/cypress/e2e/search.cy.ts +++ b/designer/client/cypress/e2e/search.cy.ts @@ -46,7 +46,7 @@ describe("Search Panel View", () => { cy.get("[data-testid=search-panel]").find("input[name='type']").should("have.value", "sink"); cy.get("[data-testid=search-panel]").find("input[name='type']").click(); - cy.realType(",processor"); + cy.realType(",dynamicService"); cy.get("[data-testid=search-panel]").find("button[type='submit']").click(); @@ -61,13 +61,13 @@ describe("Search Panel View", () => { cy.get("[data-testid=search-panel]").find("svg[id='advanced-search-icon']").click(); cy.get("[data-testid=search-panel]").find("input[name='type']").click(); - cy.realType("sink,processor"); + cy.realType("sink,dynamicSe"); cy.get("[data-testid=search-panel]").find("button[type='submit']").click(); cy.get("[data-testid=search-panel]") .find("input[data-selector='NODES_IN_SCENARIO']") - .should("have.value", "type:(sink,processor) se"); + .should("have.value", "type:(sink,dynamicSe) se"); cy.get("[data-testid=search-panel]").contains("dynamicService"); cy.get("[data-testid=search-panel]").contains("sendSms"); @@ -76,7 +76,7 @@ describe("Search Panel View", () => { it("should filter nodes when setting up multiple selectors using form", () => { cy.get("[data-testid=search-panel]").find("svg[id='advanced-search-icon']").click(); - cy.get("[data-testid=search-panel]").find("input[name='id']").click(); + cy.get("[data-testid=search-panel]").find("input[name='name']").click(); cy.realType("bounded,dynamic,send,enricher"); cy.get("[data-testid=search-panel]").find("input[name='type']").click(); @@ -86,7 +86,7 @@ describe("Search Panel View", () => { cy.get("[data-testid=search-panel]") .find("input[data-selector='NODES_IN_SCENARIO']") - .should("have.value", "id:(bounded,dynamic,send,enricher) type:(sink,enricher) "); + .should("have.value", "name:(bounded,dynamic,send,enricher) type:(sink,enricher)"); cy.get("[data-testid=search-panel]").contains("enricher"); cy.get("[data-testid=search-panel]").contains("sendSms"); diff --git a/designer/client/src/components/sidePanels/SearchLabeledAutocomplete.tsx b/designer/client/src/components/sidePanels/SearchLabeledAutocomplete.tsx new file mode 100644 index 00000000000..4bd32b9e85f --- /dev/null +++ b/designer/client/src/components/sidePanels/SearchLabeledAutocomplete.tsx @@ -0,0 +1,30 @@ +import { Autocomplete, FormControl } from "@mui/material"; +import { nodeInput } from "../graph/node-modal/NodeDetailsContent/NodeTableStyled"; +import React from "react"; + +export const SearchLabeledAutocomplete = ({ children, name, options, value, setFilterFields }) => { + function handleChange(_, value) { + setFilterFields((prev) => ({ ...prev, [name]: [value] })); + } + + return ( + + {children} + ( +
+ +
+ )} + /> +
+ ); +}; + +SearchLabeledAutocomplete.displayName = "SearchLabeledAutocomplete"; diff --git a/designer/client/src/components/sidePanels/SearchLabeledInput.tsx b/designer/client/src/components/sidePanels/SearchLabeledInput.tsx index 23e5354edd9..96acec5c5b5 100644 --- a/designer/client/src/components/sidePanels/SearchLabeledInput.tsx +++ b/designer/client/src/components/sidePanels/SearchLabeledInput.tsx @@ -1,19 +1,18 @@ -import { forwardRef, PropsWithChildren } from "react"; +import React from "react"; import { FormControl } from "@mui/material"; import { nodeInput } from "../graph/node-modal/NodeDetailsContent/NodeTableStyled"; -import React from "react"; -export type SearchLabeledInputProps = PropsWithChildren<{ - name: string; -}>; +export const SearchLabeledInput = ({ children, name, value, setFilterFields }) => { + function handleChange(event) { + setFilterFields((prev) => ({ ...prev, [name]: event.target.value.split(",") })); + } -export const SearchLabeledInput = forwardRef(({ children, ...props }, ref) => { return ( {children} - + ); -}); +}; SearchLabeledInput.displayName = "SearchLabeledInput"; diff --git a/designer/client/src/components/toolbars/search/AdvancedSearchFilters.tsx b/designer/client/src/components/toolbars/search/AdvancedSearchFilters.tsx index 51f0f9bf713..a939285597c 100644 --- a/designer/client/src/components/toolbars/search/AdvancedSearchFilters.tsx +++ b/designer/client/src/components/toolbars/search/AdvancedSearchFilters.tsx @@ -1,99 +1,58 @@ -import React, { MutableRefObject, useEffect, useMemo, useRef, useState } from "react"; +import React, { useEffect, useMemo } from "react"; import { Button, Box, Typography } from "@mui/material"; import { useTranslation } from "react-i18next"; import { SearchLabeledInput } from "../../sidePanels/SearchLabeledInput"; import { SearchLabel } from "../../sidePanels/SearchLabel"; +import { resolveSearchQuery, searchQueryToString, useNodeTypes } from "./utils"; import { SearchQuery } from "./SearchResults"; -import { resolveSearchQuery } from "./utils"; - -const transformInput = (input: string, fieldName: string) => { - return input === "" ? "" : `${fieldName}:(${input})`; -}; - -function extractSimpleSearchQuery(text: string): string { - const regex = /(\w+):\(([^)]*)\)/g; - let match: RegExpExecArray | null; - let lastIndex = 0; - - while ((match = regex.exec(text)) !== null) { - lastIndex = regex.lastIndex; - } - - const rest = text.slice(lastIndex).trim(); - - return rest; -} +import { SearchLabeledAutocomplete } from "../../sidePanels/SearchLabeledAutocomplete"; export function AdvancedSearchFilters({ + filterFields, + setFilterFields, filter, setFilter, setCollapsedHandler, - refForm, }: { + filterFields: SearchQuery; + setFilterFields: React.Dispatch>; filter: string; setFilter: React.Dispatch>; setCollapsedHandler: React.Dispatch>; - refForm: MutableRefObject; }) { const { t } = useTranslation(); - //const refForm = useRef(null); const displayNames = useMemo( () => ({ - id: t("panels.search.field.id", "Name"), + name: t("panels.search.field.id", "Name"), description: t("panels.search.field.description", "Description"), type: t("panels.search.field.type", "Type"), - paramName: t("panels.search.field.paramName", "Label"), - paramValue: t("panels.search.field.paramValue", "Value"), - outputValue: t("panels.search.field.outputValue", "Output"), - edgeExpression: t("panels.search.field.edgeExpression", "Edge"), + label: t("panels.search.field.paramName", "Label"), + value: t("panels.search.field.paramValue", "Value"), + output: t("panels.search.field.outputValue", "Output"), + edge: t("panels.search.field.edgeExpression", "Edge"), }), [t], ); - //Here be dragons: direct DOM manipulation useEffect(() => { - if (refForm.current) { - const searchQuery = resolveSearchQuery(filter); - const formElements = refForm.current.elements; - - Array.from(formElements).forEach((element: HTMLInputElement) => { - if (element.name in searchQuery) { - element.value = (searchQuery[element.name] || []).join(","); - } else { - element.value = ""; - } - }); - } + const searchQuery = resolveSearchQuery(filter); + setFilterFields(searchQuery); }, [filter]); const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); - const formData = new FormData(event.currentTarget); - - const transformedInputs = Array.from(formData.entries()) - .map(([fieldName, fieldValue]) => { - const input = (fieldValue as string) || ""; - return transformInput(input, fieldName); - }) - .filter((input) => input !== ""); - - const finalText = transformedInputs.join(" ").trim() + " " + extractSimpleSearchQuery(filter); - - setFilter(finalText); + setFilter(searchQueryToString(filterFields)); setCollapsedHandler(false); }; const handleClear = () => { - setFilter(extractSimpleSearchQuery(filter)); - - refForm.current.reset(); + setFilter(filterFields?.plainQuery); }; return ( {t("search.panel.advancedFilters.label", "Advanced Search")} - - - - - + + - - + + - - + + + + + - - + + - - + + - - + +