* 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 <jamesespy@R0X44KL2QN.local>
This commit is contained in:
James Espy 2024-07-08 07:48:22 -06:00 коммит произвёл GitHub
Родитель 3d77f9bc27
Коммит d7fd8f0ea4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
5 изменённых файлов: 183 добавлений и 2 удалений

Просмотреть файл

@ -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.

Просмотреть файл

@ -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;

Просмотреть файл

@ -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(<ComposedDashboard />);
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(<ComposedDashboard />);
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(<ComposedDashboard />);
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(<ComposedDashboard />);
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", () => {

Просмотреть файл

@ -43,6 +43,7 @@ export const View = (props: Props) => {
<>
<AccountDeletionNotification />
<main className={styles.wrapper}>
{props.eligibleForPremium && <TopNavBar l10n={props.l10n} />}
<header className={styles.hero}>
<div className={styles.heroContent}>
<h1>{props.l10n.getString("landing-all-hero-title")}</h1>
@ -252,6 +253,52 @@ export const View = (props: Props) => {
);
};
export const TopNavBar = ({ l10n }: { l10n: ExtendedReactLocalization }) => {
return (
<div className={styles.navbar}>
<div className={styles.navbarLinksContainer}>
<TelemetryLink
className={styles.navbarLinks}
href="/how-it-works"
eventData={{
link_id: "navbar_how_it_works",
}}
>
{l10n.getString("landing-all-hero-navbar-link-how-it-works")}
</TelemetryLink>
<TelemetryLink
className={styles.navbarLinks}
href="#pricing"
eventData={{
link_id: "navbar_pricing",
}}
>
{l10n.getString("landing-all-hero-navbar-link-pricing")}
</TelemetryLink>
<TelemetryLink
data-testid="navbar_faqs"
className={styles.navbarLinks}
href="#faq"
eventData={{
link_id: "navbar_faqs",
}}
>
{l10n.getString("landing-all-hero-navbar-link-faqs")}
</TelemetryLink>
<TelemetryLink
className={styles.navbarLinks}
href="/breaches"
eventData={{
link_id: "navbar_breaches",
}}
>
{l10n.getString("landing-all-hero-navbar-link-all-breaches")}
</TelemetryLink>
</div>
</div>
);
};
const HeroImage = (props: Props) => {
if (!props.eligibleForPremium) {
return (

Просмотреть файл

@ -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 {