diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 4f615830f..15202dcf6 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -4,7 +4,7 @@ import { useFeatureFlags } from "src/hooks/useFeatureFlags"; import { assetPath } from "src/utils/assetPath"; import { useTranslations } from "next-intl"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { GovBanner, NavMenuButton, @@ -14,8 +14,9 @@ import { } from "@trussworks/react-uswds"; type PrimaryLinks = { - i18nKey: string; + linkText: string; href: string; + flag?: string; }[]; type Props = { @@ -23,38 +24,67 @@ type Props = { locale?: string; }; +const toNavLinkItems = (linkDetails: { linkText: string; href: string }[]) => { + return linkDetails.map((link) => ( + + {link.linkText} + + )); +}; + const Header = ({ logoPath, locale }: Props) => { const t = useTranslations("Header"); const [isMobileNavExpanded, setIsMobileNavExpanded] = useState(false); + const { + featureFlagsManager: { featureFlags }, + } = useFeatureFlags(); + const handleMobileNavToggle = () => { setIsMobileNavExpanded(!isMobileNavExpanded); }; - const primaryLinksRef = useRef([]); - const { featureFlagsManager } = useFeatureFlags(); + const primaryNavLinkConfigs: PrimaryLinks = [ + { linkText: t("nav_link_home"), href: "/" }, + { linkText: t("nav_link_process"), href: "/process" }, + { linkText: t("nav_link_research"), href: "/research" }, + { linkText: t("nav_link_subscribe"), href: "/subscribe" }, + ]; - 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 searchNavLink = { - i18nKey: t("nav_link_search"), + const featureFlaggedNavLinkConfigs: PrimaryLinks = [ + { + linkText: t("nav_link_search"), href: "/search?status=forecasted,posted", - }; - if (featureFlagsManager.isFeatureEnabled("showSearchV0")) { - primaryLinksRef.current.splice(1, 0, searchNavLink); - } - }, [featureFlagsManager, t]); + flag: "showSearchV0", + }, + ]; - const navItems = primaryLinksRef.current.map((link) => ( - - {link.i18nKey} - - )); - const language = locale && locale.match("/^es/") ? "spanish" : "english"; + const primaryLinksRef = useRef(primaryNavLinkConfigs); + + // note that this will not update when feature flags are updated without a refresh + useEffect(() => { + const navLinksFromFlags = featureFlaggedNavLinkConfigs.reduce( + (acc, link) => { + const { flag = "" } = link; + console.log("####", featureFlags[flag]); + if ( + featureFlags[flag] && + !primaryLinksRef.current.some( + (existingLink) => existingLink.href === link.href, + ) + ) { + acc.splice(1, 0, link); + } + return acc; + }, + primaryNavLinkConfigs, + ); + primaryLinksRef.current = navLinksFromFlags; + }, [featureFlags]); + + const language = useMemo( + () => (locale && locale.match("/^es/") ? "spanish" : "english"), + [locale], + ); return ( <> @@ -85,7 +115,7 @@ const Header = ({ logoPath, locale }: Props) => { /> diff --git a/frontend/tests/components/Header.test.tsx b/frontend/tests/components/Header.test.tsx index 1f8fb07f5..3b0178921 100644 --- a/frontend/tests/components/Header.test.tsx +++ b/frontend/tests/components/Header.test.tsx @@ -1,5 +1,5 @@ import userEvent from "@testing-library/user-event"; -import { render, screen } from "tests/react-utils"; +import { render, screen, waitFor } from "tests/react-utils"; import Header from "src/components/Header"; @@ -17,7 +17,27 @@ const props = { ], }; +let searchFeatureFlag = false; + +const getSearchFeatureFlag = () => { + console.log("$$$", searchFeatureFlag); + return searchFeatureFlag; +}; + +jest.mock("src/hooks/useFeatureFlags", () => ({ + useFeatureFlags: () => ({ + featureFlagsManager: { + featureFlags: { + showSearchV0: getSearchFeatureFlag(), + }, + }, + }), +})); + describe("Header", () => { + afterEach(() => { + searchFeatureFlag = false; + }); it("toggles the mobile nav menu", async () => { render(
); const menuButton = screen.getByTestId("navMenuButton"); @@ -50,4 +70,28 @@ describe("Header", () => { expect(govBanner).toHaveAttribute("aria-expanded", "true"); }); + + it("displays expected nav links without feature flags", () => { + render(
); + + const expectedNavLinks = ["Home", "Process", "Research", "Subscribe"]; + expectedNavLinks.forEach((linkText) => { + const link = screen.getByText(linkText); + expect(link).toBeInTheDocument(); + }); + // ensure search is not included by default + try { + screen.getByText("Search"); + } catch (_e) { + expect(false).toBeFalsy; + } + }); + + it("displays expected nav links without feature flags", async () => { + searchFeatureFlag = true; + + render(
); + + waitFor(() => expect(screen.getByText("Search")).toBeInTheDocument()); + }); });