Modifies ExploreTool page to match sprint 4 design (#481)

* initial commit of sprint 4 explore page

* adds styling on HowYouCanHelp module

* troubleshooting li element on deployed URL

removing local bullet styles

* removing unused styles

* recreating HowYouCanHelp

* explicit list el styles

* adds bullets back in

* fixes tooltip style and alert padding

* componentize MapLegend

* fix links

* inital intl and unit tests

* adds trusswork tooltip for comparison

* updates based on various feedback and disucssions:

- removes react-tooltip
- placeholder trussworks tooltip
- removes download packet component
- intl on HowYouCanHelp
- updates MapLegend tests
- add initial cy test on ExploreTool page

* removes bold on alert

* PR feedback:

- removes location from J40Alert
- localizes `COLOR KEY`

* adds intl to constants file

* modifies download zip URL to new S3 location

* removes location depedencies on Alerts

* add localization for HowYouCanHelp
This commit is contained in:
Vim 2021-08-10 09:45:45 -07:00 committed by GitHub
commit 174a0e1330
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 974 additions and 426 deletions

View file

@ -2,20 +2,22 @@
describe('Census Block Group download', () => { describe('Census Block Group download', () => {
it('validate file download', () => { it('validate file download', () => {
const filename = `usa.zip`; // const filename = `usa.zip`;
cy.visit('localhost:8000/en/cejst'); cy.visit('localhost:8000/en/cejst');
cy.get('#download-link').invoke('attr', 'target', '_blank');
cy.intercept(`https://justice40-data.s3.amazonaws.com/Score/${filename}`, // Todo VS: Download packet component is being moved. Will be re-enabled with
{ // cy.get('#download-link').invoke('attr', 'target', '_blank');
body: 'success', // cy.intercept(`https://justice40-data.s3.amazonaws.com/Score/${filename}`,
headers: { // {
'Content-Type': 'text/html; charset=utf-8', // body: 'success',
'Content-Disposition': 'attachment', // headers: {
}, // 'Content-Type': 'text/html; charset=utf-8',
}, // 'Content-Disposition': 'attachment',
).as('downloadRequest'); // },
cy.get('button[class*="downloadPacket"]').click(); // },
cy.wait('@downloadRequest'); // ).as('downloadRequest');
cy.readFile(`cypress/downloads/${filename}`).should('exist'); // cy.get('button[class*="downloadPacket"]').click();
// cy.wait('@downloadRequest');
// cy.readFile(`cypress/downloads/${filename}`).should('exist');
}); });
}); });

View file

@ -0,0 +1,13 @@
// / <reference types="Cypress" />
describe('tests links on Explore Tool Page', () => {
it('validate file download', () => {
cy.visit('localhost:8000/en/cejst');
// Checks to make sure all a tags have an href:
cy.get('a').each(($a) => {
const message = $a.text();
expect($a, message).to.have.attr('href').not.contain('undefined');
});
});
});

View file

@ -24,7 +24,7 @@
"deploy": "gatsby build --prefix-paths && gh-pages -d public", "deploy": "gatsby build --prefix-paths && gh-pages -d public",
"lint": "eslint './src/**/*.{ts,tsx}' --ignore-pattern node_modules/ --ignore-pattern public --ignore-pattern *scss.d.ts", "lint": "eslint './src/**/*.{ts,tsx}' --ignore-pattern node_modules/ --ignore-pattern public --ignore-pattern *scss.d.ts",
"lint:fix": "npm run lint -- --quiet --fix", "lint:fix": "npm run lint -- --quiet --fix",
"intl:extract": "formatjs extract 'src/(pages|components)/*.tsx' --out-file src/intl/en.json", "intl:extract": "formatjs extract 'src/**/*.tsx' --out-file src/intl/en.json",
"intl:compile-en": "formatjs compile src/intl/en.json --ast --out-file compiled-lang/en.json" "intl:compile-en": "formatjs compile src/intl/en.json --ast --out-file compiled-lang/en.json"
}, },
"devDependencies": { "devDependencies": {

View file

@ -0,0 +1,11 @@
.alertWrapper {
margin-bottom: 0;
}
.alertHide {
display: none;
}
.alertWarning {
margin-top: 0;
}

View file

@ -0,0 +1,14 @@
declare namespace AlertWrapperScssNamespace {
export interface IAlertWrapperScss {
alertWrapper: string;
alertHide: string;
alertWarning: string;
}
}
declare const AlertWrapperScssModule: AlertWrapperScssNamespace.IAlertWrapperScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: AlertWrapperScssNamespace.IAlertWrapperScss;
};
export = AlertWrapperScssModule;

View file

@ -0,0 +1,41 @@
import React from 'react';
import {Alert} from '@trussworks/react-uswds';
import {FormattedMessage} from 'gatsby-plugin-intl';
import * as styles from './alertWrapper.module.scss';
interface IAlertWrapperProps {
hideWarningAlert?: boolean
}
const AlertWrapper = ({hideWarningAlert}:IAlertWrapperProps) => {
return (
<div className={styles.alertWrapper}>
<Alert className={'j40-sitealert'} type="info">
<span className={'j40-sitealert-title'}><FormattedMessage
id='header.alertTitleBeta'
description={'Alerts that appear on every page - title'}
defaultMessage={`Public beta`}/> - </span>
<span className={'j40-sitealert-body'}>
<FormattedMessage
id='header.alertBodyBeta'
description={'Alerts that appear on every page'}
defaultMessage={`This website will be continuously updated`}/>
</span>
<br/>
</Alert>
<Alert
className={`j40-sitealert' ${hideWarningAlert ? styles.alertHide : null} ${styles.alertWarning}`}
type="warning">
<b>Limited data sources </b>
This tool currently includes 16 datasets. Over time, datasets could be
added, updated, or removed. The datasets come from a variety of sources
based on availability, quality, and relevance to environmental, energy,
and climate issues. Each dataset has limitations, such as how recently
the data was updated.
</Alert>
</div>
);
}; ;
export default AlertWrapper;

View file

@ -4,11 +4,10 @@ import {defineMessages} from 'react-intl';
import * as styles from './datasetCard.module.scss'; import * as styles from './datasetCard.module.scss';
interface IDatasetCardProps { interface IDatasetCardProps {
key: number,
datasetCardProps: { [key:string]: string } datasetCardProps: { [key:string]: string }
} }
const DatasetCard = ({key, datasetCardProps}:IDatasetCardProps) => { const DatasetCard = ({datasetCardProps}:IDatasetCardProps) => {
const intl = useIntl(); const intl = useIntl();
const messages = defineMessages({ const messages = defineMessages({
whatIsIt: { whatIsIt: {
@ -33,11 +32,6 @@ const DatasetCard = ({key, datasetCardProps}:IDatasetCardProps) => {
}, },
}); });
// Todo VS: figure out how to ignore unused variables such as keys
// tried tsconfig, no-unused-vars, @typescript-eslint/no-unused-vars
// Also check associated unit test warning.
console.log(key);
return ( return (
<div className={styles.datasetCard}> <div className={styles.datasetCard}>
<h3 className={styles.datasetCardIndicator}>{datasetCardProps.indicator}</h3> <h3 className={styles.datasetCardIndicator}>{datasetCardProps.indicator}</h3>

View file

@ -1,7 +1,5 @@
@import "../utils.scss"; @import "../utils.scss";
$headingFontColor: #122e51;
.datasetContainer { .datasetContainer {
background-color: #eef6fb; background-color: #eef6fb;
} }
@ -30,5 +28,5 @@ $headingFontColor: #122e51;
} }
.j40AlertContainer { .j40AlertContainer {
background-color: #faf3d1; background-color: $j40AlertWarningColor;
} }

View file

@ -83,7 +83,7 @@ const DatasetContainer = () => {
<h1 className={styles.datasetContainerHeader}>{intl.formatMessage(messages.cumulativeScore)}</h1> <h1 className={styles.datasetContainerHeader}>{intl.formatMessage(messages.cumulativeScore)}</h1>
<p className={styles.datasetContainerSubTitle}>{intl.formatMessage(messages.subTitle)}</p> <p className={styles.datasetContainerSubTitle}>{intl.formatMessage(messages.subTitle)}</p>
<div className={styles.datasetCardsContainer}> <div className={styles.datasetCardsContainer}>
{cards.map((card, index) => <DatasetCard key={index} datasetCardProps={card}/>)} {cards.map((card) => <DatasetCard key={card.indicator} datasetCardProps={card}/>)}
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,3 +0,0 @@
.howYouCanHelpContainer {
margin: 29px 24px 49px 42px;
}

View file

@ -1,19 +0,0 @@
import React from 'react';
import * as styles from './HowYouCanHelp.module.scss';
const HowYouCanHelp = () => {
return (
<div className={styles.howYouCanHelpContainer}>
<h2>How You Can Help Improve the Tool</h2>
<ul className={'usa-list'}>
<li>If you have information that could help, wed love to hear from you.</li>
<li>View our full set of data sources and methodology
where you can add or download sources and check statuses on our data roadmap.</li>
<li>Check out our timeline and send feedback or attend relevant events.</li>
<li>Contact us and share the stories of your community.</li>
</ul>
</div>
);
};
export default HowYouCanHelp;

View file

@ -0,0 +1,24 @@
@import "../utils.scss";
.howYouCanHelpContainer {
margin: 2rem 0;
.howYouCanHelpText {
color: $headingFontColor;
padding-left: 1rem;
}
}
.howYouCanHelpHeader {
color: $headingFontColor;
font-weight: normal;
}
.howYouCanHelpList {
list-style-type: disc;
padding-bottom: 1rem;
}
.howYouCanHelpListWrapper {
padding-left: 2rem;
}

View file

@ -1,6 +1,12 @@
declare namespace HowYouCanHelpModuleScssNamespace { declare namespace HowYouCanHelpModuleScssNamespace {
export interface IHowYouCanHelpModuleScss { export interface IHowYouCanHelpModuleScss {
howYouCanHelpContainer: string; howYouCanHelpContainer: string;
howYouCanHelpHeader: string;
howYouCanHelpBullet: string,
listWrapper: string;
howYouCanHelpText: string;
howYouCanHelpList: string;
howYouCanHelpListWrapper: string;
} }
} }

View file

@ -0,0 +1,95 @@
import React from 'react';
import {Link} from 'gatsby-plugin-intl';
import {useIntl} from 'gatsby-plugin-intl';
import {defineMessages} from 'react-intl';
import * as styles from './howYouCanHelp.module.scss';
const HowYouCanHelp = () => {
const intl = useIntl();
const messages = defineMessages({
youCanHelpHeader: {
id: 'howYouCanHelp.header.text',
defaultMessage: 'How You Can Help Improve the Tool',
description: 'the header of the how you can help section',
},
youCanHelpInfoText: {
id: 'youCanHelpInfoText.list.element.prefix',
defaultMessage: 'If you have helpful information, wed love to',
description: 'you can help info text ',
},
youCanHelpInfoLinkText: {
id: 'youCanHelpInfoLink.link.text',
defaultMessage: 'get an email from you',
description: 'you can help info text ',
},
youCanHelpDataMethPrefixText: {
id: 'youCanHelpDataMethPrefixText.link.prefix.text',
defaultMessage: 'View our',
description: 'view our',
},
youCanHelpDataMethLinkText: {
id: 'youCanHelpDataMethLinkText.link.text',
defaultMessage: 'Data & methodology',
description: 'Data & methodology link',
},
youCanHelpDataMethSuffixText: {
id: 'youCanHelpDataMethSuffixText.link.suffix.text',
defaultMessage: 'and send us feedback',
description: 'send us feedbackv via email',
},
youCanHelpSharingPrefixText: {
id: 'youCanHelpSharingPrefixText.link.prefix.text',
defaultMessage: 'Find your community and',
description: 'find your community',
},
youCanHelpSharingLinkText: {
id: 'youCanHelpSharingLinkText.link.text',
defaultMessage: 'share your feedback',
description: 'sharing link to email',
},
});
return (
<div className={styles.howYouCanHelpContainer}>
<h2 className={styles.howYouCanHelpHeader}>
{intl.formatMessage(messages.youCanHelpHeader)}
</h2>
<ul className={styles.howYouCanHelpListWrapper}>
<li className={styles.howYouCanHelpList}>
<div className={styles.howYouCanHelpText}>
{intl.formatMessage(messages.youCanHelpInfoText)}
{` `}
<a href={'mailto:screeningtool.feedback@usds.gov'}>
{intl.formatMessage(messages.youCanHelpInfoLinkText)}
</a>
{` `}.
</div>
</li>
<li className={styles.howYouCanHelpList}>
<div className={styles.howYouCanHelpText}>
{intl.formatMessage(messages.youCanHelpDataMethPrefixText)}
{` `}
<Link to={'/methodology'}>
{intl.formatMessage(messages.youCanHelpDataMethLinkText)}
</Link>
{` `}
{intl.formatMessage(messages.youCanHelpDataMethSuffixText)}
</div>
</li>
<li className={styles.howYouCanHelpList}>
<div className={styles.howYouCanHelpText}>
{intl.formatMessage(messages.youCanHelpSharingPrefixText)}
{` `}
<a href={'mailto:screeningtool.feedback@usds.gov'}>
{intl.formatMessage(messages.youCanHelpSharingLinkText)}
</a>
{` `}.
</div>
</li>
</ul>
</div>
);
};
export default HowYouCanHelp;

View file

@ -0,0 +1,46 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the HowYouCanHelp checks if various text fields are visible 1`] = `
<DocumentFragment>
<div>
<h2>
How You Can Help Improve the Tool
</h2>
<ul>
<li>
<div>
If you have helpful information, wed love to
<a
href="mailto:screeningtool.feedback@usds.gov"
>
get an email from you
</a>
.
</div>
</li>
<li>
<div>
View our
<a
href="/en/methodology"
>
Data & methodology
</a>
and send us feedback.
</div>
</li>
<li>
<div>
Find your community and
<a
href="mailto:screeningtool.feedback@usds.gov"
>
share your feedback
</a>
.
</div>
</li>
</ul>
</div>
</DocumentFragment>
`;

View file

@ -0,0 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../../test/testHelpers';
import HowYouCanHelp from '../index';
describe('rendering of the HowYouCanHelp', () => {
const {asFragment} = render(
<LocalizedComponent>
<HowYouCanHelp />
</LocalizedComponent>,
);
it('checks if various text fields are visible', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -3,7 +3,13 @@ import {useIntl} from 'gatsby-plugin-intl';
import {defineMessages} from 'react-intl'; import {defineMessages} from 'react-intl';
import * as styles from './j40Alert.module.scss'; import * as styles from './j40Alert.module.scss';
const J40Alert = () => { // This prop follows an inversion of control pattern allowing the user of this component to specify
// how it's rendered. See more here: https://kentcdodds.com/blog/inversion-of-control
interface IJ40AlertProps {
alertStyle?: {[key:string]: string};
}
const J40Alert = ({alertStyle}:IJ40AlertProps) => {
const intl = useIntl(); const intl = useIntl();
const messages = defineMessages({ const messages = defineMessages({
alertMsg: { alertMsg: {
@ -13,7 +19,7 @@ const J40Alert = () => {
}, },
}); });
return ( return (
<div className={styles.j40Alert}> <div className={styles.j40Alert} style={alertStyle}>
{intl.formatMessage(messages.alertMsg)} {intl.formatMessage(messages.alertMsg)}
</div> </div>
); );

View file

@ -1,5 +1,13 @@
.j40Alert { @import "../utils.scss";
@mixin j40AlertBase {
font-size: large; font-size: large;
font-weight: 600; background-color: $j40AlertWarningColor;
padding: 1rem 0; margin-top: 2rem;
padding-top: 1rem;
padding-bottom: 1rem;
}
.j40Alert {
@include j40AlertBase;
} }

View file

@ -1,6 +1,7 @@
declare namespace J40AlertScssNamespace { declare namespace J40AlertScssNamespace {
export interface IJ40AlertScss { export interface IJ40AlertScss {
j40Alert: string; j40Alert: string;
j40AlertLeftPad: string;
} }
} }

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the J40Alert checks if various text fields are visible 1`] = ` exports[`rendering of the J40Alert tests the rendering of J40Alert 1`] = `
<DocumentFragment> <DocumentFragment>
<div> <div>
Limited data sources — Datasets may be added, updated, or removed. Limited data sources — Datasets may be added, updated, or removed.

View file

@ -1,16 +1,16 @@
import * as React from 'react'; import * as React from 'react';
import {render} from '@testing-library/react'; import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../../test/testHelpers'; import {LocalizedComponent} from '../../../test/testHelpers';
import J40Alert from '../../J40Alert'; import J40Alert from '../index';
describe('rendering of the J40Alert', () => { describe('rendering of the J40Alert', () => {
const {asFragment} = render( it('tests the rendering of J40Alert', () => {
<LocalizedComponent> const {asFragment} = render(
<J40Alert /> <LocalizedComponent>
</LocalizedComponent>, <J40Alert />
); </LocalizedComponent>,
);
it('checks if various text fields are visible', () => {
expect(asFragment()).toMatchSnapshot(); expect(asFragment()).toMatchSnapshot();
}); });
}); });

View file

@ -1,7 +1,6 @@
import React, {useState} from 'react'; import React, {useState} from 'react';
import {FormattedMessage, Link, useIntl} from 'gatsby-plugin-intl'; import {Link, useIntl} from 'gatsby-plugin-intl';
import { import {
Alert,
Header, Header,
NavMenuButton, NavMenuButton,
PrimaryNav, PrimaryNav,
@ -11,11 +10,7 @@ import {defineMessages} from 'react-intl';
// @ts-ignore // @ts-ignore
import siteLogo from '../../src/images/icon.png'; import siteLogo from '../../src/images/icon.png';
interface IJ40HeaderProps { const J40Header = () => {
location: Location
}
const J40Header = ({location}:IJ40HeaderProps) => {
const isMethodologyPage = location.pathname.match(/methodology\/?/);
const intl = useIntl(); const intl = useIntl();
const [mobileNavOpen, setMobileNavOpen] = useState(false); const [mobileNavOpen, setMobileNavOpen] = useState(false);
const messages = defineMessages({ const messages = defineMessages({
@ -119,29 +114,6 @@ const J40Header = ({location}:IJ40HeaderProps) => {
</PrimaryNav> </PrimaryNav>
</div> </div>
</Header> </Header>
<Alert className={'j40-sitealert'} type="info">
<span className={'j40-sitealert-title'}><FormattedMessage
id='header.alertTitleBeta'
description={'Alerts that appear on every page - title'}
defaultMessage={`Public beta`}/> - </span>
<span className={'j40-sitealert-body'}>
<FormattedMessage
id='header.alertBodyBeta'
description={'Alerts that appear on every page'}
defaultMessage={`This website will be continuously updated`}/>
</span>
<br/>
</Alert>
{!isMethodologyPage && <Alert
className={'j40-sitealert'}
type="warning">
<b>Limited data sources </b>
This tool currently includes 16 datasets. Over time, datasets could be
added, updated, or removed. The datasets come from a variety of sources
based on availability, quality, and relevance to environmental, energy,
and climate issues. Each dataset has limitations, such as how recently
the data was updated.
</Alert>}
</> </>
); );
}; };

View file

@ -0,0 +1,117 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import {Tooltip} from '@trussworks/react-uswds';
import * as styles from './mapLegend.module.scss';
import * as constants from '../../data/constants';
// @ts-ignore
import infoIcon from '/node_modules/uswds/dist/img/usa-icons/info_outline.svg';
// Todo VS: This information will be used in the re-design of the tool-tip
// const getToolTipContent = (type:string) => {
// const intl = useIntl();
// const messages = defineMessages({
// priorityHeader: {
// id: 'tooltip.info.priority.header',
// defaultMessage: constants.PRIORITIZED_COMMUNITY,
// description: 'the header of the prioritized community tooltip',
// },
// thresholdHeader: {
// id: 'tooltip.info.threshold.header',
// defaultMessage: constants.THRESHOLD_COMMUNITY,
// description: 'the header of the threshold community tooltip',
// },
// priorityText: {
// id: 'tooltip.info.priority.text',
// defaultMessage: 'A prioritized community is one that has a cumulative index score of Xth ' +
// 'percentile and above. 40% of the benefits from investments outlined by the ' +
// 'Justice40 Initiative should go to prioritized communities.',
// description: 'the text of the prioritized community tooltip',
// },
// thresholdText: {
// id: 'tooltip.info.threshold.text',
// defaultMessage: 'Communities with a cumulative index score between Y - X.99th percentile are ' +
// 'considered threshold communities. While these communities are currently not considered a ' +
// 'prioritized community, this may change based on updates to the scoring method.',
// description: 'the text of the threshold community tooltip',
// },
// });
// return (type === 'prioritized') ?
// (
// <div>
// <h2>{intl.formatMessage(messages.priorityHeader)}</h2>
// <p className={styles.legendTooltipText}>{intl.formatMessage(messages.priorityText)}</p>
// </div>
// ) :
// (
// <div>
// <h2>{intl.formatMessage(messages.thresholdHeader)}</h2>
// <p className={styles.legendTooltipText}>{intl.formatMessage(messages.thresholdText)}</p>
// </div>
// );
// };
const MapLegend = () => {
const intl = useIntl();
// Type definitions required for @trussworks tooltip. This type defines the div that wraps the icon.
// This allows to pass children and other attributes.
type IconWrapperProps = React.PropsWithChildren<{
className?: string
}> &
JSX.IntrinsicElements['div'] &
React.RefAttributes<HTMLDivElement>
const IconWrapper: React.ForwardRefExoticComponent<IconWrapperProps> = React.forwardRef(
({className, children, ...tooltipProps}: IconWrapperProps, ref) => (
<div ref={ref} className={styles.infoIconWrapper} {...tooltipProps}>
{children}
</div>
),
);
IconWrapper.displayName = 'custom info wrapper';
return (
<div className={styles.legendContainer}>
<h3 className={styles.legendHeader}>{intl.formatMessage(constants.EXPLORE_TOOL_PAGE_TEXT.LEGEND_LABEL)}</h3>
<div className={styles.swatchContainer}>
<div className={styles.legendItem}>
<div className={styles.colorSwatch} id={styles.prioritized} />
<span>{intl.formatMessage(constants.EXPLORE_TOOL_PAGE_TEXT.PRIORITY_LABEL)}</span>
{/* Using @trussworks tooltip */}
<Tooltip<IconWrapperProps>
label={`
Communities that have cumulative
index score of Xth percentile
and above
`}
position='left'
asCustom={IconWrapper}>
<img className={styles.infoIcon} src={infoIcon} />
</Tooltip>
</div>
<div className={styles.legendItem}>
<div className={styles.colorSwatch} id={styles.threshold} />
<span>{intl.formatMessage(constants.EXPLORE_TOOL_PAGE_TEXT.THRESHOLD_LABEL)}</span>
{/* Using @trussworks tooltip */}
<Tooltip<IconWrapperProps>
label={`
Communities with a cumulative
index score between Y - X.99th
percentile
`}
position='left'
asCustom={IconWrapper}>
<img className={styles.infoIcon} src={infoIcon} />
</Tooltip>
</div>
</div>
</div>
);
};
export default MapLegend;

View file

@ -0,0 +1,96 @@
@import "../utils.scss";
$min-color: #fafaf8;
$med-color: rgba(26, 68, 128, 0.2);
$max-color: rgba(26, 68, 128, 0.6);
$alertInfoColor: #e7f6f8;
.legendContainer {
margin: 1rem 1.2rem 1rem 2.5rem;
font-size: 0.8em;
border: 1px solid #8c9297;
padding: 0 1.8rem;
flex: 1;
color: $headingFontColor;
.legendTooltipText {
max-width: 14rem;
font-size: medium;
}
@media screen and (max-width: $mobileBreakpoint) {
margin: 1rem 0;
}
}
.legendHeader {
margin-bottom: 0;
}
.swatchContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
@media screen and (max-width: $mobileBreakpoint) {
flex-wrap: wrap;
}
}
.legendItem {
display: flex;
flex-direction: row;
align-items: center;
span {
font-size: medium;
}
.infoIconWrapper {
margin-left: 0.8rem;
.infoIcon {
width: 2rem;
height: 2rem;
margin-top: 0.4rem;
}
}
@media screen and (max-width: $mobileBreakpoint) {
padding-left: 3rem;
}
}
.colorSwatch {
box-sizing: border-box;
height: 1.7rem;
width: 1.7rem;
margin-right: 10px;
border-radius: 50%;
}
#prioritized {
background-color: $max-color;
}
#threshold {
background-color: $med-color;
}
#nonPrioritized {
background-color: $min-color;
}
.legendTooltipTheme {
color: $headingFontColor !important;
background-color: white !important;
opacity: 1 !important;
border: $sidePanelBorder !important;
box-shadow: 0 0 11px rgba(33, 33, 33, 0.2) !important;
&.place-top {
&:after {
border-top-color: white !important;
border-top-style: solid !important;
border-top-width: 6px !important;
}
}
}

View file

@ -7,7 +7,11 @@ declare namespace HowYouCanHelpModuleScssNamespace {
prioritized: string, prioritized: string,
threshold: string, threshold: string,
nonPrioritized: string, nonPrioritized: string,
legendItem: string legendItem: string,
infoIcon: string,
legendTooltipText: string,
legendTooltipTheme: string,
infoIconWrapper: string,
} }
} }

View file

@ -0,0 +1,25 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../../test/testHelpers';
import MapLegend from '../index';
import * as constants from '../../../data/constants';
describe('rendering of the MapLegend', () => {
const {getAllByText} = render(
<LocalizedComponent>
<MapLegend />
</LocalizedComponent>,
);
// Snapshot testing was unusable as the Tooltip lib rendered hash based class
// names on each render
const intlPriorityLabel = constants.EXPLORE_TOOL_PAGE_TEXT.PRIORITY_LABEL.defaultMessage;
const intlThresholdLabel = constants.EXPLORE_TOOL_PAGE_TEXT.THRESHOLD_LABEL.defaultMessage;
it('checks if various objects in the component rendered', () => {
expect(getAllByText(intlPriorityLabel)[0]).toHaveTextContent(intlPriorityLabel);
expect(getAllByText(intlThresholdLabel)[0]).toHaveTextContent(intlThresholdLabel);
});
});

View file

@ -0,0 +1,44 @@
import * as React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import {defineMessages} from 'react-intl';
import J40Alert from '../J40Alert';
import J40Map from '../J40Map';
import * as styles from './mapWrapper.module.scss';
import * as constants from '../../data/constants';
interface IMapWrapperProps {
location: Location
}
const MapWrapper = ({location}: IMapWrapperProps) => {
const intl = useIntl();
const messages = defineMessages({
downloadLinkText: {
id: 'mapwrapper.download.link',
defaultMessage: 'Download the draft list of prioritized communities (pre-decisional) and datasets used',
description: 'download link for datasets',
},
downloadContents: {
id: 'mapwrapper.download.contents',
defaultMessage: 'ZIP file will contain one .xlsx, one .csv and one .pdf (30 MB).',
description: 'download link contents',
},
});
return (
<>
<J40Alert alertStyle={{'padding-left': '1rem'}}/>
<J40Map location={location}/>
<div className={styles.mapCaptionTextLink}>
<a href={constants.DOWNLOAD_ZIP_URL}>
{intl.formatMessage(messages.downloadLinkText)}
</a>
</div>
<div>{intl.formatMessage(messages.downloadContents)}</div>
</>
);
};
export default MapWrapper;

View file

@ -0,0 +1,4 @@
.mapCaptionTextLink {
padding-top: 1rem;
padding-bottom: 0.4rem;
}

View file

@ -0,0 +1,13 @@
declare namespace MapWrapperScssNamespace {
export interface IMapWrapperScss {
mapCaptionTextLink: string;
}
}
declare const MapWrapperScssModule: MapWrapperScssNamespace.IMapWrapperScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: MapWrapperScssNamespace.IMapWrapperScss;
};
export = MapWrapperScssModule;

View file

@ -1,10 +1,9 @@
import React from 'react'; import React from 'react';
import {Button} from '@trussworks/react-uswds'; import {Button} from '@trussworks/react-uswds';
import * as styles from './downloadPacket.module.scss';
// @ts-ignore // @ts-ignore
import downloadIcon from '/node_modules/uswds/dist/img/usa-icons/file_download.svg'; import downloadIcon from '/node_modules/uswds/dist/img/usa-icons/file_download.svg';
import * as styles from './downloadPacket.module.scss';
export const cbgFileURL = 'https://justice40-data.s3.amazonaws.com/Score/usa.zip'; import * as constants from '../data/constants';
const DownloadPacket = () => { const DownloadPacket = () => {
return ( return (
@ -16,7 +15,7 @@ const DownloadPacket = () => {
prioritized communities (30,021 census block groups) and 18 datasets. prioritized communities (30,021 census block groups) and 18 datasets.
</div> </div>
<div className={styles.downloadBoxButtonContainer}> <div className={styles.downloadBoxButtonContainer}>
<a id={'download-link'} href={cbgFileURL}> <a id={'download-link'} href={constants.DOWNLOAD_ZIP_URL}>
<Button className={styles.downloadBoxButton} type="button"> <Button className={styles.downloadBoxButton} type="button">
<div><img src={downloadIcon} /> </div> <div><img src={downloadIcon} /> </div>
<div className={styles.downloadPacketText}>Download packet</div> <div className={styles.downloadPacketText}>Download packet</div>

View file

@ -12,7 +12,7 @@ const Layout = ({children, location}: ILayoutProps) => {
// @ts-ignore // @ts-ignore
return ( return (
<URLFlagProvider location={location}> <URLFlagProvider location={location}>
<J40Header location={location}/> <J40Header />
<main id={'main-content'}> <main id={'main-content'}>
{children} {children}
</main> </main>

View file

@ -1,41 +0,0 @@
$min-color: #fafaf8;
$med-color: rgba(26, 68, 128, 0.2);
$max-color: rgba(26, 68, 128, 0.6);
.legendContainer {
margin-top: 19px;
font-size: 0.8em;
}
.swatchContainer {
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.legendItem {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 29px;
}
.colorSwatch {
box-sizing: border-box;
height: 20px;
width: 20px;
border: 1px solid #1a4480;
margin-right: 10px;
}
#prioritized {
background-color: $max-color;
}
#threshold {
background-color: $med-color;
}
#nonPrioritized {
background-color: $min-color;
}

View file

@ -1,26 +0,0 @@
import React from 'react';
import * as styles from './mapLegend.module.scss';
const MapLegend = () => {
return (
<div className={styles.legendContainer}>
<h3 className={styles.legendHeader}>COLOR KEY</h3>
<div className={styles.swatchContainer}>
<div className={styles.legendItem}>
<div className={styles.colorSwatch} id={styles.prioritized} />
<span>Prioritized Community</span>
</div>
<div className={styles.legendItem}>
<div className={styles.colorSwatch} id={styles.threshold} />
<span>Threshold Community</span>
</div>
<div className={styles.legendItem}>
<div className={styles.colorSwatch} id={styles.nonPrioritized} />
<span>Non-Prioritized Community</span>
</div>
</div>
</div>
);
};
export default MapLegend;

View file

@ -1,20 +0,0 @@
import * as React from 'react';
import J40Map from './J40Map';
import MapLegend from '../components/mapLegend';
interface IMapWrapperProps {
location: Location
}
const MapWrapper = ({location}: IMapWrapperProps) => {
return (
<>
<h2>Explore the Tool</h2>
<J40Map location={location}/>
<MapLegend />
</>
);
};
export default MapWrapper;

View file

@ -12,3 +12,5 @@ $mobileBreakpoint: 400px;
//Styles with Dataset container //Styles with Dataset container
$datasetContainerColor: #eef6fb; $datasetContainerColor: #eef6fb;
$headingFontColor: #122e51;
$j40AlertWarningColor: #faf3d1;

View file

@ -1,7 +1,9 @@
import {LngLatBoundsLike} from 'maplibre-gl'; import {LngLatBoundsLike} from 'maplibre-gl';
import {isMobile as isMobileReactDeviceDetect} from 'react-device-detect'; import {isMobile as isMobileReactDeviceDetect} from 'react-device-detect';
import {defineMessages} from 'react-intl';
// URLS // URLS
export const DOWNLOAD_ZIP_URL = 'https://justice40-data.s3.amazonaws.com/data-pipeline/data/score/downloadable/Screening+Tool+Data.zip';
export const FEATURE_TILE_BASE_URL = 'https://d2zjid6n5ja2pt.cloudfront.net'; export const FEATURE_TILE_BASE_URL = 'https://d2zjid6n5ja2pt.cloudfront.net';
const XYZ_SUFFIX = '{z}/{x}/{y}.pbf'; const XYZ_SUFFIX = '{z}/{x}/{y}.pbf';
export const featureURLForTilesetName = (tilesetName :string ) : string => { export const featureURLForTilesetName = (tilesetName :string ) : string => {
@ -114,4 +116,24 @@ export const SCORE_BOUNDARY_LOW = 0.0;
export const SCORE_BOUNDARY_THRESHOLD = 0.6; export const SCORE_BOUNDARY_THRESHOLD = 0.6;
export const SCORE_BOUNDARY_PRIORITIZED = 0.75; export const SCORE_BOUNDARY_PRIORITIZED = 0.75;
// Explore the Tool:
export const EXPLORE_TOOL_PAGE_TEXT = defineMessages({
PRIORITY_LABEL: {
id: 'legend.info.priority.label',
defaultMessage: 'Prioritized community',
description: 'the label of the prioritized community legend',
},
THRESHOLD_LABEL: {
id: 'legend.info.threshold.label',
defaultMessage: 'Threshold community',
description: 'the label of the threshold community legend',
},
LEGEND_LABEL: {
id: 'legend.colorkey.label',
defaultMessage: 'COLOR KEY',
description: 'the label of the key in the legend',
},
});
export const isMobile = isMobileReactDeviceDetect; export const isMobile = isMobileReactDeviceDetect;

View file

@ -95,6 +95,34 @@
"defaultMessage": "Email us", "defaultMessage": "Email us",
"description": "Heading for page to allow users to contact project maintainers" "description": "Heading for page to allow users to contact project maintainers"
}, },
"datasetAlert.header.alertMsg": {
"defaultMessage": "Limited data sources — Datasets may be added, updated, or removed.",
"description": "an alert message to inform users that datasets may change"
},
"datasetCard.dataDateRange": {
"defaultMessage": "Data date range:",
"description": "label associated with explaining the card"
},
"datasetCard.dataResolution": {
"defaultMessage": "Data resolution:",
"description": "label associated with explaining the card"
},
"datasetCard.dataSource": {
"defaultMessage": "Data source:",
"description": "label associated with explaining the card"
},
"datasetCard.whatIsIt": {
"defaultMessage": "What is it?",
"description": "label associated with explaining the card"
},
"datasetContainer.header.cumulativeScore": {
"defaultMessage": "Datasets used in cumulative score",
"description": "section label of which datasets are used in cumulative score"
},
"datasetContainer.subTitle": {
"defaultMessage": "The datasets come from a variety of sources and were selected after considering relevance, availability, recency and quality.",
"description": "description of the dataset section"
},
"footer.arialabel": { "footer.arialabel": {
"defaultMessage": "Footer navigation", "defaultMessage": "Footer navigation",
"description": "aria-label text for whole footer" "description": "aria-label text for whole footer"
@ -129,7 +157,7 @@
}, },
"header.about": { "header.about": {
"defaultMessage": "About", "defaultMessage": "About",
"description": "Navigate to the about page" "description": "Header navigate item to the about page"
}, },
"header.alertBodyBeta": { "header.alertBodyBeta": {
"defaultMessage": "This website will be continuously updated", "defaultMessage": "This website will be continuously updated",
@ -141,27 +169,27 @@
}, },
"header.contact": { "header.contact": {
"defaultMessage": "Contact", "defaultMessage": "Contact",
"description": "Navigate to the Contact page" "description": "Header navigate item to the Contact page"
}, },
"header.explore": { "header.explore": {
"defaultMessage": "Explore the tool", "defaultMessage": "Explore the tool",
"description": "Navigate to the Explore the tool page" "description": "Header navigate item to the Explore the tool page"
}, },
"header.methodology": { "header.methodology": {
"defaultMessage": "Data & methodology", "defaultMessage": "Data & methodology",
"description": "Navigate to the Methodology page" "description": "Header navigate item to the Methodology page"
},
"header.timeline": {
"defaultMessage": "Timeline",
"description": "Navigate to the Timeline page"
}, },
"header.title.line1": { "header.title.line1": {
"defaultMessage": "Climate and Economic Justice", "defaultMessage": "Climate and Economic Justice",
"description": "Title in header line 1 of 2" "description": "Title in nav header line 1 of 2"
}, },
"header.title.line2": { "header.title.line2": {
"defaultMessage": "Screening Tool", "defaultMessage": "Screening Tool",
"description": "Title in header line 2 of 2" "description": "Title in nav header line 2 of 2"
},
"howYouCanHelp.header.text": {
"defaultMessage": "How You Can Help Improve the Tool",
"description": "the header of the how you can help section"
}, },
"index.aboutContent.header": { "index.aboutContent.header": {
"defaultMessage": "About Justice40", "defaultMessage": "About Justice40",
@ -223,6 +251,18 @@
"defaultMessage": "Transparent:", "defaultMessage": "Transparent:",
"description": "Italic label for 1st paragraph of section 3 on index page" "description": "Italic label for 1st paragraph of section 3 on index page"
}, },
"legend.colorkey.label": {
"defaultMessage": "COLOR KEY",
"description": "the label of the key in the legend"
},
"legend.info.priority.label": {
"defaultMessage": "Prioritized community",
"description": "the label of the prioritized community legend"
},
"legend.info.threshold.label": {
"defaultMessage": "Threshold community",
"description": "the label of the threshold community legend"
},
"map.territoryFocus.alaska.long": { "map.territoryFocus.alaska.long": {
"defaultMessage": "Alaska", "defaultMessage": "Alaska",
"description": "The full name indicating the bounds of Alaska" "description": "The full name indicating the bounds of Alaska"
@ -270,5 +310,13 @@
"mapIntro.mapIntroHeader": { "mapIntro.mapIntroHeader": {
"defaultMessage": "Zoom and select a census block group to view data", "defaultMessage": "Zoom and select a census block group to view data",
"description": "introductory text of ways to use the map" "description": "introductory text of ways to use the map"
},
"mapwrapper.download.contents": {
"defaultMessage": "ZIP file will contain one .xlsx, one .csv and one .pdf (30 MB).",
"description": "download link contents"
},
"mapwrapper.download.link": {
"defaultMessage": "Download the draft list of prioritized communities (pre-decisional) and datasets used",
"description": "download link for datasets"
} }
} }

View file

@ -1,12 +1,21 @@
@import "../components/utils.scss";
$primary-color: #112f4e; $primary-color: #112f4e;
.disclaimer { h1.explorePageHeader {
font-weight: bolder;
margin-bottom: 0;
color: $headingFontColor;
}
.explorePageSubHeader {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
line-height: 25px; line-height: 25px;
color: $headingFontColor;
.textBox { .explorePageHeaderText {
flex: 2; max-width: 34rem;
max-width: 700px; font-size: large;
} }
} }

View file

@ -1,7 +1,8 @@
declare namespace CejstModuleScssNamespace { declare namespace CejstModuleScssNamespace {
export interface ICejstModuleScss { export interface ICejstModuleScss {
disclaimer: string; explorePageHeader;
textBox: string; explorePageSubHeader: string;
explorePageHeaderText: string;
} }
} }

View file

@ -1,9 +1,13 @@
import React from 'react'; import React from 'react';
import Layout from '../components/layout'; import {Link} from 'gatsby-plugin-intl';
import MapWrapper from '../components/mapWrapper';
import AlertWrapper from '../components/AlertWrapper';
import HowYouCanHelp from '../components/HowYouCanHelp'; import HowYouCanHelp from '../components/HowYouCanHelp';
import DownloadPacket from '../components/downloadPacket';
import J40MainGridContainer from '../components/J40MainGridContainer'; import J40MainGridContainer from '../components/J40MainGridContainer';
import Layout from '../components/layout';
import MapWrapper from '../components/MapWrapper';
import MapLegend from '../components/MapLegend';
import * as styles from './cejst.module.scss'; import * as styles from './cejst.module.scss';
import {Grid} from '@trussworks/react-uswds'; import {Grid} from '@trussworks/react-uswds';
@ -16,45 +20,28 @@ const CEJSTPage = ({location}: IMapPageProps) => {
// We temporarily removed MapControls, which would enable you to `setFeatures` also, for now // We temporarily removed MapControls, which would enable you to `setFeatures` also, for now
// We will bring back later when we have interactive controls. // We will bring back later when we have interactive controls.
return (<Layout location={location}> return (<Layout location={location}>
<J40MainGridContainer> <J40MainGridContainer fullWidth={true}>
<AlertWrapper hideWarningAlert={true}/>
</J40MainGridContainer>
<J40MainGridContainer className={'j40-main-content'}>
<Grid row><Grid col> <Grid row><Grid col>
<section> <section>
<h2>Just Progress communities</h2> <h1 className={styles.explorePageHeader}>Explore the tool</h1>
<div className={styles.disclaimer}> <div className={styles.explorePageSubHeader}>
<div className={styles.textBox}> <div className={styles.explorePageHeaderText}>
<p> <p>
Just Progress helps identify and prioritize communities across Zoom into the map to see which communities the tool has currently
the United States and U.S. territories identified as prioritized (the top 25% of communities) or on the
that have been historically overburdened and underserved. threshold. Learn more about the formula and datasets that were
These communities will receive 40% of used to prioritize these communities on the
the benefits from investments in key areas outlined by the {` `}
&nbsp; <Link to={'/methodology'}>Data & methodology</Link>
<a {` `}
href={'https://www.whitehouse.gov/briefing-room/' + page.
'presidential-actions/2021/01/27/' +
'executive-order-on-tackling-the-climate-' +
'crisis-at-home-and-abroad/'}
target={'_blank'}
rel={'noreferrer'}>
Executive Order on Tackling the Climate Crisis at Home and
Abroad
</a>.
</p>
<p>
Download the Just Progress packet or explore the map below to
see the list of prioritized communities. To learn more about
how
these communities were prioritized check out the
&nbsp;
<a
href={'./methodology'}>
Methodology
</a>
&nbsp;
page.
</p> </p>
</div> </div>
<DownloadPacket/> <MapLegend />
</div> </div>
</section> </section>
</Grid></Grid> </Grid></Grid>

View file

@ -1,8 +1,10 @@
import * as React from 'react'; import * as React from 'react';
import Layout from '../components/layout';
import J40MainGridContainer from '../components/J40MainGridContainer';
import {FormattedMessage} from 'gatsby-plugin-intl';
import {Grid} from '@trussworks/react-uswds'; import {Grid} from '@trussworks/react-uswds';
import {FormattedMessage} from 'gatsby-plugin-intl';
import AlertWrapper from '../components/AlertWrapper';
import J40MainGridContainer from '../components/J40MainGridContainer';
import Layout from '../components/layout';
interface ContactPageProps { interface ContactPageProps {
location: Location; location: Location;
@ -12,47 +14,53 @@ const ContactPage = ({location}: ContactPageProps) => {
const generalemail = 'screeningtool.feedback@usds.gov'; const generalemail = 'screeningtool.feedback@usds.gov';
const techemail = 'screeningtool.support@usds.gov'; const techemail = 'screeningtool.support@usds.gov';
return (<Layout location={location}> return (
<J40MainGridContainer> <Layout location={location}>
<Grid row><Grid col>
<section className={'usa-prose'}>
<h2><FormattedMessage
id={'contact.pageheader'}
description={'H2 header for contact page'}
defaultMessage={'Contact'}/></h2>
<h3><FormattedMessage
id={'contact.sectionheader'}
description={'Heading for page to allow users to contact project maintainers'}
defaultMessage={'Email us'}/></h3>
<p> <J40MainGridContainer fullWidth={true}>
<FormattedMessage <AlertWrapper />
id={'contact.general'} </J40MainGridContainer>
description={'Contact page body text'}
defaultMessage={` <J40MainGridContainer>
<Grid row><Grid col>
<section className={'usa-prose'}>
<h2><FormattedMessage
id={'contact.pageheader'}
description={'H2 header for contact page'}
defaultMessage={'Contact'}/></h2>
<h3><FormattedMessage
id={'contact.sectionheader'}
description={'Heading for page to allow users to contact project maintainers'}
defaultMessage={'Email us'}/></h3>
<p>
<FormattedMessage
id={'contact.general'}
description={'Contact page body text'}
defaultMessage={`
For general feedback, email {general_email_address} For general feedback, email {general_email_address}
`} `}
values={{ values={{
general_email_address: general_email_address:
<a href={`mailto:${generalemail}`}>{generalemail}</a>, <a href={`mailto:${generalemail}`}>{generalemail}</a>,
}}/> }}/>
</p> </p>
<p> <p>
<FormattedMessage <FormattedMessage
id={'contact.general'} id={'contact.general'}
description={'Contact page body text'} description={'Contact page body text'}
defaultMessage={` defaultMessage={`
For technical support, email {tech_email_address} For technical support, email {tech_email_address}
`} `}
values={{ values={{
tech_email_address: tech_email_address:
<a href={`mailto:${techemail}`}>{techemail}</a>, <a href={`mailto:${techemail}`}>{techemail}</a>,
}}/> }}/>
</p> </p>
</section> </section>
</Grid></Grid> </Grid></Grid>
</J40MainGridContainer> </J40MainGridContainer>
</Layout> </Layout>
); );
}; };

View file

@ -1,10 +1,12 @@
import * as React from 'react'; import * as React from 'react';
import Layout from '../components/layout';
import AreasOfFocusList from '../components/areasOfFocusList';
import J40MainGridContainer from '../components/J40MainGridContainer';
import {FormattedMessage, useIntl} from 'gatsby-plugin-intl';
import {defineMessages} from 'react-intl'; import {defineMessages} from 'react-intl';
import {Grid} from '@trussworks/react-uswds'; 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';
interface IndexPageProps { interface IndexPageProps {
location: Location; location: Location;
@ -49,139 +51,155 @@ const IndexPage = ({location}: IndexPageProps) => {
}, },
}); });
return (<Layout location={location}> return (
<J40MainGridContainer> <Layout location={location}>
<Grid row><Grid col>
<section className={'usa-prose'}>
<h1>{intl.formatMessage(messages.aboutHeader)}</h1>
<p><FormattedMessage <J40MainGridContainer fullWidth={true}>
id={'index.aboutContent.p1'} <AlertWrapper />
description={'paragraph 1 of main content on index page'} </J40MainGridContainer>
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.
`}/></p>
<p><FormattedMessage <J40MainGridContainer>
id="index.aboutContent.p2" <Grid row>
description={'paragraph 2 of main content on index page'} <Grid col>
defaultMessage={` <section className={'usa-prose'}>
Federal agencies will prioritize benefits using a new <h1>{intl.formatMessage(messages.aboutHeader)}</h1>
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.
`}/></p>
<p><FormattedMessage <p>
id={'index.aboutContent.p3'} <FormattedMessage
description={'paragraph 3 of main content on index page'} id={'index.aboutContent.p1'}
defaultMessage={` description={'paragraph 1 of main content on index page'}
Read more about the Justice40 Initiative in President Bidens defaultMessage={`
{presidentLink} In an effort to address historical environmental injustices,
`} President Biden created the Justice40 Initiative on January
values={{ 27, 2021. The Justice40 Initiative directs 40% of the
presidentLink: benefits from federal investments in seven key areas to
overburdened and underserved communities.
`}/>
</p>
<p>
<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.
`}/>
</p>
<p><FormattedMessage
id={'index.aboutContent.p3'}
description={'paragraph 3 of main content on index page'}
defaultMessage={`
Read more about the Justice40 Initiative in President Bidens
{presidentLink}
`}
values={{
presidentLink:
<a <a
href={intl.formatMessage(messages.presidentalLinkUri)} href={intl.formatMessage(messages.presidentalLinkUri)}
target="_blank" target="_blank"
rel="noreferrer">{intl.formatMessage(messages.presidentalLinkLabel)} rel="noreferrer">{intl.formatMessage(messages.presidentalLinkLabel)}
</a>, </a>,
}}/> }}/>
</p> </p>
<h2><FormattedMessage <h2>
id={'index.section2.header'} <FormattedMessage
description={'section 2 header'} id={'index.section2.header'}
defaultMessage={'Areas of Focus'}/></h2> description={'section 2 header'}
defaultMessage={'Areas of Focus'}/>
</h2>
<AreasOfFocusList/> <AreasOfFocusList/>
<h2><FormattedMessage <h2>
id={'index.section3.header'} <FormattedMessage
description={'section 3 header'} id={'index.section3.header'}
defaultMessage={'A Transparent, Community-First Approach'}/></h2> description={'section 3 header'}
defaultMessage={'A Transparent, Community-First Approach'}/>
</h2>
<p><FormattedMessage <p><FormattedMessage
id={'index.section3.intro'} id={'index.section3.intro'}
description={'section 3 content paragraph 1 intro'} description={'section 3 content paragraph 1 intro'}
defaultMessage={` defaultMessage={`
Successful initiatives are guided by direct input from the Successful initiatives are guided by direct input from the
communities they are serving. CEQ commits to transparency, communities they are serving. CEQ commits to transparency,
inclusivity, and iteration in building this screening tool.`}/> inclusivity, and iteration in building this screening tool.`}/>
</p> </p>
<p> <p>
<FormattedMessage <FormattedMessage
id={'index.section3.transparent'} id={'index.section3.transparent'}
description={'section 3 content transparent'} description={'section 3 content transparent'}
defaultMessage={` defaultMessage={`
{inlineHeader} The code and data behind the screening {inlineHeader} The code and data behind the screening
tool are open source, meaning it is available for the public tool are open source, meaning it is available for the public
to review and contribute to. This tool is being developed to review and contribute to. This tool is being developed
publicly so that communities, academic experts, and anyone publicly so that communities, academic experts, and anyone
whos interested can be involved in the tool-building whos interested can be involved in the tool-building
process.`} process.`}
values={{ values={{
inlineHeader: inlineHeader:
<i>{intl.formatMessage(messages.transparentLabel)}</i>, <i>{intl.formatMessage(messages.transparentLabel)}</i>,
}}/> }}/>
</p> </p>
<p> <p>
<FormattedMessage <FormattedMessage
id={'index.section3.inclusive'} id={'index.section3.inclusive'}
description={'section 3 content inclusive'} description={'section 3 content inclusive'}
defaultMessage={` defaultMessage={`
{inlineHeader} Many areas which lack investments also {inlineHeader} Many areas which lack investments also
lack environmental data and would be overlooked using lack environmental data and would be overlooked using
available environmental data. CEQ is actively reaching out available environmental data. CEQ is actively reaching out
to groups that have historically been excluded from to groups that have historically been excluded from
decision-making, such as groups in rural and tribal areas, decision-making, such as groups in rural and tribal areas,
to understand their needs and ask for their input. to understand their needs and ask for their input.
`} `}
values={{ values={{
inlineHeader: inlineHeader:
<i>{intl.formatMessage(messages.inclusiveLabel)}</i>, <i>{intl.formatMessage(messages.inclusiveLabel)}</i>,
}}/> }}/>
</p> </p>
<p> <p>
<FormattedMessage <FormattedMessage
id={'index.section3.iterative'} id={'index.section3.iterative'}
description={'section 3 content iterative'} description={'section 3 content iterative'}
defaultMessage={` defaultMessage={`
{inlineHeader} The initial community prioritization list {inlineHeader} The initial community prioritization list
provided by the screening tool is the beginning of a provided by the screening tool is the beginning of a
collaborative process in score refinement, rather than a collaborative process in score refinement, rather than a
final answer. CEQ has received recommendations on data sets final answer. CEQ has received recommendations on data sets
from community interviews, the White House Environmental from community interviews, the White House Environmental
Justice Advisory Council, and through public comment, but Justice Advisory Council, and through public comment, but
establishing a score that is truly representative will be a establishing a score that is truly representative will be a
long-term, ongoing process. As communities submit feedback long-term, ongoing process. As communities submit feedback
and recommendations, CEQ will continue to improve the tools and recommendations, CEQ will continue to improve the tools
being built and the processes for stakeholder and public being built and the processes for stakeholder and public
engagement. engagement.
`} `}
values={{ values={{
inlineHeader: inlineHeader:
<i>{intl.formatMessage(messages.iterativeLabel)}</i>, <i>{intl.formatMessage(messages.iterativeLabel)}</i>,
}}/> }}/>
</p> </p>
</section> </section>
</Grid></Grid> </Grid>
</J40MainGridContainer> </Grid>
</Layout>); </J40MainGridContainer>
</Layout>);
}; };
export default IndexPage; export default IndexPage;

View file

@ -1,8 +1,10 @@
import * as React from 'react'; import * as React from 'react';
import Layout from '../components/layout'; import {Grid} from '@trussworks/react-uswds';
import AlertWrapper from '../components/AlertWrapper';
import DatasetContainer from '../components/DatasetContainer'; import DatasetContainer from '../components/DatasetContainer';
import J40MainGridContainer from '../components/J40MainGridContainer'; import J40MainGridContainer from '../components/J40MainGridContainer';
import {Grid} from '@trussworks/react-uswds'; import Layout from '../components/layout';
interface MethodPageProps { interface MethodPageProps {
location: Location; location: Location;
@ -10,46 +12,57 @@ interface MethodPageProps {
// markup // markup
const IndexPage = ({location}: MethodPageProps) => { const IndexPage = ({location}: MethodPageProps) => {
return (<Layout location={location}> return (
<J40MainGridContainer> <Layout location={location}>
<Grid row><Grid col>
<section> <J40MainGridContainer fullWidth={true}>
<h1>Methodology</h1> <AlertWrapper hideWarningAlert={true}/>
<p> </J40MainGridContainer>
<J40MainGridContainer className={'j40-main-content'}>
<Grid row>
<Grid col>
<section>
<h1>Methodology</h1>
<p>
The Just Progress tool combines demographic, environmental, and The Just Progress tool combines demographic, environmental, and
socio-economic data to generate a cumulative index score, referred socio-economic data to generate a cumulative index score, referred
to as the Just Progress Index. The tool currently utilizes to as the Just Progress Index. The tool currently utilizes
national, national,
publically-available data from the United States Census Bureaus publically-available data from the United States Census Bureaus
American Community Survey (ACS) and the EPAs EJScreen tool. American Community Survey (ACS) and the EPAs EJScreen tool.
</p> </p>
<p> <p>
The various inputs into the Just Progress Index are averaged into The various inputs into the Just Progress Index are averaged into
2 categories: Pollution Burden and Demographics. 2 categories: Pollution Burden and Demographics.
</p> </p>
<p> <p>
Pollution Burden: health risks arising from proximity and Pollution Burden: health risks arising from proximity and
potential exposures to pollution and other adverse environmental potential exposures to pollution and other adverse environmental
conditions conditions
</p> </p>
<p> <p>
Demographics: sensitive populations and socioeconomic factors that Demographics: sensitive populations and socioeconomic factors that
make a community more vulnerable make a community more vulnerable
</p> </p>
<p> <p>
<b>Pollution Burden average x Demographics average = Just Progress <b>Pollution Burden average x Demographics average = Just Progress
Index</b> Index</b>
</p> </p>
</section> </section>
</Grid></Grid> </Grid></Grid>
</J40MainGridContainer> </J40MainGridContainer>
<J40MainGridContainer fullWidth={true}> <J40MainGridContainer fullWidth={true}>
<Grid row><Grid col> <Grid row>
<DatasetContainer/> <Grid col>
</Grid></Grid> <DatasetContainer/>
</J40MainGridContainer> </Grid>
</Layout>); </Grid>
</J40MainGridContainer>
</Layout>
);
}; };
export default IndexPage; export default IndexPage;

View file

@ -30,6 +30,10 @@ $theme-font-role-heading: "sans";
$j40-max-width: 80ex; $j40-max-width: 80ex;
$primary-color: #112f4e; $primary-color: #112f4e;
#main-content {
border-top: 0;
}
.j40-two-column { .j40-two-column {
overflow: hidden; overflow: hidden;
padding: 0; padding: 0;
@ -135,9 +139,8 @@ $primary-color: #112f4e;
float: left; // allows vertical centering on logo and text float: left; // allows vertical centering on logo and text
margin-right: 0.5em; // space between logo and text margin-right: 0.5em; // space between logo and text
width: 3em; width: 3em;
padding: 0.4em; // this will change the size of the logo too padding: 0.4em; // this will change the size of the logo too
} }
} }
} }
@ -186,7 +189,7 @@ $primary-color: #112f4e;
} }
h1 { h1 {
font-size: 2.13rem; font-size: 2.7rem;
} }
h2 { h2 {
font-size: 1.46rem; font-size: 1.46rem;
@ -235,9 +238,6 @@ $primary-color: #112f4e;
font-weight: bold; font-weight: bold;
} }
.j40-sitealert-body {
}
.usa-alert { .usa-alert {
margin: 0; margin: 0;
padding-bottom: 0; padding-bottom: 0;