-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Issue #2732] clear search input when clicking search nav link #2756
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
"use client"; | ||
|
||
import { useFeatureFlags } from "src/hooks/useFeatureFlags"; | ||
import { assetPath } from "src/utils/assetPath"; | ||
|
||
import { useTranslations } from "next-intl"; | ||
import Link from "next/link"; | ||
import { useEffect, useRef, useState } from "react"; | ||
import { usePathname } from "next/navigation"; | ||
import { useCallback, useMemo, useState } from "react"; | ||
import { | ||
GovBanner, | ||
NavMenuButton, | ||
|
@@ -14,47 +14,75 @@ import { | |
Header as USWDSHeader, | ||
} from "@trussworks/react-uswds"; | ||
|
||
type PrimaryLinks = { | ||
i18nKey: string; | ||
href: string; | ||
}[]; | ||
type PrimaryLink = { | ||
text?: string; | ||
href?: string; | ||
}; | ||
|
||
type Props = { | ||
logoPath?: string; | ||
locale?: string; | ||
}; | ||
|
||
const Header = ({ logoPath, locale }: Props) => { | ||
const NavLinks = ({ | ||
mobileExpanded, | ||
onToggleMobileNav, | ||
}: { | ||
mobileExpanded: boolean; | ||
onToggleMobileNav: () => unknown; | ||
}) => { | ||
const t = useTranslations("Header"); | ||
const [isMobileNavExpanded, setIsMobileNavExpanded] = useState(false); | ||
const handleMobileNavToggle = () => { | ||
setIsMobileNavExpanded(!isMobileNavExpanded); | ||
}; | ||
const path = usePathname(); | ||
|
||
const primaryLinksRef = useRef<PrimaryLinks>([]); | ||
const { featureFlagsManager } = useFeatureFlags(); | ||
const getSearchLink = useCallback( | ||
(onSearch: boolean) => { | ||
return { | ||
text: t("nav_link_search"), | ||
href: onSearch ? "/search?refresh=true" : "/search", | ||
}; | ||
}, | ||
[t], | ||
); | ||
|
||
useEffect(() => { | ||
primaryLinksRef.current = [ | ||
{ i18nKey: t("nav_link_home"), href: "/" }, | ||
{ i18nKey: t("nav_link_process"), href: "/process" }, | ||
{ i18nKey: t("nav_link_research"), href: "/research" }, | ||
{ i18nKey: t("nav_link_subscribe"), href: "/subscribe" }, | ||
const navLinkList = useMemo(() => { | ||
return [ | ||
{ text: t("nav_link_home"), href: "/" }, | ||
getSearchLink(path.includes("/search")), | ||
{ text: t("nav_link_process"), href: "/process" }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't get a chance to review this, but this could be simplified by just including an array of links instead of an array of objects that is then mapped to links. Something for future clean-up. |
||
{ text: t("nav_link_research"), href: "/research" }, | ||
{ text: t("nav_link_subscribe"), href: "/subscribe" }, | ||
]; | ||
const searchNavLink = { | ||
i18nKey: t("nav_link_search"), | ||
href: "/search", | ||
}; | ||
if (featureFlagsManager.isFeatureEnabled("showSearchV0")) { | ||
primaryLinksRef.current.splice(1, 0, searchNavLink); | ||
} | ||
}, [featureFlagsManager, t]); | ||
}, [t, path, getSearchLink]); | ||
|
||
const navItems = useMemo(() => { | ||
return navLinkList.map((link: PrimaryLink) => { | ||
if (!link.text || !link.href) { | ||
return <></>; | ||
} | ||
return ( | ||
<Link href={link.href} key={link.href}> | ||
{link.text} | ||
</Link> | ||
); | ||
}); | ||
}, [navLinkList]); | ||
|
||
return ( | ||
<PrimaryNav | ||
items={navItems} | ||
mobileExpanded={mobileExpanded} | ||
onToggleMobileNav={onToggleMobileNav} | ||
></PrimaryNav> | ||
); | ||
}; | ||
|
||
const navItems = primaryLinksRef.current.map((link) => ( | ||
<Link href={link.href} key={link.href}> | ||
{link.i18nKey} | ||
</Link> | ||
)); | ||
const Header = ({ logoPath, locale }: Props) => { | ||
const t = useTranslations("Header"); | ||
const [isMobileNavExpanded, setIsMobileNavExpanded] = | ||
useState<boolean>(false); | ||
const handleMobileNavToggle = () => { | ||
setIsMobileNavExpanded(!isMobileNavExpanded); | ||
}; | ||
const language = locale && locale.match("/^es/") ? "spanish" : "english"; | ||
|
||
return ( | ||
|
@@ -85,11 +113,10 @@ const Header = ({ logoPath, locale }: Props) => { | |
label={t("nav_menu_toggle")} | ||
/> | ||
</div> | ||
<PrimaryNav | ||
items={navItems} | ||
<NavLinks | ||
mobileExpanded={isMobileNavExpanded} | ||
onToggleMobileNav={handleMobileNavToggle} | ||
></PrimaryNav> | ||
/> | ||
</div> | ||
</USWDSHeader> | ||
</> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,22 +4,38 @@ import { QueryContext } from "src/app/[locale]/search/QueryProvider"; | |
import { useSearchParamUpdater } from "src/hooks/useSearchParamUpdater"; | ||
|
||
import { useTranslations } from "next-intl"; | ||
import { useContext } from "react"; | ||
import { useContext, useEffect, useRef } from "react"; | ||
import { Icon } from "@trussworks/react-uswds"; | ||
|
||
interface SearchBarProps { | ||
query: string | null | undefined; | ||
} | ||
|
||
export default function SearchBar({ query }: SearchBarProps) { | ||
const inputRef = useRef<HTMLInputElement>(null); | ||
const { queryTerm, updateQueryTerm } = useContext(QueryContext); | ||
const { updateQueryParams } = useSearchParamUpdater(); | ||
const { updateQueryParams, searchParams } = useSearchParamUpdater(); | ||
const t = useTranslations("Search"); | ||
|
||
const handleSubmit = () => { | ||
updateQueryParams("", "query", queryTerm, false); | ||
}; | ||
|
||
const t = useTranslations("Search"); | ||
// if we have "refresh=true" query param, clear the input | ||
// this supports the expected refresh of the input if the user clicks the search link while on the search page | ||
useEffect(() => { | ||
if (searchParams.get("refresh") && inputRef.current) { | ||
updateQueryTerm(""); | ||
inputRef.current.value = ""; | ||
} | ||
}, [searchParams, updateQueryTerm]); | ||
|
||
// removes the "refresh" param once a user has dirtied the input | ||
useEffect(() => { | ||
if (searchParams.get("refresh") && inputRef.current?.value) { | ||
updateQueryParams("", "refresh"); | ||
} | ||
}, [searchParams, updateQueryParams]); | ||
|
||
return ( | ||
<div className="margin-top-5 margin-bottom-2"> | ||
|
@@ -36,6 +52,7 @@ export default function SearchBar({ query }: SearchBarProps) { | |
</label> | ||
<div className="usa-search usa-search--big" role="search"> | ||
<input | ||
ref={inputRef} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non blocking: Is it worth just adding either useState or possibly the newer server action state management for this form as opposed to using refs and essentially handling setting state manually? |
||
className="usa-input maxw-none" | ||
id="query" | ||
type="search" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
love this!! kudos!