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', () => {
it('validate file download', () => {
const filename = `usa.zip`;
// const filename = `usa.zip`;
cy.visit('localhost:8000/en/cejst');
cy.get('#download-link').invoke('attr', 'target', '_blank');
cy.intercept(`https://justice40-data.s3.amazonaws.com/Score/${filename}`,
{
body: 'success',
headers: {
'Content-Type': 'text/html; charset=utf-8',
'Content-Disposition': 'attachment',
},
},
).as('downloadRequest');
cy.get('button[class*="downloadPacket"]').click();
cy.wait('@downloadRequest');
cy.readFile(`cypress/downloads/${filename}`).should('exist');
// Todo VS: Download packet component is being moved. Will be re-enabled with
// cy.get('#download-link').invoke('attr', 'target', '_blank');
// cy.intercept(`https://justice40-data.s3.amazonaws.com/Score/${filename}`,
// {
// body: 'success',
// headers: {
// 'Content-Type': 'text/html; charset=utf-8',
// 'Content-Disposition': 'attachment',
// },
// },
// ).as('downloadRequest');
// 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",
"lint": "eslint './src/**/*.{ts,tsx}' --ignore-pattern node_modules/ --ignore-pattern public --ignore-pattern *scss.d.ts",
"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"
},
"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';
interface IDatasetCardProps {
key: number,
datasetCardProps: { [key:string]: string }
}
const DatasetCard = ({key, datasetCardProps}:IDatasetCardProps) => {
const DatasetCard = ({datasetCardProps}:IDatasetCardProps) => {
const intl = useIntl();
const messages = defineMessages({
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 (
<div className={styles.datasetCard}>
<h3 className={styles.datasetCardIndicator}>{datasetCardProps.indicator}</h3>

View file

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

View file

@ -83,7 +83,7 @@ const DatasetContainer = () => {
<h1 className={styles.datasetContainerHeader}>{intl.formatMessage(messages.cumulativeScore)}</h1>
<p className={styles.datasetContainerSubTitle}>{intl.formatMessage(messages.subTitle)}</p>
<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>

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 {
export interface IHowYouCanHelpModuleScss {
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 * 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 messages = defineMessages({
alertMsg: {
@ -13,7 +19,7 @@ const J40Alert = () => {
},
});
return (
<div className={styles.j40Alert}>
<div className={styles.j40Alert} style={alertStyle}>
{intl.formatMessage(messages.alertMsg)}
</div>
);

View file

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

View file

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

View file

@ -1,6 +1,6 @@
// 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>
<div>
Limited data sources — Datasets may be added, updated, or removed.

View file

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

View file

@ -1,7 +1,6 @@
import React, {useState} from 'react';
import {FormattedMessage, Link, useIntl} from 'gatsby-plugin-intl';
import {Link, useIntl} from 'gatsby-plugin-intl';
import {
Alert,
Header,
NavMenuButton,
PrimaryNav,
@ -11,11 +10,7 @@ import {defineMessages} from 'react-intl';
// @ts-ignore
import siteLogo from '../../src/images/icon.png';
interface IJ40HeaderProps {
location: Location
}
const J40Header = ({location}:IJ40HeaderProps) => {
const isMethodologyPage = location.pathname.match(/methodology\/?/);
const J40Header = () => {
const intl = useIntl();
const [mobileNavOpen, setMobileNavOpen] = useState(false);
const messages = defineMessages({
@ -119,29 +114,6 @@ const J40Header = ({location}:IJ40HeaderProps) => {
</PrimaryNav>
</div>
</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,
threshold: 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 {Button} from '@trussworks/react-uswds';
import * as styles from './downloadPacket.module.scss';
// @ts-ignore
import downloadIcon from '/node_modules/uswds/dist/img/usa-icons/file_download.svg';
export const cbgFileURL = 'https://justice40-data.s3.amazonaws.com/Score/usa.zip';
import * as styles from './downloadPacket.module.scss';
import * as constants from '../data/constants';
const DownloadPacket = () => {
return (
@ -16,7 +15,7 @@ const DownloadPacket = () => {
prioritized communities (30,021 census block groups) and 18 datasets.
</div>
<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">
<div><img src={downloadIcon} /> </div>
<div className={styles.downloadPacketText}>Download packet</div>

View file

@ -12,7 +12,7 @@ const Layout = ({children, location}: ILayoutProps) => {
// @ts-ignore
return (
<URLFlagProvider location={location}>
<J40Header location={location}/>
<J40Header />
<main id={'main-content'}>
{children}
</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
$datasetContainerColor: #eef6fb;
$headingFontColor: #122e51;
$j40AlertWarningColor: #faf3d1;

View file

@ -1,7 +1,9 @@
import {LngLatBoundsLike} from 'maplibre-gl';
import {isMobile as isMobileReactDeviceDetect} from 'react-device-detect';
import {defineMessages} from 'react-intl';
// 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';
const XYZ_SUFFIX = '{z}/{x}/{y}.pbf';
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_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;

View file

@ -95,6 +95,34 @@
"defaultMessage": "Email us",
"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": {
"defaultMessage": "Footer navigation",
"description": "aria-label text for whole footer"
@ -129,7 +157,7 @@
},
"header.about": {
"defaultMessage": "About",
"description": "Navigate to the about page"
"description": "Header navigate item to the about page"
},
"header.alertBodyBeta": {
"defaultMessage": "This website will be continuously updated",
@ -141,27 +169,27 @@
},
"header.contact": {
"defaultMessage": "Contact",
"description": "Navigate to the Contact page"
"description": "Header navigate item to the Contact page"
},
"header.explore": {
"defaultMessage": "Explore the tool",
"description": "Navigate to the Explore the tool page"
"description": "Header navigate item to the Explore the tool page"
},
"header.methodology": {
"defaultMessage": "Data & methodology",
"description": "Navigate to the Methodology page"
},
"header.timeline": {
"defaultMessage": "Timeline",
"description": "Navigate to the Timeline page"
"description": "Header navigate item to the Methodology page"
},
"header.title.line1": {
"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": {
"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": {
"defaultMessage": "About Justice40",
@ -223,6 +251,18 @@
"defaultMessage": "Transparent:",
"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": {
"defaultMessage": "Alaska",
"description": "The full name indicating the bounds of Alaska"
@ -270,5 +310,13 @@
"mapIntro.mapIntroHeader": {
"defaultMessage": "Zoom and select a census block group to view data",
"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;
.disclaimer {
h1.explorePageHeader {
font-weight: bolder;
margin-bottom: 0;
color: $headingFontColor;
}
.explorePageSubHeader {
display: flex;
flex-wrap: wrap;
line-height: 25px;
color: $headingFontColor;
.textBox {
flex: 2;
max-width: 700px;
.explorePageHeaderText {
max-width: 34rem;
font-size: large;
}
}

View file

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

View file

@ -1,9 +1,13 @@
import React from 'react';
import Layout from '../components/layout';
import MapWrapper from '../components/mapWrapper';
import {Link} from 'gatsby-plugin-intl';
import AlertWrapper from '../components/AlertWrapper';
import HowYouCanHelp from '../components/HowYouCanHelp';
import DownloadPacket from '../components/downloadPacket';
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 {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 will bring back later when we have interactive controls.
return (<Layout location={location}>
<J40MainGridContainer>
<J40MainGridContainer fullWidth={true}>
<AlertWrapper hideWarningAlert={true}/>
</J40MainGridContainer>
<J40MainGridContainer className={'j40-main-content'}>
<Grid row><Grid col>
<section>
<h2>Just Progress communities</h2>
<div className={styles.disclaimer}>
<div className={styles.textBox}>
<h1 className={styles.explorePageHeader}>Explore the tool</h1>
<div className={styles.explorePageSubHeader}>
<div className={styles.explorePageHeaderText}>
<p>
Just Progress helps identify and prioritize communities across
the United States and U.S. territories
that have been historically overburdened and underserved.
These communities will receive 40% of
the benefits from investments in key areas outlined by the
&nbsp;
<a
href={'https://www.whitehouse.gov/briefing-room/' +
'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;
Zoom into the map to see which communities the tool has currently
identified as prioritized (the top 25% of communities) or on the
threshold. Learn more about the formula and datasets that were
used to prioritize these communities on the
{` `}
<Link to={'/methodology'}>Data & methodology</Link>
{` `}
page.
</p>
</div>
<DownloadPacket/>
<MapLegend />
</div>
</section>
</Grid></Grid>

View file

@ -1,8 +1,10 @@
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 {FormattedMessage} from 'gatsby-plugin-intl';
import AlertWrapper from '../components/AlertWrapper';
import J40MainGridContainer from '../components/J40MainGridContainer';
import Layout from '../components/layout';
interface ContactPageProps {
location: Location;
@ -12,7 +14,13 @@ const ContactPage = ({location}: ContactPageProps) => {
const generalemail = 'screeningtool.feedback@usds.gov';
const techemail = 'screeningtool.support@usds.gov';
return (<Layout location={location}>
return (
<Layout location={location}>
<J40MainGridContainer fullWidth={true}>
<AlertWrapper />
</J40MainGridContainer>
<J40MainGridContainer>
<Grid row><Grid col>
<section className={'usa-prose'}>

View file

@ -1,10 +1,12 @@
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 {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 {
location: Location;
@ -49,13 +51,21 @@ const IndexPage = ({location}: IndexPageProps) => {
},
});
return (<Layout location={location}>
return (
<Layout location={location}>
<J40MainGridContainer fullWidth={true}>
<AlertWrapper />
</J40MainGridContainer>
<J40MainGridContainer>
<Grid row><Grid col>
<Grid row>
<Grid col>
<section className={'usa-prose'}>
<h1>{intl.formatMessage(messages.aboutHeader)}</h1>
<p><FormattedMessage
<p>
<FormattedMessage
id={'index.aboutContent.p1'}
description={'paragraph 1 of main content on index page'}
defaultMessage={`
@ -64,9 +74,11 @@ const IndexPage = ({location}: IndexPageProps) => {
27, 2021. The Justice40 Initiative directs 40% of the
benefits from federal investments in seven key areas to
overburdened and underserved communities.
`}/></p>
`}/>
</p>
<p><FormattedMessage
<p>
<FormattedMessage
id="index.aboutContent.p2"
description={'paragraph 2 of main content on index page'}
defaultMessage={`
@ -81,7 +93,8 @@ const IndexPage = ({location}: IndexPageProps) => {
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>
<p><FormattedMessage
id={'index.aboutContent.p3'}
@ -100,17 +113,21 @@ const IndexPage = ({location}: IndexPageProps) => {
}}/>
</p>
<h2><FormattedMessage
<h2>
<FormattedMessage
id={'index.section2.header'}
description={'section 2 header'}
defaultMessage={'Areas of Focus'}/></h2>
defaultMessage={'Areas of Focus'}/>
</h2>
<AreasOfFocusList/>
<h2><FormattedMessage
<h2>
<FormattedMessage
id={'index.section3.header'}
description={'section 3 header'}
defaultMessage={'A Transparent, Community-First Approach'}/></h2>
defaultMessage={'A Transparent, Community-First Approach'}/>
</h2>
<p><FormattedMessage
id={'index.section3.intro'}
@ -179,7 +196,8 @@ const IndexPage = ({location}: IndexPageProps) => {
}}/>
</p>
</section>
</Grid></Grid>
</Grid>
</Grid>
</J40MainGridContainer>
</Layout>);
};

View file

@ -1,8 +1,10 @@
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 J40MainGridContainer from '../components/J40MainGridContainer';
import {Grid} from '@trussworks/react-uswds';
import Layout from '../components/layout';
interface MethodPageProps {
location: Location;
@ -10,9 +12,16 @@ interface MethodPageProps {
// markup
const IndexPage = ({location}: MethodPageProps) => {
return (<Layout location={location}>
<J40MainGridContainer>
<Grid row><Grid col>
return (
<Layout location={location}>
<J40MainGridContainer fullWidth={true}>
<AlertWrapper hideWarningAlert={true}/>
</J40MainGridContainer>
<J40MainGridContainer className={'j40-main-content'}>
<Grid row>
<Grid col>
<section>
<h1>Methodology</h1>
<p>
@ -45,11 +54,15 @@ const IndexPage = ({location}: MethodPageProps) => {
</J40MainGridContainer>
<J40MainGridContainer fullWidth={true}>
<Grid row><Grid col>
<Grid row>
<Grid col>
<DatasetContainer/>
</Grid></Grid>
</Grid>
</Grid>
</J40MainGridContainer>
</Layout>);
</Layout>
);
};
export default IndexPage;

View file

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