From c0b6c7e7b18ff5ed85649a7416cdf8cda4267295 Mon Sep 17 00:00:00 2001 From: nios-x Date: Fri, 27 Feb 2026 15:47:09 +0000 Subject: [PATCH] feat/move-main-page-search-bar --- frontend/src/app/chapters/page.tsx | 1 + frontend/src/app/committees/page.tsx | 1 + frontend/src/app/contribute/page.tsx | 1 + frontend/src/app/members/page.tsx | 1 + frontend/src/app/mentorship/programs/page.tsx | 1 + frontend/src/app/my/mentorship/page.tsx | 1 + frontend/src/app/organizations/page.tsx | 1 + frontend/src/app/page.tsx | 9 --- frontend/src/app/projects/page.tsx | 1 + frontend/src/components/Header.tsx | 64 ++++++++++++++++++- frontend/src/components/MultiSearch.tsx | 23 +++++-- frontend/src/components/SearchPageLayout.tsx | 21 ++++-- frontend/src/types/search.ts | 3 + 13 files changed, 106 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/chapters/page.tsx b/frontend/src/app/chapters/page.tsx index 621a8d9a6d..5e40d40197 100644 --- a/frontend/src/app/chapters/page.tsx +++ b/frontend/src/app/chapters/page.tsx @@ -84,6 +84,7 @@ const ChaptersPage = () => { isLoaded={isLoaded} onPageChange={handlePageChange} onSearch={handleSearch} + scopeLabel="Search within chapters" searchPlaceholder="Search for chapters..." searchQuery={searchQuery} totalPages={totalPages} diff --git a/frontend/src/app/committees/page.tsx b/frontend/src/app/committees/page.tsx index bd52479962..0f17b5851a 100644 --- a/frontend/src/app/committees/page.tsx +++ b/frontend/src/app/committees/page.tsx @@ -59,6 +59,7 @@ const CommitteesPage = () => { isLoaded={isLoaded} onPageChange={handlePageChange} onSearch={handleSearch} + scopeLabel="Search within committees" searchPlaceholder="Search for committees..." searchQuery={searchQuery} totalPages={totalPages} diff --git a/frontend/src/app/contribute/page.tsx b/frontend/src/app/contribute/page.tsx index 24317b1bfd..a6eb29ee45 100644 --- a/frontend/src/app/contribute/page.tsx +++ b/frontend/src/app/contribute/page.tsx @@ -77,6 +77,7 @@ const ContributePage = () => { isLoaded={isLoaded} onPageChange={handlePageChange} onSearch={handleSearch} + scopeLabel="Search within issues" searchPlaceholder="Search for issues..." searchQuery={searchQuery} totalPages={totalPages} diff --git a/frontend/src/app/members/page.tsx b/frontend/src/app/members/page.tsx index f024ab9a47..9bfc8beaac 100644 --- a/frontend/src/app/members/page.tsx +++ b/frontend/src/app/members/page.tsx @@ -59,6 +59,7 @@ const UsersPage = () => { isLoaded={isLoaded} onPageChange={handlePageChange} onSearch={handleSearch} + scopeLabel="Search within members" searchPlaceholder="Search for members..." searchQuery={searchQuery} totalPages={totalPages} diff --git a/frontend/src/app/mentorship/programs/page.tsx b/frontend/src/app/mentorship/programs/page.tsx index a987299769..4b2eaaa480 100644 --- a/frontend/src/app/mentorship/programs/page.tsx +++ b/frontend/src/app/mentorship/programs/page.tsx @@ -41,6 +41,7 @@ const ProgramsPage = () => { isLoaded={isLoaded} onPageChange={handlePageChange} onSearch={handleSearch} + scopeLabel="Search within programs" searchPlaceholder="Search for programs..." searchQuery={searchQuery} totalPages={totalPages} diff --git a/frontend/src/app/my/mentorship/page.tsx b/frontend/src/app/my/mentorship/page.tsx index c62134b310..0c54a7ba31 100644 --- a/frontend/src/app/my/mentorship/page.tsx +++ b/frontend/src/app/my/mentorship/page.tsx @@ -126,6 +126,7 @@ const MyMentorshipPage: React.FC = () => { setPage(1) }} searchQuery={searchQuery} + scopeLabel="Search within your programs" searchPlaceholder="Search your programs" indexName="my-programs" > diff --git a/frontend/src/app/organizations/page.tsx b/frontend/src/app/organizations/page.tsx index bb567387ad..a8b7f6af6d 100644 --- a/frontend/src/app/organizations/page.tsx +++ b/frontend/src/app/organizations/page.tsx @@ -59,6 +59,7 @@ const OrganizationPage = () => { isLoaded={isLoaded} onPageChange={handlePageChange} onSearch={handleSearch} + scopeLabel="Search within organizations" searchPlaceholder="Search for organizations..." searchQuery={searchQuery} totalPages={totalPages} diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 859d1fb2b5..d4d28f701a 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -40,7 +40,6 @@ import LoadingSpinner from 'components/LoadingSpinner' import MovingLogos from 'components/LogoCarousel' import Milestones from 'components/Milestones' import DialogComp from 'components/Modal' -import MultiSearchBar from 'components/MultiSearch' import RecentIssues from 'components/RecentIssues' import RecentPullRequests from 'components/RecentPullRequests' import RecentReleases from 'components/RecentReleases' @@ -146,14 +145,6 @@ export default function Home() { Your gateway to OWASP. Discover, engage, and help shape the future!

-
- -
{ isLoaded={isLoaded} onPageChange={handlePageChange} onSearch={handleSearch} + scopeLabel="Search within projects" searchPlaceholder="Search for projects..." searchQuery={searchQuery} sortChildren={ diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index cfbf5b7e33..86c74dc53f 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -11,9 +11,11 @@ import { FaStar as FaSolidStar, FaBars, FaTimes, + FaSearch, } from 'react-icons/fa' import { desktopViewMinWidth, headerLinks } from 'utils/constants' import { cn } from 'utils/utility' +import MultiSearchBar from 'components/MultiSearch' import ModeToggle from 'components/ModeToggle' import NavButton from 'components/NavButton' import NavDropdown from 'components/NavDropDown' @@ -22,18 +24,28 @@ import UserMenu from 'components/UserMenu' export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthEnabled: boolean }) { const pathname = usePathname() const [mobileMenuOpen, setMobileMenuOpen] = useState(false) - const toggleMobileMenu = () => setMobileMenuOpen(!mobileMenuOpen) + const [mobileSearchOpen, setMobileSearchOpen] = useState(false) + const toggleMobileMenu = () => { + setMobileMenuOpen((prev) => !prev) + setMobileSearchOpen(false) + } + const toggleMobileSearch = () => { + setMobileSearchOpen((prev) => !prev) + setMobileMenuOpen(false) + } useEffect(() => { const handleResize = () => { if (globalThis.innerWidth >= desktopViewMinWidth) { setMobileMenuOpen(false) + setMobileSearchOpen(false) } } const handleOutsideClick = (event: Event) => { const navbar = document.getElementById('navbar-sticky') const sidebar = document.querySelector('.fixed.inset-y-0') + const mobileSearch = document.getElementById('global-search-mobile') if ( mobileMenuOpen && navbar && @@ -43,6 +55,15 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE ) { setMobileMenuOpen(false) } + if ( + mobileSearchOpen && + mobileSearch && + !mobileSearch.contains(event.target as Node) && + navbar && + !navbar.contains(event.target as Node) + ) { + setMobileSearchOpen(false) + } } globalThis.addEventListener('resize', handleResize) @@ -60,7 +81,10 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE {/* Logo */} setMobileMenuOpen(false)} + onClick={() => { + setMobileMenuOpen(false) + setMobileSearchOpen(false) + }} className="rounded-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white" >
@@ -108,7 +132,26 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE })}
+
+ +
+
+ +
+ {mobileSearchOpen && ( +
+ +
+ )}
setMobileMenuOpen(false)} + onClick={() => { + setMobileMenuOpen(false) + setMobileSearchOpen(false) + }} className="rounded-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white" >
diff --git a/frontend/src/components/MultiSearch.tsx b/frontend/src/components/MultiSearch.tsx index efa3c69fc2..5a82ce22b3 100644 --- a/frontend/src/components/MultiSearch.tsx +++ b/frontend/src/components/MultiSearch.tsx @@ -13,6 +13,7 @@ import type { Organization } from 'types/organization' import type { Project } from 'types/project' import type { MultiSearchBarProps, Suggestion } from 'types/search' import type { User } from 'types/user' +import { cn } from 'utils/utility' type SearchHit = Chapter | Event | Organization | Project | User @@ -22,6 +23,9 @@ const MultiSearchBar: React.FC = ({ indexes, initialValue = '', eventData, + autoFocus = false, + containerClassName, + inputClassName, }) => { const [searchQuery, setSearchQuery] = useState(initialValue) const [suggestions, setSuggestions] = useState([]) @@ -115,6 +119,10 @@ const MultiSearchBar: React.FC = ({ useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { + const isInputFocused = document.activeElement === inputRef.current + if (!isInputFocused && !showSuggestions) { + return + } if (event.key === 'Escape') { setShowSuggestions(false) inputRef.current?.blur() @@ -158,8 +166,6 @@ const MultiSearchBar: React.FC = ({ }, [searchQuery, suggestions, highlightedIndex, handleSuggestionClick]) useEffect(() => { - inputRef.current?.focus() - const handleClickOutside = (event: MouseEvent) => { if (searchBarRef.current && !searchBarRef.current.contains(event.target as Node)) { setShowSuggestions(false) @@ -173,6 +179,12 @@ const MultiSearchBar: React.FC = ({ } }, []) + useEffect(() => { + if (autoFocus) { + inputRef.current?.focus() + } + }, [autoFocus]) + const handleSearchChange = (e: React.ChangeEvent) => { const newQuery = e.target.value setSearchQuery(newQuery) @@ -221,7 +233,7 @@ const MultiSearchBar: React.FC = ({ } return ( -
+
{isLoaded ? ( <> @@ -236,7 +248,10 @@ const MultiSearchBar: React.FC = ({ onChange={handleSearchChange} onFocus={handleFocusSearch} placeholder={placeholder} - className="h-12 w-full rounded-lg border-1 border-gray-300 bg-white pr-10 pl-10 text-lg text-black focus:ring-1 focus:ring-blue-500 focus:outline-hidden dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:focus:ring-blue-300" + className={cn( + 'h-12 w-full rounded-lg border-1 border-gray-300 bg-white pr-10 pl-10 text-lg text-black focus:ring-1 focus:ring-blue-500 focus:outline-hidden dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:focus:ring-blue-300', + inputClassName + )} /> {searchQuery && (