From d7fd8f0ea467d676c2aa6b0928f3697e14da42d9 Mon Sep 17 00:00:00 2001 From: James Espy <31937894+jespy2@users.noreply.github.com> Date: Mon, 8 Jul 2024 07:48:22 -0600 Subject: [PATCH] Mntor 3250 (#4701) * navbar with link text added * updated link path for breaches * added styles and classnames * updated spacing * componentized navbar * added navbar to how it works page * removed navbar from how it works page per PM request * updated navbar and header background colors * telemetry added to links * nav tags changed to div to conform with axe test requirement of being only used once * added unit tests for navbar * added conditional logic to show to US-only users * updated links to TelemetryLinks * updated font color on navbar links * corrected test to be composed with US users * updated to group-level comment * navbar strings moved to locales-pending file * updated conditional render to use eligibleForPremium * styles updated for mobile * fixed linting error * fixed linting error --------- Co-authored-by: James Espy --- locales-pending/landing-premium.ftl | 9 ++ .../(public)/LandingView.module.scss | 35 ++++++- .../(redesign)/(public)/LandingView.test.tsx | 92 +++++++++++++++++++ .../(redesign)/(public)/LandingView.tsx | 47 ++++++++++ .../(public)/PublicShell.module.scss | 2 +- 5 files changed, 183 insertions(+), 2 deletions(-) diff --git a/locales-pending/landing-premium.ftl b/locales-pending/landing-premium.ftl index 3f710a08e..398d4b7f1 100644 --- a/locales-pending/landing-premium.ftl +++ b/locales-pending/landing-premium.ftl @@ -2,6 +2,15 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +## Top navbar + +landing-all-hero-navbar-link-how-it-works = How it works +landing-all-hero-navbar-link-pricing = Pricing +landing-all-hero-navbar-link-faqs = FAQs +landing-all-hero-navbar-link-all-breaches = All breaches + +## + landing-premium-hero-lead = We scan to see if your phone number, passwords or home address have been leaked, and help you make it private again. landing-premium-hero-emailform-input-label = Enter your email address to check for data breach exposures and sites selling your info. diff --git a/src/app/(proper_react)/(redesign)/(public)/LandingView.module.scss b/src/app/(proper_react)/(redesign)/(public)/LandingView.module.scss index 143280f47..1634ac11a 100644 --- a/src/app/(proper_react)/(redesign)/(public)/LandingView.module.scss +++ b/src/app/(proper_react)/(redesign)/(public)/LandingView.module.scss @@ -5,7 +5,40 @@ flex-direction: column; height: 100%; } - +.navbar { + font: $text-body-xl; + padding: $layout-xs; + display: flex; + flex-direction: row; + justify-content: flex-start; + background-color: $color-grey-05; + .navbarLinksContainer { + display: flex; + flex-direction: column; + .navbarLinks { + text-decoration: none; + color: $color-grey-50; + } + } + @media screen and (min-width: $screen-sm) { + justify-content: flex-end; + .navbarLinksContainer { + flex-direction: row; + justify-content: flex-end; + .navbarLinks { + margin-left: $spacing-lg; + } + } + } + @media screen and (min-width: $screen-md) { + padding: $layout-xs $layout-xl; + .navbarLinksContainer { + .navbarLinks { + margin-left: $spacing-xl; + } + } + } +} .hero { flex: 1 0 auto; display: flex; diff --git a/src/app/(proper_react)/(redesign)/(public)/LandingView.test.tsx b/src/app/(proper_react)/(redesign)/(public)/LandingView.test.tsx index 6988ca781..54b05cf78 100644 --- a/src/app/(proper_react)/(redesign)/(public)/LandingView.test.tsx +++ b/src/app/(proper_react)/(redesign)/(public)/LandingView.test.tsx @@ -739,6 +739,98 @@ describe("When Premium is available", () => { // test: jest.spyOn(console, "error").mockImplementationOnce(() => undefined); }); + + it("counts the number of clicks How it Works link in top navbar", async () => { + const mockedRecord = useTelemetry(); + const ComposedDashboard = composeStory(LandingUs, Meta); + render(); + + const user = userEvent.setup(); + + const navbarLink = screen.getByRole("link", { name: "How it works" }); + // jsdom will complain about not being able to navigate to a different page + // after clicking the link; suppress that error, as it's not relevant to the + // test: + jest.spyOn(console, "error").mockImplementationOnce(() => undefined); + await user.click(navbarLink); + + expect(mockedRecord).toHaveBeenCalledWith( + "link", + "click", + expect.objectContaining({ + link_id: "navbar_how_it_works", + }), + ); + }); + + it("counts the number of clicks Pricing link in top navbar", async () => { + const mockedRecord = useTelemetry(); + const ComposedDashboard = composeStory(LandingUs, Meta); + render(); + + const user = userEvent.setup(); + + const navbarLink = screen.getByRole("link", { name: "Pricing" }); + // jsdom will complain about not being able to navigate to a different page + // after clicking the link; suppress that error, as it's not relevant to the + // test: + jest.spyOn(console, "error").mockImplementationOnce(() => undefined); + await user.click(navbarLink); + + expect(mockedRecord).toHaveBeenCalledWith( + "link", + "click", + expect.objectContaining({ + link_id: "navbar_pricing", + }), + ); + }); + + it("counts the number of clicks FAQs link in top navbar", async () => { + const mockedRecord = useTelemetry(); + const ComposedDashboard = composeStory(LandingUs, Meta); + render(); + + const user = userEvent.setup(); + + const navbarLink = screen.getByTestId("navbar_faqs"); + // jsdom will complain about not being able to navigate to a different page + // after clicking the link; suppress that error, as it's not relevant to the + // test: + jest.spyOn(console, "error").mockImplementationOnce(() => undefined); + await user.click(navbarLink); + + expect(mockedRecord).toHaveBeenCalledWith( + "link", + "click", + expect.objectContaining({ + link_id: "navbar_faqs", + }), + ); + }); + + it("counts the number of clicks All breaches link in top navbar", async () => { + const mockedRecord = useTelemetry(); + const ComposedDashboard = composeStory(LandingUs, Meta); + render(); + + const user = userEvent.setup(); + + const navbarLink = screen.getByRole("link", { name: "All breaches" }); + // jsdom will complain about not being able to navigate to a different page + // after clicking the link; suppress that error, as it's not relevant to the + // test: + jest.spyOn(console, "error").mockImplementationOnce(() => undefined); + await user.click(navbarLink); + + expect(mockedRecord).toHaveBeenCalledWith( + "link", + "click", + expect.objectContaining({ + link_id: "navbar_breaches", + }), + ); + }); }); it("does not show a confirmaton message if the user has just deleted their account", () => { diff --git a/src/app/(proper_react)/(redesign)/(public)/LandingView.tsx b/src/app/(proper_react)/(redesign)/(public)/LandingView.tsx index d7addc3cd..4c162b904 100644 --- a/src/app/(proper_react)/(redesign)/(public)/LandingView.tsx +++ b/src/app/(proper_react)/(redesign)/(public)/LandingView.tsx @@ -43,6 +43,7 @@ export const View = (props: Props) => { <>
+ {props.eligibleForPremium && }

{props.l10n.getString("landing-all-hero-title")}

@@ -252,6 +253,52 @@ export const View = (props: Props) => { ); }; +export const TopNavBar = ({ l10n }: { l10n: ExtendedReactLocalization }) => { + return ( +
+
+ + {l10n.getString("landing-all-hero-navbar-link-how-it-works")} + + + {l10n.getString("landing-all-hero-navbar-link-pricing")} + + + {l10n.getString("landing-all-hero-navbar-link-faqs")} + + + {l10n.getString("landing-all-hero-navbar-link-all-breaches")} + +
+
+ ); +}; + const HeroImage = (props: Props) => { if (!props.eligibleForPremium) { return ( diff --git a/src/app/(proper_react)/(redesign)/(public)/PublicShell.module.scss b/src/app/(proper_react)/(redesign)/(public)/PublicShell.module.scss index e7b098ef4..d099282bd 100644 --- a/src/app/(proper_react)/(redesign)/(public)/PublicShell.module.scss +++ b/src/app/(proper_react)/(redesign)/(public)/PublicShell.module.scss @@ -11,7 +11,7 @@ align-items: center; justify-content: space-between; padding: $spacing-md; - background-color: $color-grey-05; + background-color: $color-white; gap: $spacing-lg; h1 {