Refactor alerts (#562)

* Refactor alerts
* Remove J40Alert and classes
* Make AlertWrapper the primary component.
* Props to show one or both alerts
* Unit tests to verify one or both works correctly
* Update pages to use AlertWrapper
* Remove unused J40Aside.
* Remove dead code from index
This commit is contained in:
TomNUSDS 2021-08-25 08:34:18 -07:00 committed by GitHub
commit b32fd6ddcb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 154 additions and 272 deletions

View file

@ -16,8 +16,8 @@
"clean": "gatsby clean",
"cy:open": "CYPRESS_REMOTE_DEBUGGING_PORT=9222 cypress open",
"cy:run": "cypress run",
"test": "jest",
"licenses": "license-checker --production --onlyAllow 'Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause;CC0-1.0;CC-BY-3.0;CC-BY-4.0;ISC;MIT;Public Domain;Unlicense;UNLICENSED;ODC-By-1.0;WTFPL;MPL-2.0'",
"test": "jest",
"test:e2e": "start-server-and-test develop http://localhost:8000 cy:open",
"test:e2e:ci": "start-server-and-test develop http://localhost:8000 cy:run",
"test:update": "npm test -- -u",

View file

@ -1,41 +1,71 @@
import React from 'react';
import {Alert} from '@trussworks/react-uswds';
import {FormattedMessage} from 'gatsby-plugin-intl';
import {useIntl} from 'gatsby-plugin-intl';
import * as styles from './alertWrapper.module.scss';
import {defineMessages} from 'react-intl';
interface IAlertWrapperProps {
hideWarningAlert?: boolean
showBetaAlert?: boolean, // defaults to true
showLimitedDataAlert?: boolean, // defaults to false
}
const AlertWrapper = ({hideWarningAlert}:IAlertWrapperProps) => {
// use like this:
// <AlertWrapper showBetaAlert={true} showLimitedDataAlert={true}/>
// <AlertWrapper showBetaAlert={true}/>
const AlertWrapper = ({
showBetaAlert = false,
showLimitedDataAlert = false,
}: IAlertWrapperProps) => {
const intl = useIntl();
const messages = defineMessages({
alertBetaTitle: {
id: 'alert.alertBetaTitle',
defaultMessage:
'Public beta',
description: 'Title for an alert inform users that datasets may change',
},
alertBetaBody: {
id: 'alert.alertBetaBody',
defaultMessage:
'This website may be continuously updated',
description: 'Body for an alert inform users that datasets may change',
},
alertDataLimitedTitle: {
id: 'alert.alertDataLimitedTitle',
defaultMessage:
'Limited data sources',
description: 'Title for an alert inform users that datasets may change',
},
alertDataLimitedBody: {
id: 'alert.alertDataLimitedBody',
defaultMessage:
'Datasets may be added, updated, or removed.',
description: 'Body for an alert inform users that datasets may change',
},
});
return (
<div className={styles.alertWrapper}>
{showBetaAlert && (
<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>
<span className={'j40-sitealert-title'}>{intl.formatMessage(messages.alertBetaTitle)}</span>
<span className={'j40-sitealert-body'}> {intl.formatMessage(messages.alertBetaBody)}</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.
)}
{showLimitedDataAlert && (
<Alert className={'j40-sitealert'} type="warning">
<span className={'j40-sitealert-title'}>{intl.formatMessage(messages.alertDataLimitedTitle)}</span>
<span className={'j40-sitealert-body'}> {intl.formatMessage(messages.alertDataLimitedBody)}</span>
<br/>
</Alert>
)}
</div>
);
}; ;
};
export default AlertWrapper;

View file

@ -0,0 +1,52 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../../test/testHelpers';
import AlertWrapper from '../../AlertWrapper';
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
const PUBLIC_BETA_REGEX = /Public beta/;
const LIMITED_DATA_REGEX = /Limited data sources/;
describe('rendering full the AlertWrapper', () => {
it('checks if component renders both alerts', () => {
const component = render(
<LocalizedComponent>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={true}/>
</LocalizedComponent>,
);
expect(component.container).toHaveTextContent(PUBLIC_BETA_REGEX);
expect(component.container).toHaveTextContent(LIMITED_DATA_REGEX);
});
});
describe('rendering showBetaAlert the AlertWrapper', () => {
it('checks if component renders only beta alert', () => {
const component = render(
<LocalizedComponent>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/>
</LocalizedComponent>,
);
expect(component.container).toHaveTextContent(PUBLIC_BETA_REGEX);
expect(component.container).not.toHaveTextContent(LIMITED_DATA_REGEX);
});
});
describe('rendering showLimitedDataAlert the AlertWrapper', () => {
it('checks if component renders only limited data alert', () => {
const component = render(
<LocalizedComponent>
<AlertWrapper showBetaAlert={false} showLimitedDataAlert={true}/>
</LocalizedComponent>,
);
expect(component.container).not.toHaveTextContent(PUBLIC_BETA_REGEX);
expect(component.container).toHaveTextContent(LIMITED_DATA_REGEX);
});
});

View file

@ -2,8 +2,8 @@ import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import {defineMessages} from 'react-intl';
import DatasetCard from '../DatasetCard';
import J40Alert from '../J40Alert';
import * as styles from './dsContainer.module.scss';
import AlertWrapper from '../AlertWrapper';
export const cards = [
{
@ -74,16 +74,14 @@ const DatasetContainer = () => {
return (
<div className={`${styles.datasetContainer} desktop:grid-col`}>
<div className={`${styles.j40AlertContainer} desktop:grid-col`}>
<div className={'grid-container-desktop-lg'}>
<J40Alert isPaddedLeft={false}/>
</div>
</div>
<div className={'grid-container-desktop-lg'}>
<h1 className={styles.datasetContainerHeader}>{intl.formatMessage(messages.cumulativeScore)}</h1>
<AlertWrapper showBetaAlert={false} showLimitedDataAlert={true}/>
<p className={styles.datasetContainerSubTitle}>{intl.formatMessage(messages.subTitle)}</p>
<div className={styles.datasetCardsContainer}>
{cards.map((card) => <DatasetCard key={card.indicator} datasetCardProps={card}/>)}
{cards.map((card) => <DatasetCard
key={card.indicator}
datasetCardProps={card}/>)}
</div>
</div>
</div>

View file

@ -5,25 +5,38 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<div
class="undefined desktop:grid-col"
>
<div
class="undefined desktop:grid-col"
>
<div
class="grid-container-desktop-lg"
>
<div
class="undefined undefined"
>
Limited data sources — Datasets may be added, updated, or removed.
</div>
</div>
</div>
<div
class="grid-container-desktop-lg"
>
<h1>
Datasets used in cumulative score
</h1>
<div>
<div
class="usa-alert usa-alert--warning j40-sitealert"
data-testid="alert"
>
<div
class="usa-alert__body"
>
<p
class="usa-alert__text"
>
<span
class="j40-sitealert-title"
>
Limited data sources
</span>
<span
class="j40-sitealert-body"
>
— Datasets may be added, updated, or removed.
</span>
<br />
</p>
</div>
</div>
</div>
<p>
The datasets come from a variety of sources and were selected after considering relevance, availability, recency and quality.
</p>

View file

@ -1,31 +0,0 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import {defineMessages} from 'react-intl';
import * as styles from './j40Alert.module.scss';
// 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 {
isPaddedLeft: boolean;
}
const J40Alert = ({isPaddedLeft}: IJ40AlertProps) => {
const intl = useIntl();
const messages = defineMessages({
alertMsg: {
id: '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',
},
});
return (
<div className={`${styles.j40Alert} ${isPaddedLeft ? styles.j40AlertPaddedLeft : styles.j40AlertNoPaddingLeft}`}>
{intl.formatMessage(messages.alertMsg)}
</div>
);
};
export default J40Alert;

View file

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

View file

@ -1,14 +0,0 @@
declare namespace J40AlertScssNamespace {
export interface IJ40AlertScss {
j40Alert: string;
j40AlertPaddedLeft: string;
j40AlertNoPaddingLeft: string;
}
}
declare const J40AlertScssModule: J40AlertScssNamespace.IJ40AlertScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: J40AlertScssNamespace.IJ40AlertScss;
};
export = J40AlertScssModule;

View file

@ -1,21 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the J40Alert tests the rendering of J40Alert with padding 1`] = `
<DocumentFragment>
<div
class="undefined undefined"
>
Limited data sources — Datasets may be added, updated, or removed.
</div>
</DocumentFragment>
`;
exports[`rendering of the J40Alert tests the rendering of J40Alert without padding 1`] = `
<DocumentFragment>
<div
class="undefined undefined"
>
Limited data sources — Datasets may be added, updated, or removed.
</div>
</DocumentFragment>
`;

View file

@ -1,26 +0,0 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../../test/testHelpers';
import J40Alert from '../index';
describe('rendering of the J40Alert', () => {
it('tests the rendering of J40Alert without padding', () => {
const {asFragment} = render(
<LocalizedComponent>
<J40Alert isPaddedLeft={false}/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
it('tests the rendering of J40Alert with padding', () => {
const {asFragment} = render(
<LocalizedComponent>
<J40Alert isPaddedLeft={true}/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -1,49 +0,0 @@
import * as React from 'react';
// @ts-ignore
import chatIcon from '/node_modules/uswds/dist/img/usa-icons/chat.svg';
// @ts-ignore
import githubIcon from '/node_modules/uswds/dist/img/usa-icons/github.svg';
const J40Aside = () => {
return (
<>
<aside
aria-labelledby="right-sidebar"
className={'j40-aside desktop:grid-col-3 usa-prose usa-section'}
id="right-sidebar">
<h2>Get Involved</h2>
<h3><img
className={'flex-align-self-center width-4 j40-aside-icon'}
src={chatIcon} alt={'chat icon'}/>
Send Feedback</h3>
<p className={'usa-prose site-prose'}>Have ideas about how to
acknowledge the on-the-ground experiences of your community?
</p>
Email: <a href="mailto: justice40open@usds.gov">
justice40open@usds.gov</a>
<p>&nbsp;</p>
<h3>
<img
className={'flex-align-self-center width-4 j40-aside-icon'}
src={githubIcon} alt={'github icon'}/>
Join the open source community</h3>
<p>
Justice40s code is open source, which means it is available for
the public to view and contribute. Anyone can view and
contribute on GitHub.
</p>
<p>
<a
href={'https://github.com/usds/justice40-tool/' +
'crisis-at-home-and-abroad/'}
target={'_blank'}
rel={'noreferrer'}
key={'github3'}>Check it out on GitHub</a>
</p>
</aside>
</>
);
};
export default J40Aside;

View file

@ -2,11 +2,11 @@ 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';
import AlertWrapper from '../AlertWrapper';
interface IMapWrapperProps {
location: Location
@ -28,7 +28,7 @@ const MapWrapper = ({location}: IMapWrapperProps) => {
});
return (
<>
<J40Alert isPaddedLeft={true}/>
<AlertWrapper showBetaAlert={false} showLimitedDataAlert={true}/>
<J40Map location={location}/>
<div className={styles.mapCaptionTextLink}>
<a href={constants.DOWNLOAD_ZIP_URL}>

View file

@ -20,8 +20,8 @@ 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 fullWidth={true}>
<AlertWrapper hideWarningAlert={true}/>
<J40MainGridContainer>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/>
</J40MainGridContainer>
<J40MainGridContainer className={'j40-main-content'}>

View file

@ -16,8 +16,8 @@ const ContactPage = ({location}: ContactPageProps) => {
return (
<Layout location={location}>
<J40MainGridContainer fullWidth={true}>
<AlertWrapper/>
<J40MainGridContainer>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/>
</J40MainGridContainer>
<J40MainGridContainer className={'usa-prose'}>

View file

@ -65,7 +65,10 @@ const IndexPage = ({location}: IndexPageProps) => {
return (
<Layout location={location}>
<AlertWrapper/>
<J40MainGridContainer>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/>
</J40MainGridContainer>
<J40MainGridContainer className={'j40-about-page'}>
<AboutCardsContainer>
@ -129,36 +132,6 @@ const IndexPage = ({location}: IndexPageProps) => {
</AboutCard>
</AboutCardsContainer>
{/* <Grid tablet={{col: true}} gap={'lg'}>*/}
{/* <Grid row className={'j40-aboutcard-sm-card'}>*/}
{/* <Grid col={3} className={'j40-about-image-col'}>*/}
{/* <img*/}
{/* className={'j40-about-large-circle-graphics'}*/}
{/* alt="usa map graphics with pins"*/}
{/* src={aboutUSMapImg}/>*/}
{/* </Grid>*/}
{/* <Grid col={9} className={'j40-section-sm-body'}>*/}
{/* <h2>About the screening tool</h2>*/}
{/* */}
{/* </Grid>*/}
{/* </Grid>*/}
{/* <Grid row gap="lg" className={'flex-align-center j40-section-sm-body'}>*/}
{/* <Grid col={3} className={'j40-about-image-col'}>*/}
{/* <img*/}
{/* className={'j40-about-large-circle-graphics'}*/}
{/* alt="graphics showing 40%"*/}
{/* src={aboutJ40Img}/>*/}
{/* </Grid>*/}
{/* <Grid col={9}>*/}
{/* <h2>About the Justice40 Initiative</h2>*/}
{/* */}
{/* </Grid>*/}
{/* </Grid>*/}
{/* </Grid>*/}
</J40MainGridContainer>
<J40MainGridContainer

View file

@ -16,8 +16,8 @@ const IndexPage = ({location}: MethodPageProps) => {
return (
<Layout location={location}>
<J40MainGridContainer fullWidth={true}>
<AlertWrapper hideWarningAlert={true}/>
<J40MainGridContainer>
<AlertWrapper showBetaAlert={true} showLimitedDataAlert={false}/>
</J40MainGridContainer>
<J40MainGridContainer className={'j40-main-content'}>

View file

@ -114,28 +114,6 @@ $j40-blue-background-color: #EFF6FB;
}
}
.j40-aside {
background-color: #eff6fb;
padding-right: 1em;
padding-left: 1em;
h2 {
font-weight: 100;
font-size: 2em;
}
h3 {
font-weight: 100;
font-size: 1.4em;
}
.j40-aside-icon {
display: flex;
margin-top: 3px;
margin-bottom: 12px;
}
}
.j40-footer {
.j40-footer-logo {
font-weight: bold;