diff --git a/src/components/Nav/MainNavLinks.tsx b/src/components/Nav/MainNavLinks.tsx index 0e3b628535..aba37768a5 100644 --- a/src/components/Nav/MainNavLinks.tsx +++ b/src/components/Nav/MainNavLinks.tsx @@ -1,12 +1,10 @@ import styles from "./styles.module.scss"; import { Logo } from "../Logo"; import { Icon } from "../Icon"; +import { useEffect, useRef } from "preact/hooks"; type MainNavLinksProps = { - links: { - label: string; - url: string; - }[]; + links: { label: string; url: string }[]; editorButtonLabel: string; donateButtonLabel: string; mobileMenuLabel: string; @@ -26,7 +24,33 @@ export const MainNavLinks = ({ isOpen, hasJumpTo, }: MainNavLinksProps) => { - if (!links || links?.length <= 0) return null; + const menuRef = useRef(null); + const buttonRef = useRef(null); + + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Tab") { + requestAnimationFrame(() => { + const active = document.activeElement; + const isFocusOutside = + menuRef.current && + buttonRef.current && + !menuRef.current.contains(active as Node) && + !buttonRef.current.contains(active as Node); + + if (isOpen && isFocusOutside) { + handleToggle(); + } + }); + } + }; + + document.addEventListener("keydown", handleKeyDown); + return () => document.removeEventListener("keydown", handleKeyDown); + }, [isOpen, handleToggle]); + + if (!links || links.length === 0) return null; const renderLogo = () => (
@@ -43,20 +67,18 @@ export const MainNavLinks = ({
); -}; +}; \ No newline at end of file diff --git a/src/components/SearchResults/index.tsx b/src/components/SearchResults/index.tsx index b3bc9673ef..f02eaafccf 100644 --- a/src/components/SearchResults/index.tsx +++ b/src/components/SearchResults/index.tsx @@ -1,4 +1,4 @@ -import { useMemo, useRef, useState } from "preact/hooks"; +import { useMemo, useRef, useState, useEffect } from "preact/hooks"; import { Icon } from "../Icon"; type SearchResult = { @@ -23,46 +23,96 @@ const SearchResults = ({ uiTranslations, }: SearchResultProps) => { const inputRef = useRef(null); + const clearButtonRef = useRef(null); + const submitButtonRef = useRef(null); const [currentFilter, setCurrentFilter] = useState(""); const [isInputEdited, setInputEdited] = useState(false); + useEffect(() => { + if (searchTerm && clearButtonRef.current) { + clearButtonRef.current.focus(); + } else if (!searchTerm && inputRef.current) { + inputRef.current.focus(); + } + }, [searchTerm]); + const allUniqueCategoriesForResults = useMemo(() => { const categories = results.map((result) => result.category); return [...new Set(categories)]; }, [results]); const uniqueCategories = useMemo(() => { - if (currentFilter) { - return [currentFilter]; - } - return allUniqueCategoriesForResults; + return currentFilter ? [currentFilter] : allUniqueCategoriesForResults; }, [currentFilter, allUniqueCategoriesForResults]); - const uiTranslationKey = (category: string) => { - return ( - category - // words in a category slugs are separated by dashes - .split("-") - .map((word) => { - // Capitalize the first letter of the word - return word.charAt(0).toUpperCase() + word.slice(1); - }) - .join(" ") - ); - }; + const uiTranslationKey = (category: string) => + category + .split("-") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); const toggleFilter = (category: string) => { - if (currentFilter === category) { - setCurrentFilter(""); - } else { - setCurrentFilter(category); - } + setCurrentFilter((prev) => (prev === category ? "" : category)); }; + const clearInput = () => { + if (inputRef.current) inputRef.current.value = ""; + setInputEdited(false); + onSearchChange(""); + }; + + const submitInput = () => { + if (inputRef.current) onSearchChange(inputRef.current.value); + }; + + const renderBigSearchForm = () => ( + + { + setInputEdited(true); + if (e.key === "Enter") { + e.preventDefault(); + submitInput(); + } + }} + class="h-fit w-full appearance-none bg-transparent px-md text-4xl placeholder-sidebar-type-color focus:outline-0" + aria-label="Search through site content" + required + /> + {isInputEdited ? ( + + ) : ( + + )} + + ); + const renderFilterByOptions = () => { - if (results.length === 0) { - return null; - } + if (results.length === 0) return null; return (
@@ -71,7 +121,11 @@ const SearchResults = ({ {allUniqueCategoriesForResults.map((category) => (
  • - ) : ( - - )} - - ); - }; - const renderResults = () => { if (results.length === 0) { return ( @@ -181,13 +178,14 @@ const SearchResults = ({ return (
    -

    {results.length} results found for

    +

    + {results.length} results found for +

    {renderBigSearchForm()}
    {renderFilterByOptions()}
    - {renderResults()}
    );