diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/index.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/index.tsx index 485ad8b83c..4c4294534e 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/index.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Sidebar/Search/index.tsx @@ -11,14 +11,13 @@ import { debounce } from "lodash"; import { useStore } from "pages/FlowEditor/lib/store"; import React, { useEffect, useMemo, useState } from "react"; import { Components, Virtuoso } from "react-virtuoso"; +import { SEARCH_DEBOUNCE_MS } from "ui/shared/constants"; import { ExternalPortalList } from "./ExternalPortalList/ExternalPortalList"; import { ALL_FACETS, SearchFacets } from "./facets"; import { SearchHeader } from "./SearchHeader"; import { SearchResultCard } from "./SearchResultCard"; -const DEBOUNCE_MS = 500; - interface SearchNodes { pattern: string; facets: SearchFacets; @@ -87,8 +86,8 @@ const Search: React.FC = () => { search(pattern); setLastPattern(pattern); setIsSearching(false); - }, DEBOUNCE_MS), - [search] + }, SEARCH_DEBOUNCE_MS), + [search], ); return ( diff --git a/editor.planx.uk/src/pages/Team.tsx b/editor.planx.uk/src/pages/Team.tsx index 92efd3380d..070cc4c289 100644 --- a/editor.planx.uk/src/pages/Team.tsx +++ b/editor.planx.uk/src/pages/Team.tsx @@ -9,6 +9,7 @@ import MenuItem from "@mui/material/MenuItem"; import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; import { hasFeatureFlag } from "lib/featureFlags"; +import { isEmpty } from "lodash"; import React, { useCallback, useEffect, useState } from "react"; import { Link, useNavigation } from "react-navi"; import { borderedFocusStyle, FONT_WEIGHT_SEMI_BOLD } from "theme"; @@ -16,6 +17,7 @@ import { AddButton } from "ui/editor/AddButton"; import SelectInput from "ui/editor/SelectInput/SelectInput"; import { SortableFields, SortControl } from "ui/editor/SortControl"; import InputLabel from "ui/public/InputLabel"; +import { SearchBox } from "ui/shared/SearchBox/SearchBox"; import { slugify } from "utils"; import FlowCard, { Card, CardContent } from "./FlowCard"; @@ -37,7 +39,7 @@ const DashboardList = styled("ul")(({ theme }) => ({ }, })); -const GetStarted: React.FC<{ flows: FlowSummary[] }> = ({ flows }) => ( +const GetStarted: React.FC<{ flows: FlowSummary[] | null }> = ({ flows }) => ( @@ -153,6 +155,9 @@ const Team: React.FC = () => { (state) => [state.getTeam(), state.canUserEditTeam, state.getFlows], ); const [flows, setFlows] = useState(null); + const [filteredFlows, setFilteredFlows] = useState( + null, + ); const sortOptions: SortableFields[] = [ { @@ -180,6 +185,7 @@ const Team: React.FC = () => { ), ); setFlows(sortedFlows); + setFilteredFlows(sortedFlows); }); }, [teamId, setFlows, getFlows]); @@ -187,7 +193,7 @@ const Team: React.FC = () => { fetchFlows(); }, [fetchFlows]); - const teamHasFlows = flows && Boolean(flows.length); + const teamHasFlows = !isEmpty(filteredFlows) && !isEmpty(flows); const showAddFlowButton = teamHasFlows && canUserEditTeam(slug); return ( @@ -216,25 +222,13 @@ const Team: React.FC = () => { {showAddFlowButton && } - - - - Search - - - - theme.palette.border.input, - pr: 5, - }} - name="search" - id="search" - /> - - - - + {hasFeatureFlag("SORT_FLOWS") && flows && ( + + records={flows} + setRecords={setFilteredFlows} + searchKey={["name", "slug"]} + /> + )} { + records: T[] | null; + setRecords: React.Dispatch>; + searchKey: FuseOptionKey[]; +} + +export const SearchBox = ({ + records, + setRecords, + searchKey, +}: SearchBoxProps) => { + const [isSearching, setIsSearching] = useState(false); + const [searchedTerm, setSearchedTerm] = useState(); + + const searchKeys = useMemo(() => searchKey, []); + + const formik = useFormik({ + initialValues: { pattern: "" }, + onSubmit: ({ pattern }) => { + setIsSearching(true); + debouncedSearch(pattern); + }, + }); + + const { results, search } = useSearch({ + list: records || [], + keys: searchKeys, + }); + + const debouncedSearch = useMemo( + () => + debounce((pattern: string) => { + search(pattern); + setSearchedTerm(pattern); + setIsSearching(false); + }, SEARCH_DEBOUNCE_MS), + [search], + ); + + useEffect(() => { + if (results && searchedTerm) { + const mappedResults = results.map((result) => result.item); + setRecords(mappedResults); + } + if (results && !searchedTerm) { + records && setRecords(records); + } + }, [results, setRecords, searchedTerm, records]); + + return ( + + + + Search + + + + { + formik.setFieldValue("pattern", e.target.value); + formik.submitForm(); + }} + /> + {searchedTerm && !isSearching && ( + { + formik.setFieldValue("pattern", ""); + formik.submitForm(); + }} + size="small" + sx={{ + position: "absolute", + right: (theme) => theme.spacing(1), + top: "50%", + transform: "translateY(-50%)", + padding: 0.5, + zIndex: 1, + }} + > + + + )} + {isSearching && ( + { + formik.setFieldValue("pattern", ""); + formik.submitForm(); + }} + size="small" + sx={{ + position: "absolute", + right: (theme) => theme.spacing(1), + top: "50%", + transform: "translateY(-50%)", + padding: 0.5, + zIndex: 1, + }} + > + + + )} + + + + + ); +}; diff --git a/editor.planx.uk/src/ui/shared/constants.ts b/editor.planx.uk/src/ui/shared/constants.ts new file mode 100644 index 0000000000..c87171b084 --- /dev/null +++ b/editor.planx.uk/src/ui/shared/constants.ts @@ -0,0 +1 @@ +export const SEARCH_DEBOUNCE_MS = 500;