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 <jamesespy@R0X44KL2QN.local>
This commit is contained in:
Родитель
3d77f9bc27
Коммит
d7fd8f0ea4
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче