From 1624fd218c05772a066231b9b431c3662bc020fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20Oksi=C5=84ski?= Date: Wed, 13 Nov 2024 11:30:15 +0100 Subject: [PATCH] [MS-840] search ux --- .../src/components/header/search-form.tsx | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/apps/storefront/src/components/header/search-form.tsx b/apps/storefront/src/components/header/search-form.tsx index bda31c8..e814711 100644 --- a/apps/storefront/src/components/header/search-form.tsx +++ b/apps/storefront/src/components/header/search-form.tsx @@ -2,6 +2,7 @@ import { useDebounce } from "@uidotdev/usehooks"; import { Search } from "lucide-react"; +import { useSearchParams } from "next/navigation"; import { useTranslations } from "next-intl"; import { type KeyboardEventHandler, @@ -38,20 +39,20 @@ type SearchState = { status: "LOADING" | "IDLE"; }; -const minLetters = 3; +const minLetters = 1; const maxSearchSuggestions = 10; const keyboardCodes = { ArrowDown: "ArrowDown", ArrowUp: "ArrowUp", Enter: "Enter", }; -const initialSearchState: SearchState = { +const initialSearchState = (searchQuery?: string | null): SearchState => ({ highlightedOptionIndex: -1, - inputValue: "", + inputValue: searchQuery ?? "", options: [], status: "IDLE", showOptions: false, -}; +}); export const SearchForm = ({ onSubmit }: { onSubmit?: () => void }) => { const ts = useTranslations("search"); @@ -60,15 +61,21 @@ export const SearchForm = ({ onSubmit }: { onSubmit?: () => void }) => { const pathname = usePathname(); const router = useRouter(); + + const searchParams = useSearchParams(); + const searchQuery = searchParams.get("q"); + const [ { inputValue, options, highlightedOptionIndex, showOptions, status }, setSearchState, - ] = useState(initialSearchState); + ] = useState(initialSearchState(searchQuery)); const isLoading = status === "LOADING"; const isIdle = status === "IDLE"; const isNoOptionHighlighted = highlightedOptionIndex === -1; const isLastOptionHighlighted = highlightedOptionIndex === options.length; + const isSearchPage = pathname === paths.search.asPath(); + const isHomePage = pathname === paths.home.asPath(); const debouncedInputValue = useDebounce( inputValue, @@ -76,14 +83,14 @@ export const SearchForm = ({ onSubmit }: { onSubmit?: () => void }) => { ); const resetSearchState = useCallback( - () => setSearchState(initialSearchState), + () => setSearchState(initialSearchState()), [], ); const handleKeyDown: KeyboardEventHandler = (event) => { if (event.code === keyboardCodes.Enter) { event.preventDefault(); - resetSearchState(); + if (onSubmit) { onSubmit(); } @@ -153,6 +160,10 @@ export const SearchForm = ({ onSubmit }: { onSubmit?: () => void }) => { // Reset the search state when input is empty/cleared resetSearchState(); + if (isSearchPage) { + router.push(paths.home.asPath()); + } + return; } @@ -167,10 +178,23 @@ export const SearchForm = ({ onSubmit }: { onSubmit?: () => void }) => { showOptions: true, })); }); + + if (isHomePage) { + router.push(paths.search.asPath({ query: { q: debouncedInputValue } })); + } + + if (isSearchPage) { + router.replace( + paths.search.asPath({ query: { q: debouncedInputValue } }), + ); + } } }, [debouncedInputValue]); useEffect(() => { + if (isSearchPage) { + return; + } resetSearchState(); }, [pathname]); @@ -214,7 +238,7 @@ export const SearchForm = ({ onSubmit }: { onSubmit?: () => void }) => { {options.map((suggestion, index) => ( void }) => { ? paths.products.asPath({ slug: suggestion.slug }) : "#" } - onClick={() => resetSearchState()} > {suggestion.label} @@ -240,7 +263,6 @@ export const SearchForm = ({ onSubmit }: { onSubmit?: () => void }) => { resetSearchState()} > {ts("search-for", { query: inputValue })}