Merge pull request #1 from titaniumbones/clean-community-build

Clean community build
This commit is contained in:
Eric Nost 2025-01-24 20:49:09 -05:00 committed by GitHub
commit 905b7bb2df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 22311 additions and 13582 deletions

View file

@ -16,7 +16,7 @@ jobs:
working-directory: client working-directory: client
strategy: strategy:
matrix: matrix:
node-version: [14.x] node-version: [18.x]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}

View file

@ -5,7 +5,7 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }} group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true cancel-in-progress: true
env: env:
node-version: 14.x node-version: 18.x
jobs: jobs:
# JOB to run change detection # JOB to run change detection
detect-fe-changes: detect-fe-changes:

View file

@ -4,7 +4,15 @@
_[¡Lea esto en español!](README-es.md)_ _[¡Lea esto en español!](README-es.md)_
This repo contains the code, processes, and documentation for the data and tech powering the [Climate and Economic Justice Screening Tool (CEJST)](https://screeningtool.geoplatform.gov). This repo contains the code, processes, and documentation for the data and tech powering the former [Climate and Economic Justice Screening Tool (CEJST)](https://screeningtool.geoplatform.gov).
## Status January 2025
The old CEJST has been taken offline by the incoming adminsitration. This fork is maintained by communities who stillrequire access to the tool, and who hope to gradually improve it.
**Many of the instructions in the README are not appropriate at the current stage of development.** In particular, the use of Docker and docker-compose is not advised. Instead, in this initial stage, we are only building the main web server (found in `client`). This is a Gatsby website, which uses React components to build a static site in the `client/public` folder. Further instructions are coming soon. For now, thank you for your interest! Please use Github issues to track problems and feature requests.
-------------
## Background ## Background

View file

@ -1 +1 @@
v14.17.1 v18.20.6

View file

@ -30,11 +30,14 @@ When(`I click on the {string} button in the navigation`, (page) => {
When(`I look for the {string} CTA`, (ctaString) => { When(`I look for the {string} CTA`, (ctaString) => {
cy.get(`[data-cy="${hyphenizeString(ctaString)}-block"]`).as('CTA_block'); cy.get(`[data-cy="${hyphenizeString(ctaString)}-block"]`).as('CTA_block');
cy.get('@CTA_block').scrollIntoView().should('be.visible'); cy.get('@CTA_block').scrollIntoView();
cy.get('@CTA_block').should('be.visible');
}); });
When(`I look for the {string}`, (footer) => { When(`I look for the {string}`, (footer) => {
cy.get(`[data-cy="${hyphenizeString(footer)}-primary-block"]`).scrollIntoView().should('be.visible'); cy.get(`[data-cy="${hyphenizeString(footer)}-primary-block"]`).as('string_block');
cy.get('@string_block').scrollIntoView();
cy.get('@string_block').should('be.visible');
}); });
// Common Thens: // Common Thens:

View file

@ -18,7 +18,7 @@ module.exports = {
image: '/static/favicon.ico', image: '/static/favicon.ico',
siteUrl: process.env.SITE_URL || 'http://localhost:8000', siteUrl: process.env.SITE_URL || 'http://localhost:8000',
}, },
pathPrefix: process.env.PATH_PREFIX || '', pathPrefix: process.env.PATH_PREFIX || 'j40-cejst-2',
plugins: [ plugins: [
{ {
resolve: 'gatsby-plugin-sass', resolve: 'gatsby-plugin-sass',

View file

@ -5,7 +5,7 @@ path = require('path');
// //
// In react-map-gl 7.x this is no longer needed: https://visgl.github.io/react-map-gl/docs/get-started // In react-map-gl 7.x this is no longer needed: https://visgl.github.io/react-map-gl/docs/get-started
// //
exports.onCreateWebpackConfig = ({stage, loaders, actions}) => { exports.onCreateWebpackConfig = ({stage, loaders, actions, getConfig}) => {
actions.setWebpackConfig({ actions.setWebpackConfig({
devtool: 'eval-source-map', devtool: 'eval-source-map',
resolve: { resolve: {
@ -14,4 +14,18 @@ exports.onCreateWebpackConfig = ({stage, loaders, actions}) => {
}, },
}, },
}); });
if (stage === 'develop') {
const config = getConfig();
// Silence CSS ordering warnings, which aren't a risk with CSS Modules
const miniCssExtractPlugin = config.plugins.find(
(plugin) => plugin.constructor.name === 'MiniCssExtractPlugin',
);
if (miniCssExtractPlugin) {
miniCssExtractPlugin.options.ignoreOrder = true;
}
actions.replaceWebpackConfig(config);
}
}; };

30702
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -39,7 +39,7 @@
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^11.2.7", "@testing-library/react": "^11.2.7",
"@types/d3-ease": "^3.0.0", "@types/d3-ease": "^3.0.0",
"@types/jest": "^26.0.24", "@types/jest": "^27.5.2",
"@types/js-search": "^1.4.4", "@types/js-search": "^1.4.4",
"@types/maplibre-gl": "^1.14.0", "@types/maplibre-gl": "^1.14.0",
"@types/node": "^15.14.9", "@types/node": "^15.14.9",
@ -47,8 +47,8 @@
"@types/react-dom": "^17.0.14", "@types/react-dom": "^17.0.14",
"@types/react-helmet": "^6.1.5", "@types/react-helmet": "^6.1.5",
"@types/react-test-renderer": "^17.0.1", "@types/react-test-renderer": "^17.0.1",
"@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^4.33.0", "@typescript-eslint/parser": "^5.62.0",
"axe-core": "^4.4.1", "axe-core": "^4.4.1",
"babel-jest": "^27.5.1", "babel-jest": "^27.5.1",
"babel-preset-gatsby": "^1.14.0", "babel-preset-gatsby": "^1.14.0",
@ -58,11 +58,11 @@
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-cypress": "^2.12.1", "eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-formatjs": "^2.21.0", "eslint-plugin-formatjs": "2.19.1",
"eslint-plugin-react": "^7.29.4", "eslint-plugin-react": "^7.29.4",
"gatsby": "^3.14.6", "gatsby": "^3.14.6",
"gatsby-cli": "^3.14.2", "gatsby-cli": "^3.14.2",
"gatsby-plugin-intl": "^0.3.3", "gatsby-plugin-intl": "^5.10.0",
"gatsby-plugin-prettier-eslint": "^1.0.6", "gatsby-plugin-prettier-eslint": "^1.0.6",
"gatsby-plugin-react-helmet": "^4.14.0", "gatsby-plugin-react-helmet": "^4.14.0",
"gatsby-plugin-sass": "^4.14.0", "gatsby-plugin-sass": "^4.14.0",
@ -80,11 +80,11 @@
}, },
"dependencies": { "dependencies": {
"@sentry/gatsby": "^7.7.0", "@sentry/gatsby": "^7.7.0",
"@trussworks/react-uswds": "^3.1.0", "@trussworks/react-uswds": "^3.2.0",
"@turf/bbox": "^6.5.0", "@turf/bbox": "^6.5.0",
"d3-ease": "^3.0.1", "d3-ease": "^3.0.1",
"gatsby-plugin-env-variables": "^2.2.0", "gatsby-plugin-env-variables": "^2.2.0",
"gatsby-plugin-robots-txt": "^1.7.0", "gatsby-plugin-robots-txt": "1.7.1",
"gatsby-plugin-sitemap": "^4.10.0", "gatsby-plugin-sitemap": "^4.10.0",
"js-search": "^2.0.1", "js-search": "^2.0.1",
"maplibre-gl": "^1.14.0", "maplibre-gl": "^1.14.0",
@ -96,8 +96,7 @@
"react-intl": "^5.24.7", "react-intl": "^5.24.7",
"react-map-gl": "^6.1.19", "react-map-gl": "^6.1.19",
"react-tooltip": "^4.2.21", "react-tooltip": "^4.2.21",
"react-use": "^17.3.2", "react-use": "^17.3.2"
"uswds": "^2.13.3"
}, },
"cypress-cucumber-preprocessor": { "cypress-cucumber-preprocessor": {
"nonGlobalStepDefinitions": false "nonGlobalStepDefinitions": false

View file

@ -0,0 +1,52 @@
@use '../../styles/design-system.scss' as *;
.createReportContainer {
display: flex;
flex-direction: column;
margin: 0;
padding: 1.2rem 1rem 0 1.2rem;
font-size: medium;
height: 100%;
}
.tractListContainer {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: stretch;
height: 50%;
overflow-y: scroll;
border: grey solid thin;
border-radius: 4px;
margin-top: 0.5rem;
}
.tractListItem {
flex-wrap: nowrap;
padding: 0.25rem;
}
.tractListItemHighlight {
flex-wrap: nowrap;
padding: 0.25rem;
font-weight: bold;
background-color: rgba(195, 222, 251, .5);
}
.tractListItemDelete {
align-content: center;
button:focus {
outline-width: 0;
}
}
.createReportButton {
align-self: center;
padding-top: 1rem;
}
.startOver {
margin-top: 1rem;
align-self: center;
}

View file

@ -0,0 +1,18 @@
declare namespace CreateReportPanelNamespace {
export interface ICreateReportPanelScss {
createReportButton: string;
createReportContainer: string;
startOver: string;
tractListContainer: string;
tractListItem: string;
tractListItemDelete: string;
tractListItemHighlight: string;
}
}
declare const CreateReportPanelScssModule: CreateReportPanelNamespace.ICreateReportPanelScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: CreateReportPanelNamespace.ICreateReportPanelScss;
};
export = CreateReportPanelScssModule;

View file

@ -0,0 +1,96 @@
import React, {useEffect, useState} from 'react';
import {MapGeoJSONFeature} from 'maplibre-gl';
import {Button, Alert, Grid} from '@trussworks/react-uswds';
import * as styles from './CreateReportPanel.module.scss';
import * as constants from '../../data/constants';
// @ts-ignore
import deleteIcon from '/node_modules/uswds/dist/img/usa-icons/close.svg';
interface ICreateReportPanel {
deleteTractHandler: (feature: MapGeoJSONFeature) => void,
className: string,
exitHandler: () => void,
featureList: MapGeoJSONFeature[],
maxNumTracts: number,
showTooManyTractsAlert: boolean,
}
const CreateReportPanel = ({
className,
featureList,
maxNumTracts,
showTooManyTractsAlert,
deleteTractHandler,
exitHandler,
}: ICreateReportPanel,
) => {
const [numPrevTracts, setNumPrevTracts] = useState<number>(0);
useEffect(() => {
// If adding a tract then scroll to the bottom of the tract list to always show the last added tract
if (numPrevTracts < featureList.length) {
const container = document.getElementById('j40-create-report-tract-list');
if (container) container.scrollTop = container.scrollHeight;
}
setNumPrevTracts(featureList.length);
}, [featureList, numPrevTracts]);
/**
* Handle the creation of a report.
*/
const handleCreateReport = () => {
if (featureList.length === 1) {
// TODO: One tract report
} else {
// TODO: Multi tract report
}
};
return (
<div id='create-report-panel' className={className}>
<div className={styles.createReportContainer}>
<h4>Create Report</h4>
{showTooManyTractsAlert ?
<Alert type='error' slim headingLevel='h4'>
You can only select up to {maxNumTracts} tracts for a report.
</Alert> :
<Alert type='info' slim headingLevel='h4'>
Select <strong>up to {maxNumTracts}</strong> tracts in the map
</Alert>
}
<p>
<span><strong>{featureList.length} tract{featureList.length === 1 ? '' : 's'}</strong> selected</span>
</p>
<div id='j40-create-report-tract-list' className={styles.tractListContainer}>
{featureList.map((item, index) => (
<Grid row key={index}
className={index === featureList.length - 1 ? styles.tractListItemHighlight : styles.tractListItem}>
<Grid col="auto">
{item.id}, {item.properties[constants.STATE_NAME]}</Grid>
<Grid col="fill" />
<Grid col="auto" className={styles.tractListItemDelete}>
<Button type='button' unstyled
onClick={() => deleteTractHandler(item)}>
<img tabIndex={0} src={deleteIcon}
alt='Need alt message'
/>
</Button>
</Grid>
</Grid>
))}
</div>
<div className={styles.createReportButton} >
<Button type='button' onClick={handleCreateReport}
disabled={featureList.length == 0}>Create Report</Button>
</div>
<div className={styles.startOver}>
<Button type='button' unstyled onClick={exitHandler}>Start Over</Button>
</div>
</div>
</div>
);
};
export default CreateReportPanel;

View file

@ -0,0 +1,3 @@
import CreateReportPanel from './CreateReportPanel';
export default CreateReportPanel;

View file

@ -16,9 +16,11 @@ exports[`rendering of the GovernmentBanner checks if component renders 1`] = `
> >
<div <div
class="usa-banner__inner" class="usa-banner__inner"
data-testid="banner-header-inner-div"
> >
<div <div
class="grid-col-auto" class="grid-col-auto"
data-testid="banner-header-flag-div"
> >
<img <img
alt="U.S. flag" alt="U.S. flag"
@ -28,6 +30,7 @@ exports[`rendering of the GovernmentBanner checks if component renders 1`] = `
</div> </div>
<div <div
class="grid-col-fill tablet:grid-col-auto" class="grid-col-fill tablet:grid-col-auto"
data-testid="banner-header-grid-div"
> >
<p <p
class="usa-banner__header-text" class="usa-banner__header-text"
@ -113,13 +116,32 @@ exports[`rendering of the GovernmentBanner checks if component renders 1`] = `
<span <span
class="icon-lock" class="icon-lock"
> >
<img <svg
alt="lock" aria-labelledby="banner-lock-title banner-lock-description"
class="usa-banner__lock-image" class="usa-banner__lock-image"
focusable="false"
height="64"
role="img" role="img"
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUyIiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNTIgNjQiPjx0aXRsZT5sb2NrPC90aXRsZT48cGF0aCBmaWxsPSIjMUIxQjFCIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNiAwYzEwLjQ5MyAwIDE5IDguNTA3IDE5IDE5djloM2E0IDQgMCAwIDEgNCA0djI4YTQgNCAwIDAgMS00IDRINGE0IDQgMCAwIDEtNC00VjMyYTQgNCAwIDAgMSA0LTRoM3YtOUM3IDguNTA3IDE1LjUwNyAwIDI2IDB6bTAgOGMtNS45NzkgMC0xMC44NDMgNC43Ny0xMC45OTYgMTAuNzEyTDE1IDE5djloMjJ2LTljMC02LjA3NS00LjkyNS0xMS0xMS0xMXoiLz48L3N2Zz4=" viewBox="0 0 52 64"
title="Lock" width="52"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="banner-lock-title"
>
Lock
</title>
<desc
id="banner-lock-description"
>
A locked padlock
</desc>
<path
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
fill="#000000"
fill-rule="evenodd"
/> />
</svg>
</span> </span>
) )
</strong> </strong>

View file

@ -10,7 +10,7 @@ import {
import {Link, useIntl} from 'gatsby-plugin-intl'; import {Link, useIntl} from 'gatsby-plugin-intl';
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {useWindowSize} from 'react-use'; import {useWindowSize} from 'react-use';
import GovernmentBanner from '../GovernmentBanner'; // import GovernmentBanner from '../GovernmentBanner';
import J40MainGridContainer from '../J40MainGridContainer'; import J40MainGridContainer from '../J40MainGridContainer';
import Language from '../Language'; import Language from '../Language';
@ -238,7 +238,7 @@ const J40Header = ({location}:IJ40Header) => {
<Header basic={true} role={'banner'}> <Header basic={true} role={'banner'}>
{/* Banners */} {/* Banners */}
<GovernmentBanner /> {/* <GovernmentBanner /> */}
<UpdateBanner/> <UpdateBanner/>
{/* Logo and Navigation */} {/* Logo and Navigation */}

View file

@ -21,9 +21,11 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
> >
<div <div
class="usa-banner__inner" class="usa-banner__inner"
data-testid="banner-header-inner-div"
> >
<div <div
class="grid-col-auto" class="grid-col-auto"
data-testid="banner-header-flag-div"
> >
<img <img
alt="U.S. flag" alt="U.S. flag"
@ -33,6 +35,7 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
</div> </div>
<div <div
class="grid-col-fill tablet:grid-col-auto" class="grid-col-fill tablet:grid-col-auto"
data-testid="banner-header-grid-div"
> >
<p <p
class="usa-banner__header-text" class="usa-banner__header-text"
@ -118,13 +121,32 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
<span <span
class="icon-lock" class="icon-lock"
> >
<img <svg
alt="lock" aria-labelledby="banner-lock-title banner-lock-description"
class="usa-banner__lock-image" class="usa-banner__lock-image"
focusable="false"
height="64"
role="img" role="img"
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUyIiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNTIgNjQiPjx0aXRsZT5sb2NrPC90aXRsZT48cGF0aCBmaWxsPSIjMUIxQjFCIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNiAwYzEwLjQ5MyAwIDE5IDguNTA3IDE5IDE5djloM2E0IDQgMCAwIDEgNCA0djI4YTQgNCAwIDAgMS00IDRINGE0IDQgMCAwIDEtNC00VjMyYTQgNCAwIDAgMSA0LTRoM3YtOUM3IDguNTA3IDE1LjUwNyAwIDI2IDB6bTAgOGMtNS45NzkgMC0xMC44NDMgNC43Ny0xMC45OTYgMTAuNzEyTDE1IDE5djloMjJ2LTljMC02LjA3NS00LjkyNS0xMS0xMS0xMXoiLz48L3N2Zz4=" viewBox="0 0 52 64"
title="Lock" width="52"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="banner-lock-title"
>
Lock
</title>
<desc
id="banner-lock-description"
>
A locked padlock
</desc>
<path
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
fill="#000000"
fill-rule="evenodd"
/> />
</svg>
</span> </span>
) )
</strong> </strong>
@ -161,24 +183,11 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
</div> </div>
</div> </div>
<div> <div>
<div <div>
class="usa-alert usa-alert--info" <strong>
data-testid="alert" This tool has been updated.
> </strong>
<div The 2.0 version of the tool was released on Dec 20, 2024.
class="usa-alert__body"
>
<h1
class="usa-alert__heading"
>
Version 2.0 of the tool is now available
</h1>
<p
class="usa-alert__text"
>
The Council on Environmental Quality (CEQ) made the 2.0 version of the tool available on Dec 20, 2024.
</p>
</div>
</div> </div>
</div> </div>
<div <div

View file

@ -12,7 +12,8 @@ import ReactMapGL, {
Popup, Popup,
FlyToInterpolator, FlyToInterpolator,
FullscreenControl, FullscreenControl,
MapRef} from 'react-map-gl'; MapRef,
} from 'react-map-gl';
import {useIntl} from 'gatsby-plugin-intl'; import {useIntl} from 'gatsby-plugin-intl';
import bbox from '@turf/bbox'; import bbox from '@turf/bbox';
import * as d3 from 'd3-ease'; import * as d3 from 'd3-ease';
@ -36,6 +37,7 @@ import 'maplibre-gl/dist/maplibre-gl.css';
import * as constants from '../data/constants'; import * as constants from '../data/constants';
import * as styles from './J40Map.module.scss'; import * as styles from './J40Map.module.scss';
import * as EXPLORE_COPY from '../data/copy/explore'; import * as EXPLORE_COPY from '../data/copy/explore';
import CreateReportPanel from './CreateReportPanel';
declare global { declare global {
interface Window { interface Window {
@ -63,6 +65,8 @@ export interface IMapFeature {
type: string; type: string;
} }
const MAX_SELECTED_TRACTS = 20;
const J40Map = ({location}: IJ40Interface) => { const J40Map = ({location}: IJ40Interface) => {
/** /**
* Initializes the zoom, and the map's center point (lat, lng) via the URL hash #{z}/{lat}/{long} * Initializes the zoom, and the map's center point (lat, lng) via the URL hash #{z}/{lat}/{long}
@ -86,11 +90,13 @@ const J40Map = ({location}: IJ40Interface) => {
zoom: zoom && parseFloat(zoom) ? parseFloat(zoom) : constants.GLOBAL_MIN_ZOOM, zoom: zoom && parseFloat(zoom) ? parseFloat(zoom) : constants.GLOBAL_MIN_ZOOM,
}); });
const [selectedFeature, setSelectedFeature] = useState<MapGeoJSONFeature>(); const [selectedFeatures, setSelectedFeatures] = useState<MapGeoJSONFeature[]>([]);
const [detailViewData, setDetailViewData] = useState<IDetailViewInterface>(); const [detailViewData, setDetailViewData] = useState<IDetailViewInterface>();
const [transitionInProgress, setTransitionInProgress] = useState<boolean>(false); const [transitionInProgress, setTransitionInProgress] = useState<boolean>(false);
const [geolocationInProgress, setGeolocationInProgress] = useState<boolean>(false); const [geolocationInProgress, setGeolocationInProgress] = useState<boolean>(false);
const [isMobileMapState, setIsMobileMapState] = useState<boolean>(false); const [isMobileMapState, setIsMobileMapState] = useState<boolean>(false);
const [inMultiSelectMode, setInMultiSelectMode] = useState<boolean>(false);
const [showTooManyTractsAlert, setShowTooManyTractsAlert] = useState<boolean>(false);
const [selectTractId, setSelectTractId] = useState<string | undefined>(undefined); const [selectTractId, setSelectTractId] = useState<string | undefined>(undefined);
const {width: windowWidth} = useWindowSize(); const {width: windowWidth} = useWindowSize();
@ -110,23 +116,84 @@ const J40Map = ({location}: IJ40Interface) => {
const flags = useFlags(); const flags = useFlags();
const intl = useIntl(); const intl = useIntl();
const selectedFeatureId = (selectedFeature && selectedFeature.id) || '';
const zoomLatLngHash = mapRef.current?.getMap()._hash._getCurrentHash(); const zoomLatLngHash = mapRef.current?.getMap()._hash._getCurrentHash();
/**
* Get the bounding box for one or more features.
* @param featureList the list of features
* @returns the bounding box
*/
const getFeaturesBbox = (featureList: MapGeoJSONFeature[]): number[] => {
if (featureList.length === 0) {
throw new Error('featureList must be a non-empty array to get a bounding box.');
}
// Calculate a max and min lat/lon from all the selected features.
const minLngList: number[] = [];
const minLatList: number[] = [];
const maxLngList: number[] = [];
const maxLatList: number[] = [];
featureList.forEach((feature) => {
const [featMinLng, featMinLat, featMaxLng, featMaxLat] = bbox(feature);
minLngList.push(featMinLng);
minLatList.push(featMinLat);
maxLngList.push(featMaxLng);
maxLatList.push(featMaxLat);
});
const minLng: number = Math.min(...minLngList);
const minLat: number = Math.max(...minLatList);
const maxLng: number = Math.max(...maxLngList);
const maxLat: number = Math.min(...maxLatList);
return [minLng, minLat, maxLng, maxLat];
};
/**
* Updates the state with the list of selected features. This function will:
* - Add the feature to the list if in multi select and the feature does not already exist
* - Remove the feature from the list if in multi select and the feature does already exist
* @param feature the feature to add or remove
* @param isMultiSelect true if in multiselect mode
* @returns the list of zero or more features
*/
const updateSelectedFeatures = (feature: MapGeoJSONFeature, isMultiSelect: boolean): MapGeoJSONFeature[] => {
if (!feature) return selectedFeatures;
// If the feature is in the list then remove it as it is being deselected
const exists = selectedFeatures.some((item) => item.id === feature.id);
let featureList: MapGeoJSONFeature[] = selectedFeatures;
if (exists) {
featureList = selectedFeatures.filter((item) => item.id !== feature.id);
setShowTooManyTractsAlert(false);
} else if (selectedFeatures.length < MAX_SELECTED_TRACTS) {
// Add the feature to the list if in multi select, otherwise replace the list
// with just this one feature.
featureList = isMultiSelect ?
[...selectedFeatures, feature] :
[feature];
} else {
setShowTooManyTractsAlert(true);
}
setSelectedFeatures(featureList);
if (!inMultiSelectMode) {
// Turn on multi select mode any time we select more than one tract.
setInMultiSelectMode(featureList.length > 1);
}
return featureList;
};
/** /**
* Selects the provided feature on the map. * Selects the provided feature on the map.
* @param feature the feature to select * @param feature the feature to select
* @param isMultiSelectKeyDown true if the multi select key is down
*/ */
const selectFeatureOnMap = (feature: IMapFeature) => { const selectFeaturesOnMap = (feature: IMapFeature, isMultiSelectKeyDown: boolean = false) => {
if (feature) { const featuresList = updateSelectedFeatures(feature, isMultiSelectKeyDown || inMultiSelectMode);
// Get the current selected feature's bounding box: if (featuresList.length > 0) {
const [minLng, minLat, maxLng, maxLat] = bbox(feature); const [minLng, minLat, maxLng, maxLat] = getFeaturesBbox(featuresList);
// Set the selectedFeature ID // Go to area of the selected feature(s)
setSelectedFeature(feature);
// Go to the newly selected feature (as long as it's not an Alaska Point)
goToPlace([ goToPlace([
[minLng, minLat], [minLng, minLat],
[maxLng, maxLat], [maxLng, maxLat],
@ -220,7 +287,8 @@ const J40Map = ({location}: IJ40Interface) => {
// @ts-ignore // @ts-ignore
const feature = event.features && event.features[0]; const feature = event.features && event.features[0];
selectFeatureOnMap(feature); // @ts-ignore
selectFeaturesOnMap(feature, event.srcEvent.ctrlKey);
} }
}; };
@ -304,7 +372,8 @@ const J40Map = ({location}: IJ40Interface) => {
filter: ['==', constants.GEOID_PROPERTY, selectTractId], filter: ['==', constants.GEOID_PROPERTY, selectTractId],
}); });
if (geoidSearchResults && geoidSearchResults.length > 0) { if (geoidSearchResults && geoidSearchResults.length > 0) {
selectFeatureOnMap(geoidSearchResults[0]); // TODO, support searching for a list of tracts
selectFeaturesOnMap(geoidSearchResults[0]);
} }
setSelectTractId(undefined); setSelectTractId(undefined);
} }
@ -321,6 +390,23 @@ const J40Map = ({location}: IJ40Interface) => {
setGeolocationInProgress(true); setGeolocationInProgress(true);
}; };
/**
* Handler for when there is a change in the multi select side panel.
* @param feature the feature that was added or removed
*/
const onReportDeleteTract = (feature: MapGeoJSONFeature) => {
updateSelectedFeatures(feature, true);
};
/**
* Handler for when the multi select is finished.
*/
const onReportExit = () => {
// Clear everything
setSelectedFeatures([]);
setDetailViewData(undefined);
setInMultiSelectMode(false);
};
return ( return (
<> <>
@ -406,8 +492,7 @@ const J40Map = ({location}: IJ40Interface) => {
} }
<MapTractLayers <MapTractLayers
selectedFeature={selectedFeature} selectedFeatures={selectedFeatures}
selectedFeatureId={selectedFeatureId}
/> />
{/* This is the first overlayed row on the map: Search and Geolocation */} {/* This is the first overlayed row on the map: Search and Geolocation */}
@ -432,7 +517,6 @@ const J40Map = ({location}: IJ40Interface) => {
onClick={onClickGeolocate} onClick={onClickGeolocate}
trackUserLocation={windowWidth < constants.USWDS_BREAKPOINTS.MOBILE_LG} trackUserLocation={windowWidth < constants.USWDS_BREAKPOINTS.MOBILE_LG}
showUserHeading={windowWidth < constants.USWDS_BREAKPOINTS.MOBILE_LG} showUserHeading={windowWidth < constants.USWDS_BREAKPOINTS.MOBILE_LG}
disabledLabel={intl.formatMessage(EXPLORE_COPY.MAP.GEOLOC_MSG_DISABLED)}
/> />
</div> </div>
@ -474,12 +558,21 @@ const J40Map = ({location}: IJ40Interface) => {
</Grid> </Grid>
<Grid desktop={{col: 3}}> <Grid desktop={{col: 3}}>
{inMultiSelectMode ?
<CreateReportPanel
className={styles.mapInfoPanel}
featureList={selectedFeatures}
deleteTractHandler={onReportDeleteTract}
exitHandler={onReportExit}
maxNumTracts={MAX_SELECTED_TRACTS}
showTooManyTractsAlert={showTooManyTractsAlert}
/> :
<MapInfoPanel <MapInfoPanel
className={styles.mapInfoPanel} className={styles.mapInfoPanel}
featureProperties={detailViewData?.properties} featureProperties={detailViewData?.properties}
selectedFeatureId={selectedFeature?.id}
hash={zoomLatLngHash} hash={zoomLatLngHash}
/> />
}
</Grid> </Grid>
</> </>
); );

View file

@ -1,4 +1,4 @@
import React, {useMemo} from 'react'; import React from 'react';
import {Source, Layer} from 'react-map-gl'; import {Source, Layer} from 'react-map-gl';
import {MapGeoJSONFeature} from 'maplibre-gl'; import {MapGeoJSONFeature} from 'maplibre-gl';
@ -9,8 +9,7 @@ import * as constants from '../../data/constants';
import * as COMMON_COPY from '../../data/copy/common'; import * as COMMON_COPY from '../../data/copy/common';
interface IMapTractLayers { interface IMapTractLayers {
selectedFeatureId: string | number, selectedFeatures: MapGeoJSONFeature[] | undefined,
selectedFeature: MapGeoJSONFeature | undefined,
} }
/** /**
@ -65,10 +64,10 @@ export const featureURLForTilesetName = (tilesetName: string): string => {
* @return {Style} * @return {Style}
*/ */
const MapTractLayers = ({ const MapTractLayers = ({
selectedFeatureId, selectedFeatures,
selectedFeature,
}: IMapTractLayers) => { }: IMapTractLayers) => {
const filter = useMemo(() => ['in', constants.GEOID_PROPERTY, selectedFeatureId], [selectedFeature]); const selectedFeatureIds = selectedFeatures ? (selectedFeatures.map((feat) => feat.id)) : [''];
const filter = ['in', constants.GEOID_PROPERTY, ...selectedFeatureIds];
return ( return (
<> <>

View file

@ -5,14 +5,12 @@ import SidePanelInfo from './SidePanelInfo';
interface IMapInfoPanelProps { interface IMapInfoPanelProps {
className: string, className: string,
featureProperties: { [key:string]: string | number } | undefined, featureProperties: { [key:string]: string | number } | undefined,
selectedFeatureId: string | number | undefined
hash: string[], hash: string[],
} }
const MapInfoPanel = ({ const MapInfoPanel = ({
className, className,
featureProperties, featureProperties,
selectedFeatureId,
hash, hash,
}:IMapInfoPanelProps) => { }:IMapInfoPanelProps) => {
return ( return (
@ -22,7 +20,7 @@ const MapInfoPanel = ({
there are two states, namely showing the AreaDetail or SidePanelInfo. When a feature there are two states, namely showing the AreaDetail or SidePanelInfo. When a feature
is selected, show the AreaDetail. When not selected show SidePanelInfo is selected, show the AreaDetail. When not selected show SidePanelInfo
*/} */}
{(featureProperties && selectedFeatureId) ? {(featureProperties) ?
<AreaDetail <AreaDetail
properties={featureProperties} properties={featureProperties}
hash={hash} hash={hash}

View file

@ -36,7 +36,7 @@ export const METH_2_0_RELEASE_DATE = new Date(2024, 11, 20, 11, 59, 59); // Dec
// Update Banner // Update Banner
export const UPDATE_BANNER_HEADING_LARGE = <FormattedMessage export const UPDATE_BANNER_HEADING_LARGE = <FormattedMessage
id={'common.pages.alerts.banner.update.heading.large'} id={'common.pages.alerts.banner.update.heading.large'}
defaultMessage={'Version 2.0 of the tool is now available'} defaultMessage={'This is an unofficial copy of the CEJST Tool. We are working on an overhaul, but in the meantime some links and text may incorrectly suggest that this site is affiliated with the US Government'}
description={'Alert heading that appears at the top of pages'} description={'Alert heading that appears at the top of pages'}
/>; />;
@ -56,7 +56,7 @@ export const UPDATE_BANNER_CONTENT_LARGE = <FormattedMessage
export const UPDATE_BANNER_CONTENT_SMALL = <FormattedMessage export const UPDATE_BANNER_CONTENT_SMALL = <FormattedMessage
id={'common.pages.alerts.banner.update.content.small'} id={'common.pages.alerts.banner.update.content.small'}
defaultMessage={`<bold>This tool has been updated.</bold> The 2.0 version of the tool was released on {relDate}.`} defaultMessage={`<bold>This is an unofficial copy of the CEJST Tool.</bold> We are working on an overhaul, but in the meantime some links and text may incorrectly suggest that this site is affiliated with the US Government.`}
description={`Alert body that appears at the top of pages.`} description={`Alert body that appears at the top of pages.`}
values={{ values={{
bold: boldFn, bold: boldFn,

View file

@ -88,11 +88,11 @@
"description": "Alert body that appears at the top of pages." "description": "Alert body that appears at the top of pages."
}, },
"common.pages.alerts.banner.update.content.small": { "common.pages.alerts.banner.update.content.small": {
"defaultMessage": "<bold>This tool has been updated.</bold> The 2.0 version of the tool was released on {relDate}.", "defaultMessage": "<bold>This is an unofficial copy of the CEJST Tool.</bold> We are working on an overhaul, but in the meantime some links and text may incorrectly suggest that this site is affiliated with the US Government.",
"description": "Alert body that appears at the top of pages." "description": "Alert body that appears at the top of pages."
}, },
"common.pages.alerts.banner.update.heading.large": { "common.pages.alerts.banner.update.heading.large": {
"defaultMessage": "Version 2.0 of the tool is now available", "defaultMessage": "This is an unofficial copy of the CEJST Tool. We are working on an overhaul, but in the meantime some links and text may incorrectly suggest that this site is affiliated with the US Government",
"description": "Alert heading that appears at the top of pages" "description": "Alert heading that appears at the top of pages"
}, },
"common.pages.alerts.census.tract.title": { "common.pages.alerts.census.tract.title": {

View file

@ -21,9 +21,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
> >
<div <div
class="usa-banner__inner" class="usa-banner__inner"
data-testid="banner-header-inner-div"
> >
<div <div
class="grid-col-auto" class="grid-col-auto"
data-testid="banner-header-flag-div"
> >
<img <img
alt="U.S. flag" alt="U.S. flag"
@ -33,6 +35,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
<div <div
class="grid-col-fill tablet:grid-col-auto" class="grid-col-fill tablet:grid-col-auto"
data-testid="banner-header-grid-div"
> >
<p <p
class="usa-banner__header-text" class="usa-banner__header-text"
@ -118,13 +121,32 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<span <span
class="icon-lock" class="icon-lock"
> >
<img <svg
alt="lock" aria-labelledby="banner-lock-title banner-lock-description"
class="usa-banner__lock-image" class="usa-banner__lock-image"
focusable="false"
height="64"
role="img" role="img"
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUyIiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNTIgNjQiPjx0aXRsZT5sb2NrPC90aXRsZT48cGF0aCBmaWxsPSIjMUIxQjFCIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNiAwYzEwLjQ5MyAwIDE5IDguNTA3IDE5IDE5djloM2E0IDQgMCAwIDEgNCA0djI4YTQgNCAwIDAgMS00IDRINGE0IDQgMCAwIDEtNC00VjMyYTQgNCAwIDAgMSA0LTRoM3YtOUM3IDguNTA3IDE1LjUwNyAwIDI2IDB6bTAgOGMtNS45NzkgMC0xMC44NDMgNC43Ny0xMC45OTYgMTAuNzEyTDE1IDE5djloMjJ2LTljMC02LjA3NS00LjkyNS0xMS0xMS0xMXoiLz48L3N2Zz4=" viewBox="0 0 52 64"
title="Lock" width="52"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="banner-lock-title"
>
Lock
</title>
<desc
id="banner-lock-description"
>
A locked padlock
</desc>
<path
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
fill="#000000"
fill-rule="evenodd"
/> />
</svg>
</span> </span>
) )
</strong> </strong>
@ -161,24 +183,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
</div> </div>
<div> <div>
<div <div>
class="usa-alert usa-alert--info" <strong>
data-testid="alert" This tool has been updated.
> </strong>
<div The 2.0 version of the tool was released on Dec 20, 2024.
class="usa-alert__body"
>
<h1
class="usa-alert__heading"
>
Version 2.0 of the tool is now available
</h1>
<p
class="usa-alert__text"
>
The Council on Environmental Quality (CEQ) made the 2.0 version of the tool available on Dec 20, 2024.
</p>
</div>
</div> </div>
</div> </div>
<div <div

View file

@ -21,9 +21,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
> >
<div <div
class="usa-banner__inner" class="usa-banner__inner"
data-testid="banner-header-inner-div"
> >
<div <div
class="grid-col-auto" class="grid-col-auto"
data-testid="banner-header-flag-div"
> >
<img <img
alt="U.S. flag" alt="U.S. flag"
@ -33,6 +35,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
<div <div
class="grid-col-fill tablet:grid-col-auto" class="grid-col-fill tablet:grid-col-auto"
data-testid="banner-header-grid-div"
> >
<p <p
class="usa-banner__header-text" class="usa-banner__header-text"
@ -118,13 +121,32 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<span <span
class="icon-lock" class="icon-lock"
> >
<img <svg
alt="lock" aria-labelledby="banner-lock-title banner-lock-description"
class="usa-banner__lock-image" class="usa-banner__lock-image"
focusable="false"
height="64"
role="img" role="img"
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUyIiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNTIgNjQiPjx0aXRsZT5sb2NrPC90aXRsZT48cGF0aCBmaWxsPSIjMUIxQjFCIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNiAwYzEwLjQ5MyAwIDE5IDguNTA3IDE5IDE5djloM2E0IDQgMCAwIDEgNCA0djI4YTQgNCAwIDAgMS00IDRINGE0IDQgMCAwIDEtNC00VjMyYTQgNCAwIDAgMSA0LTRoM3YtOUM3IDguNTA3IDE1LjUwNyAwIDI2IDB6bTAgOGMtNS45NzkgMC0xMC44NDMgNC43Ny0xMC45OTYgMTAuNzEyTDE1IDE5djloMjJ2LTljMC02LjA3NS00LjkyNS0xMS0xMS0xMXoiLz48L3N2Zz4=" viewBox="0 0 52 64"
title="Lock" width="52"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="banner-lock-title"
>
Lock
</title>
<desc
id="banner-lock-description"
>
A locked padlock
</desc>
<path
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
fill="#000000"
fill-rule="evenodd"
/> />
</svg>
</span> </span>
) )
</strong> </strong>
@ -161,24 +183,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
</div> </div>
<div> <div>
<div <div>
class="usa-alert usa-alert--info" <strong>
data-testid="alert" This tool has been updated.
> </strong>
<div The 2.0 version of the tool was released on Dec 20, 2024.
class="usa-alert__body"
>
<h1
class="usa-alert__heading"
>
Version 2.0 of the tool is now available
</h1>
<p
class="usa-alert__text"
>
The Council on Environmental Quality (CEQ) made the 2.0 version of the tool available on Dec 20, 2024.
</p>
</div>
</div> </div>
</div> </div>
<div <div

View file

@ -21,9 +21,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
> >
<div <div
class="usa-banner__inner" class="usa-banner__inner"
data-testid="banner-header-inner-div"
> >
<div <div
class="grid-col-auto" class="grid-col-auto"
data-testid="banner-header-flag-div"
> >
<img <img
alt="U.S. flag" alt="U.S. flag"
@ -33,6 +35,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
<div <div
class="grid-col-fill tablet:grid-col-auto" class="grid-col-fill tablet:grid-col-auto"
data-testid="banner-header-grid-div"
> >
<p <p
class="usa-banner__header-text" class="usa-banner__header-text"
@ -118,13 +121,32 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<span <span
class="icon-lock" class="icon-lock"
> >
<img <svg
alt="lock" aria-labelledby="banner-lock-title banner-lock-description"
class="usa-banner__lock-image" class="usa-banner__lock-image"
focusable="false"
height="64"
role="img" role="img"
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUyIiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNTIgNjQiPjx0aXRsZT5sb2NrPC90aXRsZT48cGF0aCBmaWxsPSIjMUIxQjFCIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNiAwYzEwLjQ5MyAwIDE5IDguNTA3IDE5IDE5djloM2E0IDQgMCAwIDEgNCA0djI4YTQgNCAwIDAgMS00IDRINGE0IDQgMCAwIDEtNC00VjMyYTQgNCAwIDAgMSA0LTRoM3YtOUM3IDguNTA3IDE1LjUwNyAwIDI2IDB6bTAgOGMtNS45NzkgMC0xMC44NDMgNC43Ny0xMC45OTYgMTAuNzEyTDE1IDE5djloMjJ2LTljMC02LjA3NS00LjkyNS0xMS0xMS0xMXoiLz48L3N2Zz4=" viewBox="0 0 52 64"
title="Lock" width="52"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="banner-lock-title"
>
Lock
</title>
<desc
id="banner-lock-description"
>
A locked padlock
</desc>
<path
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
fill="#000000"
fill-rule="evenodd"
/> />
</svg>
</span> </span>
) )
</strong> </strong>
@ -161,24 +183,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
</div> </div>
<div> <div>
<div <div>
class="usa-alert usa-alert--info" <strong>
data-testid="alert" This tool has been updated.
> </strong>
<div The 2.0 version of the tool was released on Dec 20, 2024.
class="usa-alert__body"
>
<h1
class="usa-alert__heading"
>
Version 2.0 of the tool is now available
</h1>
<p
class="usa-alert__text"
>
The Council on Environmental Quality (CEQ) made the 2.0 version of the tool available on Dec 20, 2024.
</p>
</div>
</div> </div>
</div> </div>
<div <div

View file

@ -21,9 +21,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
> >
<div <div
class="usa-banner__inner" class="usa-banner__inner"
data-testid="banner-header-inner-div"
> >
<div <div
class="grid-col-auto" class="grid-col-auto"
data-testid="banner-header-flag-div"
> >
<img <img
alt="U.S. flag" alt="U.S. flag"
@ -33,6 +35,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
<div <div
class="grid-col-fill tablet:grid-col-auto" class="grid-col-fill tablet:grid-col-auto"
data-testid="banner-header-grid-div"
> >
<p <p
class="usa-banner__header-text" class="usa-banner__header-text"
@ -118,13 +121,32 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<span <span
class="icon-lock" class="icon-lock"
> >
<img <svg
alt="lock" aria-labelledby="banner-lock-title banner-lock-description"
class="usa-banner__lock-image" class="usa-banner__lock-image"
focusable="false"
height="64"
role="img" role="img"
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUyIiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNTIgNjQiPjx0aXRsZT5sb2NrPC90aXRsZT48cGF0aCBmaWxsPSIjMUIxQjFCIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNiAwYzEwLjQ5MyAwIDE5IDguNTA3IDE5IDE5djloM2E0IDQgMCAwIDEgNCA0djI4YTQgNCAwIDAgMS00IDRINGE0IDQgMCAwIDEtNC00VjMyYTQgNCAwIDAgMSA0LTRoM3YtOUM3IDguNTA3IDE1LjUwNyAwIDI2IDB6bTAgOGMtNS45NzkgMC0xMC44NDMgNC43Ny0xMC45OTYgMTAuNzEyTDE1IDE5djloMjJ2LTljMC02LjA3NS00LjkyNS0xMS0xMS0xMXoiLz48L3N2Zz4=" viewBox="0 0 52 64"
title="Lock" width="52"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="banner-lock-title"
>
Lock
</title>
<desc
id="banner-lock-description"
>
A locked padlock
</desc>
<path
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
fill="#000000"
fill-rule="evenodd"
/> />
</svg>
</span> </span>
) )
</strong> </strong>
@ -161,24 +183,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
</div> </div>
<div> <div>
<div <div>
class="usa-alert usa-alert--info" <strong>
data-testid="alert" This tool has been updated.
> </strong>
<div The 2.0 version of the tool was released on Dec 20, 2024.
class="usa-alert__body"
>
<h1
class="usa-alert__heading"
>
Version 2.0 of the tool is now available
</h1>
<p
class="usa-alert__text"
>
The Council on Environmental Quality (CEQ) made the 2.0 version of the tool available on Dec 20, 2024.
</p>
</div>
</div> </div>
</div> </div>
<div <div

View file

@ -21,9 +21,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
> >
<div <div
class="usa-banner__inner" class="usa-banner__inner"
data-testid="banner-header-inner-div"
> >
<div <div
class="grid-col-auto" class="grid-col-auto"
data-testid="banner-header-flag-div"
> >
<img <img
alt="U.S. flag" alt="U.S. flag"
@ -33,6 +35,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
<div <div
class="grid-col-fill tablet:grid-col-auto" class="grid-col-fill tablet:grid-col-auto"
data-testid="banner-header-grid-div"
> >
<p <p
class="usa-banner__header-text" class="usa-banner__header-text"
@ -118,13 +121,32 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<span <span
class="icon-lock" class="icon-lock"
> >
<img <svg
alt="lock" aria-labelledby="banner-lock-title banner-lock-description"
class="usa-banner__lock-image" class="usa-banner__lock-image"
focusable="false"
height="64"
role="img" role="img"
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUyIiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNTIgNjQiPjx0aXRsZT5sb2NrPC90aXRsZT48cGF0aCBmaWxsPSIjMUIxQjFCIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNiAwYzEwLjQ5MyAwIDE5IDguNTA3IDE5IDE5djloM2E0IDQgMCAwIDEgNCA0djI4YTQgNCAwIDAgMS00IDRINGE0IDQgMCAwIDEtNC00VjMyYTQgNCAwIDAgMSA0LTRoM3YtOUM3IDguNTA3IDE1LjUwNyAwIDI2IDB6bTAgOGMtNS45NzkgMC0xMC44NDMgNC43Ny0xMC45OTYgMTAuNzEyTDE1IDE5djloMjJ2LTljMC02LjA3NS00LjkyNS0xMS0xMS0xMXoiLz48L3N2Zz4=" viewBox="0 0 52 64"
title="Lock" width="52"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="banner-lock-title"
>
Lock
</title>
<desc
id="banner-lock-description"
>
A locked padlock
</desc>
<path
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
fill="#000000"
fill-rule="evenodd"
/> />
</svg>
</span> </span>
) )
</strong> </strong>
@ -161,24 +183,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
</div> </div>
<div> <div>
<div <div>
class="usa-alert usa-alert--info" <strong>
data-testid="alert" This tool has been updated.
> </strong>
<div The 2.0 version of the tool was released on Dec 20, 2024.
class="usa-alert__body"
>
<h1
class="usa-alert__heading"
>
Version 2.0 of the tool is now available
</h1>
<p
class="usa-alert__text"
>
The Council on Environmental Quality (CEQ) made the 2.0 version of the tool available on Dec 20, 2024.
</p>
</div>
</div> </div>
</div> </div>
<div <div

View file

@ -21,9 +21,11 @@ exports[`rendering of the Privacy Policy page matches Privacy Policy page snapsh
> >
<div <div
class="usa-banner__inner" class="usa-banner__inner"
data-testid="banner-header-inner-div"
> >
<div <div
class="grid-col-auto" class="grid-col-auto"
data-testid="banner-header-flag-div"
> >
<img <img
alt="U.S. flag" alt="U.S. flag"
@ -33,6 +35,7 @@ exports[`rendering of the Privacy Policy page matches Privacy Policy page snapsh
</div> </div>
<div <div
class="grid-col-fill tablet:grid-col-auto" class="grid-col-fill tablet:grid-col-auto"
data-testid="banner-header-grid-div"
> >
<p <p
class="usa-banner__header-text" class="usa-banner__header-text"
@ -118,13 +121,32 @@ exports[`rendering of the Privacy Policy page matches Privacy Policy page snapsh
<span <span
class="icon-lock" class="icon-lock"
> >
<img <svg
alt="lock" aria-labelledby="banner-lock-title banner-lock-description"
class="usa-banner__lock-image" class="usa-banner__lock-image"
focusable="false"
height="64"
role="img" role="img"
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUyIiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNTIgNjQiPjx0aXRsZT5sb2NrPC90aXRsZT48cGF0aCBmaWxsPSIjMUIxQjFCIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNiAwYzEwLjQ5MyAwIDE5IDguNTA3IDE5IDE5djloM2E0IDQgMCAwIDEgNCA0djI4YTQgNCAwIDAgMS00IDRINGE0IDQgMCAwIDEtNC00VjMyYTQgNCAwIDAgMSA0LTRoM3YtOUM3IDguNTA3IDE1LjUwNyAwIDI2IDB6bTAgOGMtNS45NzkgMC0xMC44NDMgNC43Ny0xMC45OTYgMTAuNzEyTDE1IDE5djloMjJ2LTljMC02LjA3NS00LjkyNS0xMS0xMS0xMXoiLz48L3N2Zz4=" viewBox="0 0 52 64"
title="Lock" width="52"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="banner-lock-title"
>
Lock
</title>
<desc
id="banner-lock-description"
>
A locked padlock
</desc>
<path
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
fill="#000000"
fill-rule="evenodd"
/> />
</svg>
</span> </span>
) )
</strong> </strong>
@ -161,24 +183,11 @@ exports[`rendering of the Privacy Policy page matches Privacy Policy page snapsh
</div> </div>
</div> </div>
<div> <div>
<div <div>
class="usa-alert usa-alert--info" <strong>
data-testid="alert" This tool has been updated.
> </strong>
<div The 2.0 version of the tool was released on Dec 20, 2024.
class="usa-alert__body"
>
<h1
class="usa-alert__heading"
>
Version 2.0 of the tool is now available
</h1>
<p
class="usa-alert__text"
>
The Council on Environmental Quality (CEQ) made the 2.0 version of the tool available on Dec 20, 2024.
</p>
</div>
</div> </div>
</div> </div>
<div <div

View file

@ -21,9 +21,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
> >
<div <div
class="usa-banner__inner" class="usa-banner__inner"
data-testid="banner-header-inner-div"
> >
<div <div
class="grid-col-auto" class="grid-col-auto"
data-testid="banner-header-flag-div"
> >
<img <img
alt="U.S. flag" alt="U.S. flag"
@ -33,6 +35,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
<div <div
class="grid-col-fill tablet:grid-col-auto" class="grid-col-fill tablet:grid-col-auto"
data-testid="banner-header-grid-div"
> >
<p <p
class="usa-banner__header-text" class="usa-banner__header-text"
@ -118,13 +121,32 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<span <span
class="icon-lock" class="icon-lock"
> >
<img <svg
alt="lock" aria-labelledby="banner-lock-title banner-lock-description"
class="usa-banner__lock-image" class="usa-banner__lock-image"
focusable="false"
height="64"
role="img" role="img"
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjUyIiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNTIgNjQiPjx0aXRsZT5sb2NrPC90aXRsZT48cGF0aCBmaWxsPSIjMUIxQjFCIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNiAwYzEwLjQ5MyAwIDE5IDguNTA3IDE5IDE5djloM2E0IDQgMCAwIDEgNCA0djI4YTQgNCAwIDAgMS00IDRINGE0IDQgMCAwIDEtNC00VjMyYTQgNCAwIDAgMSA0LTRoM3YtOUM3IDguNTA3IDE1LjUwNyAwIDI2IDB6bTAgOGMtNS45NzkgMC0xMC44NDMgNC43Ny0xMC45OTYgMTAuNzEyTDE1IDE5djloMjJ2LTljMC02LjA3NS00LjkyNS0xMS0xMS0xMXoiLz48L3N2Zz4=" viewBox="0 0 52 64"
title="Lock" width="52"
xmlns="http://www.w3.org/2000/svg"
>
<title
id="banner-lock-title"
>
Lock
</title>
<desc
id="banner-lock-description"
>
A locked padlock
</desc>
<path
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
fill="#000000"
fill-rule="evenodd"
/> />
</svg>
</span> </span>
) )
</strong> </strong>
@ -161,24 +183,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
</div> </div>
</div> </div>
<div> <div>
<div <div>
class="usa-alert usa-alert--info" <strong>
data-testid="alert" This tool has been updated.
> </strong>
<div The 2.0 version of the tool was released on Dec 20, 2024.
class="usa-alert__body"
>
<h1
class="usa-alert__heading"
>
Version 2.0 of the tool is now available
</h1>
<p
class="usa-alert__text"
>
The Council on Environmental Quality (CEQ) made the 2.0 version of the tool available on Dec 20, 2024.
</p>
</div>
</div> </div>
</div> </div>
<div <div

View file

@ -37,7 +37,7 @@ services:
environment: environment:
# See the client readme for more info on environment variables: # See the client readme for more info on environment variables:
# https://github.com/usds/justice40-tool/blob/main/client/README.md # https://github.com/usds/justice40-tool/blob/main/client/README.md
DATA_SOURCE: local DATA_SOURCE: cdn
TZ: America/Los_Angeles TZ: America/Los_Angeles
volumes: volumes:
- ./client/src:/client/src - ./client/src:/client/src