Skip to content

Commit

Permalink
[Issue 789] Newsletter Pages (#804)
Browse files Browse the repository at this point in the history
* add newsletter pages, content, and Sendy form HTML
  • Loading branch information
andycochran authored Dec 6, 2023
1 parent 1c05c3c commit 2f948fc
Show file tree
Hide file tree
Showing 8 changed files with 364 additions and 0 deletions.
30 changes: 30 additions & 0 deletions frontend/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,36 @@
"agile_title": "Agile",
"agile_content": "We’re building a simpler Grants.gov with you, not for you. Our process gives us the flexibility to swiftly respond to feedback and adapt to changing priorities and requirements."
},
"Newsletter": {
"alert_title": "Simpler Grants.gov is a work in progress.",
"alert": "To search for funding opportunities and apply, go to <LinkToGrants>www.grants.gov</LinkToGrants>.",
"title": "Newsletter signup",
"intro": "Subscribe to get Simpler.Grants.gov project updates in your inbox!",
"paragraph_1": "If you sign up for the Simpler.Grants.gov newsletter, we’ll keep you informed of our progress and you’ll know about every opportunity to get involved.",
"list": "<ul><li>Hear about upcoming milestones</li><li>Be the first to know when we launch new code</li><li>Test out new features and functionalities</li><li>Participate in usability tests and other user research efforts</li><li>Learn about ways to provide feedback </li></ul>",
"disclaimer": "The Simpler.Grants.gov newsletter is powered by the Sendy data service. Personal information is not stored within Simpler.Grants.gov. "
},
"Newsletter_confirmation": {
"alert_title": "Simpler Grants.gov is a work in progress.",
"alert": "To search for funding opportunities and apply, go to <LinkToGrants>www.grants.gov</LinkToGrants>.",
"title": "You’re subscribed",
"intro": "You are signed up to receive project updates from Simpler.Grants.gov.",
"paragraph_1": "Thank you for subscribing. We’ll keep you informed of our progress and you’ll know about every opportunity to get involved.",
"heading": "Learn more",
"paragraph_2": "You can read all about our <process-link>transparent process</process-link> and what we’re doing now, or explore <research-link>our existing user research</research-link> and the findings that are guiding our work.",
"disclaimer": "The Simpler.Grants.gov newsletter is powered by the Sendy data service. Personal information is not stored within Simpler.Grants.gov. "
},
"Newsletter_unsubscribe": {
"alert_title": "Simpler Grants.gov is a work in progress.",
"alert": "To search for funding opportunities and apply, go to <LinkToGrants>www.grants.gov</LinkToGrants>.",
"title": "You have unsubscribed",
"intro": "You will no longer receive project updates from Simpler.Grants.gov. ",
"paragraph_1": "Did you unsubscribe by accident? Sign up again.",
"button_resub": "Re-subscribe",
"heading": "Learn more",
"paragraph_2": "You can read all about our <process-link>transparent process</process-link> and what we’re doing now, or explore <research-link>our existing user research</research-link> and the findings that are guiding our work.",
"disclaimer": "The Simpler.Grants.gov newsletter is powered by the Sendy data service. Personal information is not stored within Simpler.Grants.gov. "
},
"ErrorPages": {
"page_not_found": {
"title": "Oops! Page Not Found",
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/constants/breadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ import { Breadcrumb, BreadcrumbList } from "src/components/Breadcrumbs";
const HOME: Breadcrumb = { title: "Home", path: "/" };
const RESEARCH: Breadcrumb = { title: "Research", path: "research/" };
const PROCESS: Breadcrumb = { title: "Process", path: "process/" };
const NEWSLETTER: Breadcrumb = { title: "Newsletter", path: "newsletter/" };
const NEWSLETTER_CONFIRMATION: Breadcrumb = {
title: "Confirmation",
path: "newsletter-confirmation/",
};
const NEWSLETTER_UNSUBSCRIBE: Breadcrumb = {
title: "Unsubscribe",
path: "newsletter-unsubscribe/",
};

export const RESEARCH_CRUMBS: BreadcrumbList = [HOME, RESEARCH];
export const PROCESS_CRUMBS: BreadcrumbList = [HOME, PROCESS];
export const NEWSLETTER_CRUMBS: BreadcrumbList = [HOME, NEWSLETTER];
export const NEWSLETTER_CONFIRMATION_CRUMBS: BreadcrumbList = [
HOME,
NEWSLETTER,
NEWSLETTER_CONFIRMATION,
];
export const NEWSLETTER_UNSUBSCRIBE_CRUMBS: BreadcrumbList = [
HOME,
NEWSLETTER,
NEWSLETTER_UNSUBSCRIBE,
];
80 changes: 80 additions & 0 deletions frontend/src/pages/newsletter-confirmation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import type { GetStaticProps, NextPage } from "next";
import { NEWSLETTER_CONFIRMATION_CRUMBS } from "src/constants/breadcrumbs";
import { ExternalRoutes } from "src/constants/routes";

import { Trans, useTranslation } from "next-i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import Link from "next/link";
import { Grid, GridContainer } from "@trussworks/react-uswds";

import Breadcrumbs from "src/components/Breadcrumbs";
import PageSEO from "src/components/PageSEO";
import FullWidthAlert from "../components/FullWidthAlert";

const NewsletterConfirmation: NextPage = () => {
const { t } = useTranslation("common", {
keyPrefix: "Newsletter_confirmation",
});

return (
<>
<PageSEO title={t("page_title")} description={t("meta_description")} />
<FullWidthAlert type="info" heading={t("alert_title")}>
<Trans
t={t}
i18nKey="alert"
components={{
LinkToGrants: (
<a
target="_blank"
rel="noopener noreferrer"
href={ExternalRoutes.GRANTS_HOME}
/>
),
}}
/>
</FullWidthAlert>
<Breadcrumbs breadcrumbList={NEWSLETTER_CONFIRMATION_CRUMBS} />

<GridContainer className="padding-bottom-5 tablet:padding-top-0 desktop-lg:padding-top-0 border-bottom-2px border-base-lightest">
<h1 className="margin-0 tablet-lg:font-sans-xl desktop-lg:font-sans-2xl">
{t("title")}
</h1>
<p className="usa-intro font-sans-md tablet:font-sans-lg desktop-lg:font-sans-xl margin-bottom-0">
{t("intro")}
</p>
<Grid row gap className="flex-align-start">
<Grid tabletLg={{ col: 6 }}>
<p className="usa-intro">{t("paragraph_1")}</p>
</Grid>
<Grid tabletLg={{ col: 6 }}>
<h2 className="tablet-lg:font-sans-lg tablet-lg:margin-bottom-05">
{t("heading")}
</h2>
<p className="margin-top-0 font-sans-md line-height-sans-4 desktop-lg:line-height-sans-6">
<Trans
t={t}
i18nKey="paragraph_2"
components={{
"process-link": <Link href="/process" />,
"research-link": <Link href="/research" />,
}}
/>
</p>
</Grid>
</Grid>
</GridContainer>
<GridContainer className="padding-bottom-5 tablet:padding-top-3 desktop-lg:padding-top-3">
<p className="font-sans-3xs text-base-dark">{t("disclaimer")}</p>
</GridContainer>
</>
);
};

// Change this to GetServerSideProps if you're using server-side rendering
export const getStaticProps: GetStaticProps = async ({ locale }) => {
const translations = await serverSideTranslations(locale ?? "en");
return { props: { ...translations } };
};

export default NewsletterConfirmation;
83 changes: 83 additions & 0 deletions frontend/src/pages/newsletter-unsubscribe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { GetStaticProps, NextPage } from "next";
import { NEWSLETTER_UNSUBSCRIBE_CRUMBS } from "src/constants/breadcrumbs";
import { ExternalRoutes } from "src/constants/routes";

import { Trans, useTranslation } from "next-i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import Link from "next/link";
import { Grid, GridContainer } from "@trussworks/react-uswds";

import Breadcrumbs from "src/components/Breadcrumbs";
import PageSEO from "src/components/PageSEO";
import FullWidthAlert from "../components/FullWidthAlert";

const NewsletterUnsubscribe: NextPage = () => {
const { t } = useTranslation("common", {
keyPrefix: "Newsletter_unsubscribe",
});

return (
<>
<PageSEO title={t("page_title")} description={t("meta_description")} />
<FullWidthAlert type="info" heading={t("alert_title")}>
<Trans
t={t}
i18nKey="alert"
components={{
LinkToGrants: (
<a
target="_blank"
rel="noopener noreferrer"
href={ExternalRoutes.GRANTS_HOME}
/>
),
}}
/>
</FullWidthAlert>
<Breadcrumbs breadcrumbList={NEWSLETTER_UNSUBSCRIBE_CRUMBS} />

<GridContainer className="padding-bottom-5 tablet:padding-top-0 desktop-lg:padding-top-0 border-bottom-2px border-base-lightest">
<h1 className="margin-0 tablet-lg:font-sans-xl desktop-lg:font-sans-2xl">
{t("title")}
</h1>
<p className="usa-intro font-sans-md tablet:font-sans-lg desktop-lg:font-sans-xl margin-bottom-0">
{t("intro")}
</p>
<Grid row gap className="flex-align-start">
<Grid tabletLg={{ col: 6 }}>
<p className="usa-intro">{t("paragraph_1")}</p>
<Link className="usa-button margin-bottom-4" href="/newsletter">
{t("button_resub")}
</Link>
</Grid>
<Grid tabletLg={{ col: 6 }}>
<h2 className="font-sans-md tablet-lg:font-sans-lg tablet-lg:margin-bottom-05">
{t("heading")}
</h2>
<p className="margin-top-0 font-sans-md line-height-sans-4 desktop-lg:line-height-sans-6">
<Trans
t={t}
i18nKey="paragraph_2"
components={{
"process-link": <Link href="/process" />,
"research-link": <Link href="/research" />,
}}
/>
</p>
</Grid>
</Grid>
</GridContainer>
<GridContainer className="padding-bottom-5 tablet:padding-top-3 desktop-lg:padding-top-3">
<p className="font-sans-3xs text-base-dark">{t("disclaimer")}</p>
</GridContainer>
</>
);
};

// Change this to GetServerSideProps if you're using server-side rendering
export const getStaticProps: GetStaticProps = async ({ locale }) => {
const translations = await serverSideTranslations(locale ?? "en");
return { props: { ...translations } };
};

export default NewsletterUnsubscribe;
107 changes: 107 additions & 0 deletions frontend/src/pages/newsletter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import type { GetStaticProps, NextPage } from "next";
import { NEWSLETTER_CRUMBS } from "src/constants/breadcrumbs";
import { ExternalRoutes } from "src/constants/routes";

import { Trans, useTranslation } from "next-i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import {
Button,
Grid,
GridContainer,
Label,
TextInput,
} from "@trussworks/react-uswds";

import Breadcrumbs from "src/components/Breadcrumbs";
import PageSEO from "src/components/PageSEO";
import FullWidthAlert from "../components/FullWidthAlert";

const Newsletter: NextPage = () => {
const { t } = useTranslation("common", { keyPrefix: "Newsletter" });

return (
<>
<PageSEO title={t("page_title")} description={t("meta_description")} />
<FullWidthAlert type="info" heading={t("alert_title")}>
<Trans
t={t}
i18nKey="alert"
components={{
LinkToGrants: (
<a
target="_blank"
rel="noopener noreferrer"
href={ExternalRoutes.GRANTS_HOME}
/>
),
}}
/>
</FullWidthAlert>
<Breadcrumbs breadcrumbList={NEWSLETTER_CRUMBS} />

<GridContainer className="padding-bottom-5 tablet:padding-top-0 desktop-lg:padding-top-0 border-bottom-2px border-base-lightest">
<h1 className="margin-0 tablet-lg:font-sans-xl desktop-lg:font-sans-2xl">
{t("title")}
</h1>
<p className="usa-intro font-sans-md tablet:font-sans-lg desktop-lg:font-sans-xl margin-bottom-0">
{t("intro")}
</p>
<Grid row gap className="flex-align-start">
<Grid tabletLg={{ col: 6 }}>
<p className="usa-intro">{t("paragraph_1")}</p>
<Trans
t={t}
i18nKey="list"
components={{
ul: (
<ul className="usa-list margin-top-0 tablet-lg:margin-top-3 font-sans-md line-height-sans-4" />
),
li: <li />,
}}
/>
</Grid>
<Grid tabletLg={{ col: 6 }}>
<form
data-testid="sendy-form"
action="https://communications.grants.gov/app/subscribe"
method="POST"
accept-charset="utf-8"
>
<Label htmlFor="name">First Name</Label>
<TextInput type="text" name="name" id="name" />
<Label htmlFor="LastName">Last Name</Label>
<TextInput type="text" name="LastName" id="LastName" />
<div className="display-none">
<Label htmlFor="hp">HP</Label>
<TextInput type="text" name="hp" id="hp" />
</div>
<Label htmlFor="email">Email</Label>
<TextInput type="email" name="email" id="email" />
<input type="hidden" name="list" value="A2zerhEC59Ea6mzTgzdTgw" />
<input type="hidden" name="subform" value="yes" />
<Button
type="submit"
name="submit"
id="submit"
className="margin-top-4"
>
Subscribe
</Button>
</form>
</Grid>
</Grid>
</GridContainer>
<GridContainer className="padding-bottom-5 tablet:padding-top-3 desktop-lg:padding-top-3">
<p className="font-sans-3xs text-base-dark">{t("disclaimer")}</p>
</GridContainer>
</>
);
};

// Change this to GetServerSideProps if you're using server-side rendering
export const getStaticProps: GetStaticProps = async ({ locale }) => {
const translations = await serverSideTranslations(locale ?? "en");
return { props: { ...translations } };
};

export default Newsletter;
12 changes: 12 additions & 0 deletions frontend/tests/pages/newsletter-confirmation.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { render, waitFor } from "@testing-library/react";
import { axe } from "jest-axe";
import NewsletterConfirmation from "src/pages/newsletter-confirmation";

describe("Newsletter", () => {
it("passes accessibility scan", async () => {
const { container } = render(<NewsletterConfirmation />);
const results = await waitFor(() => axe(container));

expect(results).toHaveNoViolations();
});
});
12 changes: 12 additions & 0 deletions frontend/tests/pages/newsletter-unsubscribe.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { render, waitFor } from "@testing-library/react";
import { axe } from "jest-axe";
import NewsletterUnsubscribe from "src/pages/newsletter-unsubscribe";

describe("Newsletter", () => {
it("passes accessibility scan", async () => {
const { container } = render(<NewsletterUnsubscribe />);
const results = await waitFor(() => axe(container));

expect(results).toHaveNoViolations();
});
});
20 changes: 20 additions & 0 deletions frontend/tests/pages/newsletter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { render, screen, waitFor } from "@testing-library/react";
import { axe } from "jest-axe";
import Newsletter from "src/pages/newsletter";

describe("Newsletter", () => {
it("renders signup form with a submit button", () => {
render(<Newsletter />);

const sendyform = screen.getByTestId("sendy-form");

expect(sendyform).toBeInTheDocument();
});

it("passes accessibility scan", async () => {
const { container } = render(<Newsletter />);
const results = await waitFor(() => axe(container));

expect(results).toHaveNoViolations();
});
});

0 comments on commit 2f948fc

Please sign in to comment.