About page sprint4 (#544)

* Card sections of the About page
* Create index.test.tsx
* Add test that verifies no console errors and no undefined variables.
* Added unit test with snapshot
* use global.console.error
* Fix unit test for console.error()
* Remove areasOfFocusList since it's no longer used
* CSS adjustments from PR
* Add back in AlertWrapper
* github.com link opens in new tab
* adds comment to globalize console.error check
* Refactor AboutCard
* Fixing the top grid so it resizes correctly on mobile. (e.g. using `<Grid desktop={{col: 9}}>` will expand to full width when going to mobile.
* So AboutCard can now do 'large' cards (ones at the top) and 'small' cards (ones at the bottom that are 2x per row).
* change `desktop` -> `tablet`
* `<Grid col={1}>{' '}</Grid>`

Co-authored-by: Vim <86254807+vim-usds@users.noreply.github.com>
This commit is contained in:
TomNUSDS 2021-08-19 20:41:54 -07:00 committed by GitHub
parent f7bfc979ba
commit a2eabda319
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 448 additions and 266 deletions

View file

@ -0,0 +1,22 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import AboutCard from './AboutCard';
describe('rendering of the AboutCard', () => {
const {asFragment} = render(
<LocalizedComponent>
<AboutCard
imgSrc={'about:blank'}
header={'Test Header'}
actionText={'Test Action'}
actionUrl={'#'}>
Content body of the action card.
</AboutCard>
</LocalizedComponent>,
);
it('checks if component renders', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,77 @@
import React from 'react';
import {Grid} from '@trussworks/react-uswds';
// the "body" section is the child object to allow for html versus just text
interface AboutCardProps {
imgSrc: string;
header: string;
size?: 'small' | 'large';
actionText?: string;
actionUrl?: string;
actionOpenInNewTab?: boolean;
className?: string;
}
const AboutCard = (props: React.PropsWithChildren<AboutCardProps>) => {
if (props.size === 'large') {
// large are the cards on top
// note it uses a top className='j40-aboutcard-lg-card'
return (
<Grid tablet={{col: true}} gap={'lg'} className={(props.className || '')}>
<Grid row className={'j40-aboutcard-lg-card'}>
<Grid tablet={{col: 3}} className={'j40-aboutpage-image-container'}>
<img
className={'j40-aboutcard-image'}
alt={props.header}
src={props.imgSrc}/>
</Grid>
<Grid tablet={{col: 9}}>
<Grid row>
<h3 className={'j40-section-header'}>{props.header}</h3>
<div className={'j40-section-body'}>{props.children}</div>
</Grid>
</Grid>
</Grid>
</Grid>
);
} else {
// small are the cards on the bottom
// note it uses a top className='j40-aboutcard-sm-card'
return (
<Grid tablet={{col: true}} gap={'lg'} className={(props.className || '')}>
<Grid row className={'j40-aboutcard-sm-card'}>
<Grid tablet={{col: 2}} className={'j40-aboutpage-image-container'}>
<img
className={'j40-aboutcard-image'}
alt={props.header}
src={props.imgSrc}/>
</Grid>
<Grid tablet={{col: 9}}>
<Grid row>
<h3 className={'j40-section-header'}>{props.header}</h3>
<div className={'j40-section-body'}>{props.children}</div>
<div className={'j40-section-footer'}>
{props.actionOpenInNewTab ?
<a
className={'j40-aboutcard-link'}
href={props.actionUrl}
target="_blank"
rel="noreferrer">{props.actionText}</a> :
<a
className={'j40-aboutcard-link'}
href={props.actionUrl}>{props.actionText}</a>
}
</div>
</Grid>
</Grid>
<Grid col={1}>{' '}</Grid>
</Grid>
</Grid>
);
}
};
export default AboutCard;

View file

@ -0,0 +1,17 @@
import React from 'react';
import {Grid} from '@trussworks/react-uswds';
type AboutCardsContainerProps = {
className?: string
}
// note: this assumes a J40MainGridContainer container!
const AboutCardsContainer = (props: React.PropsWithChildren<AboutCardsContainerProps>) => {
return (
<Grid row gap={'lg'} className={'j40-aboutcard-container ' + (props.className || '')}>
{props.children}
</Grid>
);
};
export default AboutCardsContainer;

View file

@ -0,0 +1,56 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the AboutCard checks if component renders 1`] = `
<DocumentFragment>
<div
class="grid-gap-lg tablet:grid-col"
data-testid="grid"
>
<div
class="grid-row j40-aboutcard-card"
data-testid="grid"
>
<div
class="grid-col-2"
data-testid="grid"
>
<img
alt="Test Header"
class="j40-aboutcard-image"
src="about:blank"
/>
</div>
<div
class="grid-col-10"
data-testid="grid"
>
<div
class="grid-row"
data-testid="grid"
>
<div
class="j40-section-header"
>
Test Header
</div>
<div
class="j40-section-body"
>
Content body of the action card.
</div>
<div
class="j40-section-footer"
>
<a
class="j40-aboutcard-link"
href="#"
>
Test Action
</a>
</div>
</div>
</div>
</div>
</div>
</DocumentFragment>
`;

View file

@ -6,13 +6,18 @@ import {GridContainer} from '@trussworks/react-uswds';
interface ILayoutProps {
children: ReactNode,
fullWidth?: boolean,
blueBackground?: boolean,
className?: string
}
const J40MainGridContainer = ({
children,
fullWidth = false,
blueBackground = false,
className = 'j40-grid-container '}: ILayoutProps) => {
// is it a blue background strip?
className += (blueBackground ? 'j40-main-grid-blue-bk ' : '');
return fullWidth ? (
<div
className={'j40-grid-container ' + className}>

View file

@ -1,97 +0,0 @@
import * as React from 'react';
// import * as styles from './areasOfFocusList.module.scss'; // TODO: move styles
import {useIntl} from 'gatsby-plugin-intl';
import {defineMessages} from 'react-intl';
// this section seems too verbose? must be a more readable way to do this.
// this inlines the svg as data:image/svg+xml For larger images this
// can cause page bloat, but it should be fine here.
// @ts-ignore
import ecoIcon from '/node_modules/uswds/dist/img/usa-icons/eco.svg';
// @ts-ignore
import busIcon from '/node_modules/uswds/dist/img/usa-icons/directions_bus.svg';
// @ts-ignore
import homeIcon from '/node_modules/uswds/dist/img/usa-icons/home.svg';
// @ts-ignore
import groupsIcon from '/node_modules/uswds/dist/img/usa-icons/groups.svg';
import pollutionIcon // @ts-ignore
from '/node_modules/uswds/dist/img/usa-icons/severe_weather.svg';
// @ts-ignore
import washIcon from '/node_modules/uswds/dist/img/usa-icons/wash.svg';
// @ts-ignore
import publicIcon from '/node_modules/uswds/dist/img/usa-icons/public.svg';
const AreasOfFocusList = () => {
const intl = useIntl();
const messages = defineMessages({
climate: {
id: 'areasOfInterest.climate',
defaultMessage: 'Climate change',
description: 'item in areasOfInterest list',
},
energy: {
id: 'areasOfInterest.energy',
defaultMessage: 'Clean energy and energy efficiency',
description: 'item in areasOfInterest list',
},
transit: {
id: 'areasOfInterest.transit',
defaultMessage: 'Clean transit',
description: 'item in areasOfInterest list',
},
housing: {
id: 'areasOfInterest.housing',
defaultMessage: 'Affordable and sustainable housing',
description: 'item in areasOfInterest list',
},
training: {
id: 'areasOfInterest.training',
defaultMessage: 'Training and workforce development',
description: 'item in areasOfInterest list',
},
pollution: {
id: 'areasOfInterest.pollution',
defaultMessage: 'Remediation of legacy pollution',
description: 'item in areasOfInterest list',
},
water: {
id: 'areasOfInterest.water',
defaultMessage: 'Clean water infrastructure',
description: 'item in areasOfInterest list',
},
});
const readMoreList: (any | string)[][] = [
[publicIcon, intl.formatMessage(messages.climate)],
[ecoIcon, intl.formatMessage(messages.energy)],
[busIcon, intl.formatMessage(messages.transit)],
[homeIcon, intl.formatMessage(messages.housing)],
[groupsIcon, intl.formatMessage(messages.training)],
[pollutionIcon, intl.formatMessage(messages.pollution)],
[washIcon, intl.formatMessage(messages.water)],
];
return (
<div className={'j40-two-column-confine'}>
<ul className={'j40-two-column'}>
{readMoreList.map((item, index) => {
return (
<li key={`readmore_li_${index}`}>
<div className={'usa-icon-list__icon'}>
<img
className={'j40-two-column-icons-spacing'}
key={`readmore_img_${index}`}
src={item[0]} alt={item[1] + ' icon'}/>
</div>
<div
className={'usa-icon-list__content'}> {item[1]} </div>
</li>
);
})
}
</ul>
</div>
);
};
export default AreasOfFocusList;

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="2154.71" height="2297.42" viewBox="0 0 2154.71 2297.42"><defs><style>.a{fill:#c1c1c1;}.b{font-size:580.24px;fill:#1c507f;font-family:Arial-BoldMT, Arial;font-weight:700;}.c{fill:none;}.d{fill:#fff;}</style></defs><circle class="a" cx="1077.36" cy="1220.06" r="1077.36"/><text class="b" transform="translate(584.94 1817.13)">40%</text><rect class="c" x="315.3" width="1569.74" height="1569.74"/><path class="d" d="M1105.34,752.14c106.61,0,200.8,25.51,277.32,58.87,70.64,31.39,115.12,102,115.12,178.56v105.95H712.91V990.22C712.91,913,757.38,842.4,828,811.66,904.55,777.65,998.73,752.14,1105.34,752.14ZM582.09,768.5c71.95,0,130.82-58.87,130.82-130.82S654,506.87,582.09,506.87,451.28,565.74,451.28,637.68,510.15,768.5,582.09,768.5ZM656,840.44c-24.2-3.92-48.4-6.54-73.91-6.54a454.56,454.56,0,0,0-181.82,37.94,131.52,131.52,0,0,0-79.8,121v102.68H614.8V990.22C614.8,935.93,629.84,884.92,656,840.44Zm972.59-71.94c71.94,0,130.81-58.87,130.81-130.82s-58.87-130.81-130.81-130.81-130.81,58.87-130.81,130.81S1556.64,768.5,1628.59,768.5Zm261.62,224.34a131.51,131.51,0,0,0-79.8-121,454.56,454.56,0,0,0-181.82-37.94c-25.51,0-49.71,2.62-73.91,6.54,26.16,44.48,41.2,95.49,41.2,149.78v105.3h294.33ZM1105.34,310.65A196.22,196.22,0,1,1,909.12,506.87,196,196,0,0,1,1105.34,310.65Z" transform="translate(-27.98 124.54)"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -0,0 +1,30 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import Index from './index';
import {LocalizedComponent} from '../test/testHelpers';
// TODO: Move this to a location that will detect on all tests
// See ticket: #550
jest.spyOn(console, 'error').mockImplementation(() => {});
afterAll(() => {
// Restore mock after all tests are done, so it won't affect other test suites
jest.resetAllMocks();
});
describe('rendering of the component', () => {
const {asFragment} = render(
<LocalizedComponent>
<Index location={window.location}/>
</LocalizedComponent>,
);
it(`should not contain 'undefined" anywhere`, () => {
expect(asFragment()).not.toContain('undefined');
});
it('No console errors', () => {
expect(global.console.error).toBeCalledTimes(0);
});
});

View file

@ -1,12 +1,29 @@
import * as React from 'react';
import {defineMessages} from 'react-intl';
import {Grid} from '@trussworks/react-uswds';
import {FormattedMessage, useIntl} from 'gatsby-plugin-intl';
import AreasOfFocusList from '../components/areasOfFocusList';
import AlertWrapper from '../components/AlertWrapper';
import J40MainGridContainer from '../components/J40MainGridContainer';
import Layout from '../components/layout';
import AboutCard from '../components/AboutCard/AboutCard';
// @ts-ignore
import aboutUSMapImg from '../images/about-usmap.svg';
// @ts-ignore
import aboutJ40Img from '../images/about-j40.svg';
import accountBalanceIcon // @ts-ignore
from '/node_modules/uswds/dist/img/usa-icons/account_balance.svg';
import groupsIcon from // @ts-ignore
'/node_modules/uswds/dist/img/usa-icons/groups.svg';
import commentIcon from // @ts-ignore
'/node_modules/uswds/dist/img/usa-icons/comment.svg';
import githubIcon from // @ts-ignore
'/node_modules/uswds/dist/img/usa-icons/github.svg';
import AboutCardsContainer from '../components/AboutCard/AboutCardsContainer';
interface IndexPageProps {
location: Location;
@ -16,11 +33,6 @@ interface IndexPageProps {
const IndexPage = ({location}: IndexPageProps) => {
const intl = useIntl();
const messages = defineMessages({
aboutHeader: {
id: 'index.aboutContent.header',
defaultMessage: 'About Justice40',
description: 'h1 header on About page',
},
presidentalLinkUri: {
id: 'index.presidentalLinkUri',
defaultMessage: 'https://www.whitehouse.gov/briefing-room/' +
@ -53,48 +65,50 @@ const IndexPage = ({location}: IndexPageProps) => {
return (
<Layout location={location}>
<J40MainGridContainer fullWidth={true}>
<AlertWrapper/>
</J40MainGridContainer>
<J40MainGridContainer className={'j40-about-page'}>
<J40MainGridContainer>
<Grid row>
<Grid col>
<section className={'usa-prose'}>
<h1>{intl.formatMessage(messages.aboutHeader)}</h1>
<AboutCardsContainer>
<AboutCard
size={'large'}
imgSrc={aboutUSMapImg}
header={'About the screening tool'}>
<p>
<FormattedMessage
id={'index.aboutContent.p1'}
description={'paragraph 1 of main content on index page'}
defaultMessage={`
In an effort to address historical environmental injustices,
President Biden created the Justice40 Initiative on January
27, 2021. The Justice40 Initiative directs 40% of the
benefits from federal investments in seven key areas to
overburdened and underserved communities.
On January 27, 2021, President Biden directed the Council on
Environmental Quality (CEQ) to create a climate and economic
justice screening tool. The purpose of the tool is to provide
socioeconomic, environmental, and climate information and data to
help inform decisions that may affect disadvantaged communities.
The tool is designed to assist Federal agencies in identifying
disadvantaged communities for the purposes of the Justice40
Initiative.
`}/>
</p>
<p>
</AboutCard>
</AboutCardsContainer>
<AboutCardsContainer>
<AboutCard
size={'large'}
imgSrc={aboutJ40Img}
header={'About the Justice40 Initiative'}>
<FormattedMessage
id="index.aboutContent.p2"
description={'paragraph 2 of main content on index page'}
defaultMessage={`
Federal agencies will prioritize benefits using a new
climate and economic justice screening tool. This screening
tool will be a map that visualizes data to compare the
cumulative impacts of environmental, climate, and economic
factors. It is being developed by the Council on
Environmental Quality (CEQ) with guidance from environmental
justice leaders and communities affected by environmental
injustices. The first version of the screening tool will be
released in July 2021. However, the screening tool and data
being used will be continuously updated to better reflect
the lived experiences of community members.
The goal of the Justice40 Initiative is for 40 percent of
benefits of Federal programs in seven key areas to flow to
disadvantaged communities. These seven key areas are: climate
change, clean energy and energy efficiency, clean transit,
affordable and sustainable housing, training and workforce
development, remediation of legacy pollution, and clean water
infrastructure.
`}/>
</p>
<p><FormattedMessage
id={'index.aboutContent.p3'}
@ -113,91 +127,95 @@ const IndexPage = ({location}: IndexPageProps) => {
}}/>
</p>
<h2>
<FormattedMessage
id={'index.section2.header'}
description={'section 2 header'}
defaultMessage={'Areas of Focus'}/>
</h2>
</AboutCard>
</AboutCardsContainer>
<AreasOfFocusList/>
{/* <Grid tablet={{col: true}} gap={'lg'}>*/}
{/* <Grid row className={'j40-aboutcard-sm-card'}>*/}
{/* <Grid col={3} className={'j40-about-image-col'}>*/}
{/* <img*/}
{/* className={'j40-about-large-circle-graphics'}*/}
{/* alt="usa map graphics with pins"*/}
{/* src={aboutUSMapImg}/>*/}
{/* </Grid>*/}
<h2>
<FormattedMessage
id={'index.section3.header'}
description={'section 3 header'}
defaultMessage={'A Transparent, Community-First Approach'}/>
</h2>
{/* <Grid col={9} className={'j40-section-sm-body'}>*/}
{/* <h2>About the screening tool</h2>*/}
{/* */}
{/* </Grid>*/}
{/* </Grid>*/}
<p><FormattedMessage
id={'index.section3.intro'}
description={'section 3 content paragraph 1 intro'}
defaultMessage={`
Successful initiatives are guided by direct input from the
communities they are serving. CEQ commits to transparency,
inclusivity, and iteration in building this screening tool.`}/>
</p>
{/* <Grid row gap="lg" className={'flex-align-center j40-section-sm-body'}>*/}
{/* <Grid col={3} className={'j40-about-image-col'}>*/}
{/* <img*/}
{/* className={'j40-about-large-circle-graphics'}*/}
{/* alt="graphics showing 40%"*/}
{/* src={aboutJ40Img}/>*/}
{/* </Grid>*/}
<p>
<FormattedMessage
id={'index.section3.transparent'}
description={'section 3 content transparent'}
defaultMessage={`
{inlineHeader} The code and data behind the screening
tool are open source, meaning it is available for the public
to review and contribute to. This tool is being developed
publicly so that communities, academic experts, and anyone
whos interested can be involved in the tool-building
process.`}
values={{
inlineHeader:
<i>{intl.formatMessage(messages.transparentLabel)}</i>,
}}/>
</p>
{/* <Grid col={9}>*/}
{/* <h2>About the Justice40 Initiative</h2>*/}
{/* */}
{/* </Grid>*/}
{/* </Grid>*/}
{/* </Grid>*/}
</J40MainGridContainer>
<p>
<FormattedMessage
id={'index.section3.inclusive'}
description={'section 3 content inclusive'}
defaultMessage={`
{inlineHeader} Many areas which lack investments also
lack environmental data and would be overlooked using
available environmental data. CEQ is actively reaching out
to groups that have historically been excluded from
decision-making, such as groups in rural and tribal areas,
to understand their needs and ask for their input.
`}
values={{
inlineHeader:
<i>{intl.formatMessage(messages.inclusiveLabel)}</i>,
}}/>
</p>
<J40MainGridContainer
fullWidth={true}
blueBackground={true}>
<J40MainGridContainer
className={'j40-about-page'}>
<AboutCardsContainer>
<AboutCard
size={'small'}
imgSrc={accountBalanceIcon}
header={'Federal program managers'}
actionText={'Go to data & methodology'}
actionUrl={'./methodology'}>
Download the screening tools draft list of prioritized
communities and information on how to use it for your program in
the future on the data and methodology page.
</AboutCard>
<p>
<FormattedMessage
id={'index.section3.iterative'}
description={'section 3 content iterative'}
defaultMessage={`
{inlineHeader} The initial community prioritization list
provided by the screening tool is the beginning of a
collaborative process in score refinement, rather than a
final answer. CEQ has received recommendations on data sets
from community interviews, the White House Environmental
Justice Advisory Council, and through public comment, but
establishing a score that is truly representative will be a
long-term, ongoing process. As communities submit feedback
and recommendations, CEQ will continue to improve the tools
being built and the processes for stakeholder and public
engagement.
`}
values={{
inlineHeader:
<i>{intl.formatMessage(messages.iterativeLabel)}</i>,
}}/>
</p>
</section>
</Grid>
</Grid>
<AboutCard
size={'small'}
imgSrc={groupsIcon}
header={'Community members'}
actionText={'Explore the tool'}
actionUrl={'./cejst'}>
Find your community or communities that you may be familiar with
and check their prioritization information on the map.
</AboutCard>
</AboutCardsContainer>
</J40MainGridContainer>
</J40MainGridContainer>
<J40MainGridContainer
className={'j40-about-page'}>
<AboutCardsContainer>
<AboutCard
size={'small'}
imgSrc={commentIcon}
header={'Send Feedback'}
actionText={'Email: screeningtool.feedback@usds.gov'}
actionUrl={'mailto:screeningtool.feedback@usds.gov'}>
Have ideas about how to acknowledge the on-the-ground experiences
of your community?
</AboutCard>
<AboutCard
size={'small'}
imgSrc={githubIcon}
header={'Join the open source community'}
actionText={'Check it out on GitHub'}
actionUrl={'https://github.com/usds/justice40-tool'}
actionOpenInNewTab={true}>
The screening tools code is open source, which means it is
available for the public to view and contribute to. Anyone can
view and contribute on GitHub.
</AboutCard>
</AboutCardsContainer>
</J40MainGridContainer>
</Layout>);
};

View file

@ -29,43 +29,12 @@ $theme-font-role-heading: "sans";
// Custom SASS/CSS goes here
$j40-max-width: 80ex;
$primary-color: #112f4e;
$j40-blue-background-color: #EFF6FB;
#main-content {
border-top: 0;
}
.j40-two-column {
overflow: hidden;
padding: 0;
@media (min-width: 40em) {
column-count: 2;
column-gap: 1em;
}
@media (max-width: 40em) {
column-count: 1;
column-gap: 0;
}
}
.j40-two-column > * {
display: inline-flex;
width: 90%;
padding-bottom: 1.2em; /* space between items */
padding-left: 1em;
}
.j40-two-column-icons-spacing {
padding-right: 1em;
width: 2.3em;
max-width: revert;
}
.j40-two-column-confine {
display: inline-flex;
max-width: fit-content;
}
@include at-media("mobile-lg") {
.j40-grid-container {
line-height: 1.5rem;
@ -198,6 +167,25 @@ $primary-color: #112f4e;
}
}
// we can use to to make all section headers consistent
.j40-section-sm-header {
font-weight: bold;
padding-bottom: 0.24rem;
padding-right: 0.24rem;
}
.j40-section-sm-body {
line-height: 1.5;
padding-bottom: 1.2rem;
padding-right: 1.2rem;
}
.j40-section-sm-footer {
padding-bottom: 0.5rem;
}
// This should really be part of uswds and use $theme-step-indicator-segment-color-complete
// The Progress element doesn't really support color changing the line connecting progress
// (like the Steps element does) and the checkbox.
@ -337,6 +325,7 @@ $primary-color: #112f4e;
list are not affected (e.g. <ul><li><p><ul><li>not styled</li></ul></p></li><ul>*/
.j40-process-list-wrapper > {
$j40-steps-list-color: #002D3F;
ul {
margin-top: 3rem;
margin-bottom: 1rem;
@ -424,3 +413,66 @@ ul.j40-process-nested-list {
margin-block-end: auto;
font-style: italic;
}
/* this is used by J40MainGridContainer to show a blue background */
.j40-main-grid-blue-bk {
background-color: $j40-blue-background-color;
}
/* about card - based on datasetCard... need to combine */
.j40-aboutcard-container {
.j40-aboutcard-lg-card {
margin: 1.2rem 0 3rem 0;
.j40-aboutpage-image-container {
width: 10.5rem;
padding: 1.24rem 1.24rem 0 .24rem;
}
.j40-aboutcard-image {
flex: 1 0 10%;
align-self: flex-start;
}
.j40-aboutcard-header {
}
.j40-aboutcard-body {
}
.j40-aboutcard-footer {
}
.j40-aboutcard-link {
font-weight: bold;
}
}
.j40-aboutcard-sm-card {
margin: 1.2rem 0 3rem 0;
.j40-aboutpage-image-container {
padding: 1.24rem 1.24rem 0 .24rem;
}
.j40-aboutcard-image {
width: 3rem;
}
.j40-aboutcard-header {
}
.j40-aboutcard-body {
}
.j40-aboutcard-footer {
}
.j40-aboutcard-link {
font-weight: bold;
}
}
}