Release of improve census tract display info (#1480)

* will replace mapbox logo (#1477)

* Replace maplibre logo with mapbox
* change the logo go to mapbox
* Add CodeQL and modify mapbox logo via useRef()

* Make side panel formula more apparent (#1481)

* Add ExceedBurden and CategorySpacer

- create ExceedBurden component that is used twice
- create a .categorySpacer class for the AND component
- modify indicator styling to work with background higlighting
- add BE stubs for Burden booleans
- remove Indicators header
- add copy to intl

* Add comments on disadv. indicator

- darken bg color
- add a border
- bold the text

* Adds indicator arrow and sub text to sidepanel

- add threshold to IndicatorInfo
- update SASS for indicatorValueCol
- update tests
- add constants to intl

* Make disadv indicators bold

- add 1px margin between indicators

* Add BE signals for new sidepanel

- tested with staging backend

* Add staging hash to URL

* Fix poverty backend signal

- refactor backend signals in constants file

* Make exceed burden value bold

* Refactor indicator values

- remove arrows and subtext when value is N/A
- Show -- when value is N/A
- intl alt tags in indicator
- fix alignment of arrows
- update snapshots

* Revert settings.json file

* revert settings file again

* Refactor what is displayed when data unavailable

- add an unavailable icon
- add data is not available subtext
- modify low income threshold
- update snapshots
- factor out rendered logic to JSX functions
- update image alt tags and intl

* Refactor Indicator component to unit test

- Add unit test for Indicator value icon
- Add unit test for Indicator value sub-text
- update snapshots

* Add de-coupled BE signals

* Rebase hotfix

* Fix indicator value 0 shows N/A icon

- remove coercion of 0 as null
- make components check null / failure case first and default all else
- update unit test to account for this use case
- update snapshots

* Add null check for percentage suffix

- update unit tests
- update snapshots

* remove cypress zoom test

- intermittent failure continue

* revert cypress to 8.3.0

* Revert cypress-cucumber

* Add Chrome to frontend deploy action

* Update logo css classname after update to maplibre (#1482)

* Add new wording to the map panel that appears upon load when no tract is selected and Outstanding CEQ changes to sidepanel (#1483)

* Add new side panel unselected tract

- add new icons
- refactor old component
- follow component folder pattern
- update snapshot tests
- add to intl

* Add bold to text, add spacing and correct typos

* Add tabindex to sidepanel content to pass a11y

* Refactor i18n anti-patterns on explore tool page

- add i18n patterns for nesting
- add i18n pattern for partial strings
- add i18n rich-text functions
- add i18n pattern for minimal context
- add i18n pattern for dates
- add i18n pattern for numbers
- add i18n pattern for centralizing rich text functions
- add i18n patter for description
- add i18n pattern for ids
- see shared drive file J40 Localization Patterns for status on refactor

* Remove links of expired public engagement sessions

- allow cypress tests to pass

* Update snapshots for public enagement page

* Copy updates to non-selected side panel

- update snapshots

* Updates to side panel copy

- ag loss and building loss text
- clean transit
- NPL RMP sites
- proportion to percent
- update snapshots
- updates to es.json

* Updates from QA

- make title smaller
- make margins above icons smaller
- add bottom margin on container
- add census before tracts in copy
- update snapshots

* Update snapshot after rebase

* disable max-len on description fields on i18n copy (#1487)

* Remove color key from Explore the tool page (#1484)

* Remove color key

* Add comment to Language component

* Move tribal note copy to meth page

- adjust responsive sizing props on Grid to allow for proper mobile viewing on Explore page

* Add responsive size to text under map

- reduce z-index of territory focus control so that it doesn't go over the survey button on mobile

* Rewrite the two "notes about" the "low" datasets (#1489)

* Refactor all copy to adhere to recommended patterns

- remove LowIncome component
- add intl README
- update snapshots

* Fix key error missing in datasetCard

- update type in IIndicators
- update snapshots

* Add two notes on low dataset cards

- refactor DatasetCard to standard component pattern
- add a note to the interface
- update snapshots

* Add function comment to force re-build

* Update missing sass module file name

* Update sidepanel non-selected copy (#1495)

* Update sidepanel non-selected copy

- update snapshots

* Update URL in deploy FE for cypress test

* removing trailing slash

* Add wording to UI that calls out improvements to display of census tract information (#1492)

* Adds census tract alert on all pages

- add i18n text
- updates snapshots
- makes public engagement page a fast link

* Style the Alert to have more space around it

* Update copy on Alert

- update snapshots

* Swap gerkhin order

* Set Alert to expire on Apr 15th 2022

* Add WHEJAC meetings (#1501)

* Add WHEJAC meetings

- add expired icons
- automatically load expired icons when event has passed
- update snapshots

* Update public engagement button

* Remove public eng gherkin tests

- need to troubleshoot why these are failing

* Remove the before CEQ in copy

* Make the count of thresholds exceeded on the side panel more clear (#1503)

* Update category / thrsh count in side panel

- connect BE signal of CC to side panel
- i18n functions to call from AreaDetail component
- update snapshots
-

* Update send feedback - color and icon

- update snapshots

* Update both exceed statements in sidepanel

- update snapshots

* Update copy for higher ed and high school (#1502)

* Update copy for higher ed and high school

- update dataset cards
- update taskforce card AND
- update side panel indicator titles
- add i18n for dataset cards title
- update snapshots

* Update threshold values for Higher ed and HS.

- update snapshots

* Update AND clause

- missing 'of' in copy
- update BE signal for non-higher ed enrollment signal
This commit is contained in:
Vim 2022-03-29 22:49:57 -04:00 committed by GitHub
commit db6b5de24e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
91 changed files with 5339 additions and 3220 deletions

View file

@ -1,21 +1,23 @@
/* eslint-disable quotes */
// External Libs:
import React from 'react';
import {useIntl, FormattedMessage} from 'gatsby-plugin-intl';
import {useIntl} from 'gatsby-plugin-intl';
import {Accordion, Button} from '@trussworks/react-uswds';
// Components:
import Category from '../Category';
import DisadvantageDot from '../DisadvantageDot';
import ExceedBurden from '../ExceedBurden';
import Indicator from '../Indicator';
// Styles and constants
import * as styles from './areaDetail.module.scss';
import * as constants from '../../data/constants';
import * as EXPLORE_COPY from '../../data/copy/explore';
import * as CONTACT_COPY from '../../data/copy/contact';
import * as COMMON_COPY from '../../data/copy/common';
// @ts-ignore
// import mailIcon from '/node_modules/uswds/dist/img/usa-icons/mail.svg';
import mailIcon from '/node_modules/uswds/dist/img/usa-icons/mail_outline.svg';
interface IAreaDetailProps {
properties: constants.J40Properties,
@ -35,6 +37,7 @@ export interface indicatorInfo {
value: number,
isDisadvagtaged: boolean,
isPercent?: boolean,
threshold?: number,
}
const AreaDetail = ({properties}:IAreaDetailProps) => {
@ -97,8 +100,8 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
}
if (indicatorName === 'poverty') {
return properties.hasOwnProperty(constants
.POVERTY_PROPERTY_PERCENTILE) ?
properties[constants.POVERTY_PROPERTY_PERCENTILE] : null;
.POVERTY_BELOW_100_PERCENTILE) ?
properties[constants.POVERTY_BELOW_100_PERCENTILE] : null;
}
if (indicatorName === 'highSchool') {
return properties.hasOwnProperty(constants
@ -117,18 +120,18 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
if (sidePanelState === constants.SIDE_PANEL_STATE_VALUES.ISLAND_AREAS) {
if (indicatorName === 'lowMedInc') {
return properties.hasOwnProperty(constants
.IS_GTE_90_ISLAND_AREA_LOW_MEDIAN_INCOME_AND_IS_LOW_HS_EDU_2009) ?
properties[constants.IS_GTE_90_ISLAND_AREA_LOW_MEDIAN_INCOME_AND_IS_LOW_HS_EDU_2009] : null;
.IS_EXCEEDS_THRESH_FOR_ISLAND_AREA_LOW_MEDIAN_INCOME) ?
properties[constants.IS_EXCEEDS_THRESH_FOR_ISLAND_AREA_LOW_MEDIAN_INCOME] : null;
}
if (indicatorName === 'unemploy') {
return properties.hasOwnProperty(constants
.IS_GTE_90_ISLAND_AREA_UNEMPLOYMENT_AND_IS_LOW_HS_EDU_2009) ?
properties[constants.IS_GTE_90_ISLAND_AREA_UNEMPLOYMENT_AND_IS_LOW_HS_EDU_2009] : null;
.IS_EXCEEDS_THRESH_FOR_ISLAND_AREA_UNEMPLOYMENT) ?
properties[constants.IS_EXCEEDS_THRESH_FOR_ISLAND_AREA_UNEMPLOYMENT] : null;
}
if (indicatorName === 'poverty') {
return properties.hasOwnProperty(constants
.IS_GTE_90_ISLAND_AREA_BELOW_100_POVERTY_AND_IS_LOW_HS_EDU_2009) ?
properties[constants.IS_GTE_90_ISLAND_AREA_BELOW_100_POVERTY_AND_IS_LOW_HS_EDU_2009] : null;
.IS_EXCEEDS_THRESH_FOR_ISLAND_AREA_BELOW_100_POVERTY) ?
properties[constants.IS_EXCEEDS_THRESH_FOR_ISLAND_AREA_BELOW_100_POVERTY] : null;
}
if (indicatorName === 'highSchool') {
return properties.hasOwnProperty(constants
@ -139,18 +142,18 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
if (indicatorName === 'lowMedInc') {
return properties.hasOwnProperty(constants
.IS_GTE_90_LOW_MEDIAN_INCOME_AND_LOW_HIGH_SCHOOL_EDU) ?
properties[constants.IS_GTE_90_LOW_MEDIAN_INCOME_AND_LOW_HIGH_SCHOOL_EDU] : null;
.IS_EXCEEDS_THRESH_FOR_LOW_MEDIAN_INCOME) ?
properties[constants.IS_EXCEEDS_THRESH_FOR_LOW_MEDIAN_INCOME] : null;
}
if (indicatorName === 'unemploy') {
return properties.hasOwnProperty(constants
.IS_GTE_90_UNEMPLOYMENT_AND_LOW_HIGH_SCHOOL_EDU) ?
properties[constants.IS_GTE_90_UNEMPLOYMENT_AND_LOW_HIGH_SCHOOL_EDU] : null;
.IS_EXCEEDS_THRESH_FOR_UNEMPLOYMENT) ?
properties[constants.IS_EXCEEDS_THRESH_FOR_UNEMPLOYMENT] : null;
}
if (indicatorName === 'poverty') {
return properties.hasOwnProperty(constants
.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU) ?
properties[constants.IS_GTE_90_BELOW_100_POVERTY_AND_LOW_HIGH_SCHOOL_EDU] : null;
.IS_EXCEEDS_THRESH_FOR_BELOW_100_POVERTY) ?
properties[constants.IS_EXCEEDS_THRESH_FOR_BELOW_100_POVERTY] : null;
}
if (indicatorName === 'highSchool') {
return properties.hasOwnProperty(constants
@ -166,24 +169,24 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EXP_AG_LOSS),
value: properties.hasOwnProperty(constants.EXP_AGRICULTURE_LOSS_PERCENTILE) ?
properties[constants.EXP_AGRICULTURE_LOSS_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_EXP_AGR_LOSS_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_EXP_AGR_LOSS_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_EXP_AGR_LOSS] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_EXP_AGR_LOSS] : null,
};
const expBldLoss:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EXP_BLD_LOSS),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EXP_BLD_LOSS),
value: properties.hasOwnProperty(constants.EXP_BUILDING_LOSS_PERCENTILE) ?
properties[constants.EXP_BUILDING_LOSS_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_EXP_BLD_LOSS_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_EXP_BLD_LOSS_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_EXP_BLD_LOSS] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_EXP_BLD_LOSS] : null,
};
const expPopLoss:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.EXP_POP_LOSS),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.EXP_POP_LOSS),
value: properties.hasOwnProperty(constants.EXP_POPULATION_LOSS_PERCENTILE) ?
properties[constants.EXP_POPULATION_LOSS_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_EXP_POP_LOSS_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_EXP_POP_LOSS_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_EXP_POP_LOSS] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_EXP_POP_LOSS] : null,
};
const lowInc:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LOW_INCOME),
@ -192,15 +195,17 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
properties[constants.POVERTY_BELOW_200_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_FEDERAL_POVERTY_LEVEL_200] ?
properties[constants.IS_FEDERAL_POVERTY_LEVEL_200] : null,
threshold: 65,
};
const higherEd:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HIGH_ED),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HIGH_ED),
value: properties.hasOwnProperty(constants.HIGHER_ED_PERCENTILE) ?
properties[constants.HIGHER_ED_PERCENTILE] : null,
value: properties.hasOwnProperty(constants.NON_HIGHER_ED_PERCENTILE) ?
properties[constants.NON_HIGHER_ED_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_HIGHER_ED_PERCENTILE] ?
properties[constants.IS_HIGHER_ED_PERCENTILE] : null,
isPercent: true,
threshold: 80,
};
const energyBurden:indicatorInfo = {
@ -208,16 +213,16 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.ENERGY_BURDEN),
value: properties.hasOwnProperty(constants.ENERGY_PERCENTILE) ?
properties[constants.ENERGY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_ENERGY_BURDEN_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_ENERGY_BURDEN_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_ENERGY_BURDEN] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_ENERGY_BURDEN] : null,
};
const pm25:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PM_2_5),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PM_2_5),
value: properties.hasOwnProperty(constants.PM25_PERCENTILE) ?
properties[constants.PM25_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_PM25_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_PM25_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_PM25] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_PM25] : null,
};
const dieselPartMatter:indicatorInfo = {
@ -225,16 +230,16 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIESEL_PARTICULATE_MATTER),
value: properties.hasOwnProperty(constants.DIESEL_MATTER_PERCENTILE) ?
properties[constants.DIESEL_MATTER_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_DIESEL_PM_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_DIESEL_PM_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_DIESEL_PM] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_DIESEL_PM] : null,
};
const trafficVolume:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.TRAFFIC_VOLUME),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.TRAFFIC_VOLUME),
value: properties.hasOwnProperty(constants.TRAFFIC_PERCENTILE) ?
properties[constants.TRAFFIC_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_TRAFFIC_PROX_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_TRAFFIC_PROX_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_TRAFFIC_PROX] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_TRAFFIC_PROX] : null,
};
const houseBurden:indicatorInfo = {
@ -242,16 +247,16 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HOUSE_BURDEN),
value: properties.hasOwnProperty(constants.HOUSING_BURDEN_PROPERTY_PERCENTILE) ?
properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_HOUSE_BURDEN_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_HOUSE_BURDEN_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_HOUSE_BURDEN] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_HOUSE_BURDEN] : null,
};
const leadPaint:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LEAD_PAINT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LEAD_PAINT),
value: properties.hasOwnProperty(constants.LEAD_PAINT_PERCENTILE) ?
properties[constants.LEAD_PAINT_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LEAD_PAINT_AND_MEDIAN_HOME_VAL_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_LEAD_PAINT_AND_MEDIAN_HOME_VAL_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_LEAD_PAINT_AND_MEDIAN_HOME_VAL] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_LEAD_PAINT_AND_MEDIAN_HOME_VAL] : null,
};
// const medHomeVal:indicatorInfo = {
// label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.MED_HOME_VAL),
@ -266,24 +271,24 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PROX_HAZ),
value: properties.hasOwnProperty(constants.PROXIMITY_TSDF_SITES_PERCENTILE) ?
properties[constants.PROXIMITY_TSDF_SITES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_HAZARD_WASTE_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_HAZARD_WASTE_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_HAZARD_WASTE] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_HAZARD_WASTE] : null,
};
const proxNPL:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PROX_NPL),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PROX_NPL),
value: properties.hasOwnProperty(constants.PROXIMITY_NPL_SITES_PERCENTILE) ?
properties[constants.PROXIMITY_NPL_SITES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_SUPERFUND_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_SUPERFUND_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_SUPERFUND] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_SUPERFUND] : null,
};
const proxRMP:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.PROX_RMP),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.PROX_RMP),
value: properties.hasOwnProperty(constants.PROXIMITY_RMP_SITES_PERCENTILE) ?
properties[constants.PROXIMITY_RMP_SITES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_RMP_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_RMP_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_RMP] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_RMP] : null,
};
const wasteWater:indicatorInfo = {
@ -291,8 +296,8 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.WASTE_WATER),
value: properties.hasOwnProperty(constants.WASTEWATER_PERCENTILE) ?
properties[constants.WASTEWATER_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_WASTEWATER_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_WASTEWATER_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_WASTEWATER] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_WASTEWATER] : null,
};
const asthma:indicatorInfo = {
@ -300,32 +305,32 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.ASTHMA),
value: properties.hasOwnProperty(constants.ASTHMA_PERCENTILE) ?
properties[constants.ASTHMA_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_ASTHMA_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_ASTHMA_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_ASTHMA] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_ASTHMA] : null,
};
const diabetes:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.DIABETES),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.DIABETES),
value: properties.hasOwnProperty(constants.DIABETES_PERCENTILE) ?
properties[constants.DIABETES_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_DIABETES_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_DIABETES_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_DIABETES] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_DIABETES] : null,
};
const heartDisease:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.HEART_DISEASE),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.HEART_DISEASE),
value: properties.hasOwnProperty(constants.HEART_PERCENTILE) ?
properties[constants.HEART_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_HEART_DISEASE_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_HEART_DISEASE_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_HEART_DISEASE] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_HEART_DISEASE] : null,
};
const lifeExpect:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LIFE_EXPECT),
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LOW_LIFE_EXPECT),
value: properties.hasOwnProperty(constants.LIFE_PERCENTILE) ?
properties[constants.LIFE_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LOW_LIFE_EXP_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_LOW_LIFE_EXP_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_LOW_LIFE_EXP] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_LOW_LIFE_EXP] : null,
};
const lingIso:indicatorInfo = {
@ -333,8 +338,8 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
description: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATOR_DESCRIPTION.LING_ISO),
value: properties.hasOwnProperty(constants.LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE) ?
properties[constants.LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE] : null,
isDisadvagtaged: properties[constants.IS_GTE_90_LINGUISITIC_ISO_AND_IS_LOW_INCOME] ?
properties[constants.IS_GTE_90_LINGUISITIC_ISO_AND_IS_LOW_INCOME] : null,
isDisadvagtaged: properties[constants.IS_EXCEEDS_THRESH_FOR_LINGUISITIC_ISO] ?
properties[constants.IS_EXCEEDS_THRESH_FOR_LINGUISITIC_ISO] : null,
};
const lowMedInc:indicatorInfo = {
label: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INDICATORS.LOW_MED_INC),
@ -360,65 +365,111 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
value: getWorkForceIndicatorValue('highSchool'),
isDisadvagtaged: getWorkForceIndicatorIsDisadv('highSchool'),
isPercent: true,
threshold: 10,
};
// Aggregate indicators based on categories
/**
* Aggregate indicators based on categories
*
* The indicators property must be an array with last two elements being the
* socioeconomic burdens.
*/
let categories = [
{
id: 'climate-change',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLIMATE),
indicators: [expAgLoss, expBldLoss, expPopLoss, lowInc, higherEd],
indicators: [expAgLoss, expBldLoss, expPopLoss],
socioEcIndicators: [lowInc, higherEd],
isDisadvagtaged: properties[constants.IS_CLIMATE_FACTOR_DISADVANTAGED_M] ?
properties[constants.IS_CLIMATE_FACTOR_DISADVANTAGED_M] : null,
isExceed1MoreBurden: properties[constants.IS_CLIMATE_EXCEED_ONE_OR_MORE_INDICATORS_M] ?
properties[constants.IS_CLIMATE_EXCEED_ONE_OR_MORE_INDICATORS_M] : null,
isExceedBothSocioBurdens: properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] ?
properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] : null,
},
{
id: 'clean-energy',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLEAN_ENERGY),
indicators: [energyBurden, pm25, lowInc, higherEd],
indicators: [energyBurden, pm25],
socioEcIndicators: [lowInc, higherEd],
isDisadvagtaged: properties[constants.IS_ENERGY_FACTOR_DISADVANTAGED_M] ?
properties[constants.IS_ENERGY_FACTOR_DISADVANTAGED_M] : null,
isExceed1MoreBurden: properties[constants.IS_ENERGY_EXCEED_ONE_OR_MORE_INDICATORS_M] ?
properties[constants.IS_ENERGY_EXCEED_ONE_OR_MORE_INDICATORS_M] : null,
isExceedBothSocioBurdens: properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] ?
properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] : null,
},
{
id: 'clean-transport',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLEAN_TRANSPORT),
indicators: [dieselPartMatter, trafficVolume, lowInc, higherEd],
indicators: [dieselPartMatter, trafficVolume],
socioEcIndicators: [lowInc, higherEd],
isDisadvagtaged: properties[constants.IS_TRANSPORT_FACTOR_DISADVANTAGED_M] ?
properties[constants.IS_TRANSPORT_FACTOR_DISADVANTAGED_M] : null,
isExceed1MoreBurden: properties[constants.IS_TRANSPORT_EXCEED_ONE_OR_MORE_INDICATORS_M] ?
properties[constants.IS_TRANSPORT_EXCEED_ONE_OR_MORE_INDICATORS_M] : null,
isExceedBothSocioBurdens: properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] ?
properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] : null,
},
{
id: 'sustain-house',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.SUSTAIN_HOUSE),
indicators: [houseBurden, leadPaint, lowInc, higherEd],
indicators: [houseBurden, leadPaint],
socioEcIndicators: [lowInc, higherEd],
isDisadvagtaged: properties[constants.IS_HOUSING_FACTOR_DISADVANTAGED_M] ?
properties[constants.IS_HOUSING_FACTOR_DISADVANTAGED_M] : null,
isExceed1MoreBurden: properties[constants.IS_HOUSING_EXCEED_ONE_OR_MORE_INDICATORS_M] ?
properties[constants.IS_HOUSING_EXCEED_ONE_OR_MORE_INDICATORS_M] : null,
isExceedBothSocioBurdens: properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] ?
properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] : null,
},
{
id: 'leg-pollute',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.LEG_POLLUTE),
indicators: [proxHaz, proxNPL, proxRMP, lowInc, higherEd],
indicators: [proxHaz, proxNPL, proxRMP],
socioEcIndicators: [lowInc, higherEd],
isDisadvagtaged: properties[constants.IS_POLLUTION_FACTOR_DISADVANTAGED_M] ?
properties[constants.IS_POLLUTION_FACTOR_DISADVANTAGED_M] : null,
isExceed1MoreBurden: properties[constants.IS_POLLUTION_EXCEED_ONE_OR_MORE_INDICATORS_M] ?
properties[constants.IS_POLLUTION_EXCEED_ONE_OR_MORE_INDICATORS_M] : null,
isExceedBothSocioBurdens: properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] ?
properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] : null,
},
{
id: 'clean-water',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.CLEAN_WATER),
indicators: [wasteWater, lowInc, higherEd],
indicators: [wasteWater],
socioEcIndicators: [lowInc, higherEd],
isDisadvagtaged: properties[constants.IS_WATER_FACTOR_DISADVANTAGED_M] ?
properties[constants.IS_WATER_FACTOR_DISADVANTAGED_M] : null,
isExceed1MoreBurden: properties[constants.IS_WATER_EXCEED_ONE_OR_MORE_INDICATORS_M] ?
properties[constants.IS_WATER_EXCEED_ONE_OR_MORE_INDICATORS_M] : null,
isExceedBothSocioBurdens: properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] ?
properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] : null,
},
{
id: 'health-burdens',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.HEALTH_BURDEN),
indicators: [asthma, diabetes, heartDisease, lifeExpect, lowInc, higherEd],
indicators: [asthma, diabetes, heartDisease, lifeExpect],
socioEcIndicators: [lowInc, higherEd],
isDisadvagtaged: properties[constants.IS_HEALTH_FACTOR_DISADVANTAGED_M] ?
properties[constants.IS_HEALTH_FACTOR_DISADVANTAGED_M] : null,
isExceed1MoreBurden: properties[constants.IS_HEALTH_EXCEED_ONE_OR_MORE_INDICATORS_M] ?
properties[constants.IS_HEALTH_EXCEED_ONE_OR_MORE_INDICATORS_M] : null,
isExceedBothSocioBurdens: properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] ?
properties[constants.IS_EXCEED_BOTH_SOCIO_INDICATORS_M] : null,
},
{
id: 'work-dev',
titleText: intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.WORK_DEV),
indicators: [lingIso, lowMedInc, , unemploy, poverty, highSchool, higherEd],
indicators: [lingIso, lowMedInc, , unemploy, poverty],
socioEcIndicators: [highSchool, higherEd],
isDisadvagtaged: properties[constants.IS_WORKFORCE_FACTOR_DISADVANTAGED_M] ?
properties[constants.IS_WORKFORCE_FACTOR_DISADVANTAGED_M] : null,
isExceed1MoreBurden: properties[constants.IS_WORKFORCE_EXCEED_ONE_OR_MORE_INDICATORS_M] ?
properties[constants.IS_WORKFORCE_EXCEED_ONE_OR_MORE_INDICATORS_M] : null,
isExceedBothSocioBurdens: properties[constants.IS_WORKFORCE_EXCEED_BOTH_SOCIO_INDICATORS_M] ?
properties[constants.IS_WORKFORCE_EXCEED_BOTH_SOCIO_INDICATORS_M] : null,
},
];
@ -448,16 +499,31 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
title: <Category name={category.titleText} isDisadvantaged={category.isDisadvagtaged}/>,
content: (
<>
{/* Category Header */}
<div className={styles.categoryHeader}>
<div>{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.INDICATOR)}</div>
<div>{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_CATEGORY.PERCENTILE)}</div>
</div>
{/* Exceeds one or more burdens */}
<ExceedBurden
text={EXPLORE_COPY.SIDE_PANEL_SPACERS.EXCEED_ONE_OR_MORE}
isBurdened={category.isExceed1MoreBurden}
/>
{/* Category Indicators */}
{/* indicators */}
{category.indicators.map((indicator:any, index:number) => {
return <Indicator key={`ind${index}`} indicator={indicator}/>;
})}
{/* AND */}
<div className={styles.categorySpacer}>AND</div>
{/* Exceeds both socioeconomic burdens */}
<ExceedBurden
text={EXPLORE_COPY.SIDE_PANEL_SPACERS.EXCEED_BOTH_SOCIO}
isBurdened={category.isExceedBothSocioBurdens}
/>
{/* socio-economic indicators */}
{category.socioEcIndicators.map((indicator:any, index:number) => {
return <Indicator key={`ind${index}`} indicator={indicator}/>;
})}
</>
),
expanded: false,
@ -521,35 +587,38 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
}
</div>
{/* Number of thresholds exceeded */}
<div className={
properties[constants.TOTAL_NUMBER_OF_DISADVANTAGE_INDICATORS] > 0 ?
styles.showThresholdExceed : styles.hideThresholdExceed
}>
<FormattedMessage
id={'explore.page.threshold.count.exceed'}
description={"threshold exceeded count"}
defaultMessage={'{disadvCount} of {totalCount} thresholds exceeded'}
values={{
disadvCount: properties[constants.TOTAL_NUMBER_OF_DISADVANTAGE_INDICATORS],
totalCount: properties[constants.TOTAL_NUMBER_OF_INDICATORS],
}}/>
{/* Number of categories exceeded */}
<div className={styles.showCategoriesExceed}>
{EXPLORE_COPY.numberOfCategoriesExceeded(properties[constants.COUNT_OF_CATEGORIES_DISADV])}
</div>
{/* Number of thresholds exceeded */}
<div className={styles.showThresholdExceed}>
{EXPLORE_COPY.numberOfThresholdsExceeded(properties[constants.TOTAL_NUMBER_OF_DISADVANTAGE_INDICATORS])}
</div>
{/* Send Feedback button */}
<a
className={styles.sendFeedbackBtn}
className={styles.sendFeedbackLink}
// The mailto string must be on a single line otherwise the email does not display subject and body
href={`
mailto:${CONTACT_COPY.FEEDBACK_EMAIL}?subject=${feedbackEmailSubject}&body=${feedbackEmailBody}
mailto:${COMMON_COPY.FEEDBACK_EMAIL}?subject=${feedbackEmailSubject}&body=${feedbackEmailBody}
`}
target={"_blank"}
rel="noreferrer"
>
<Button
type="button">
<div>
{EXPLORE_COPY.COMMUNITY.SEND_FEEDBACK.TITLE}
type="button"
className={styles.sendFeedbackBtn}
>
<div className={styles.buttonContainer}>
<div className={styles.buttonText}>
{EXPLORE_COPY.COMMUNITY.SEND_FEEDBACK.TITLE}
</div>
<img
className={styles.buttonImage}
src={mailIcon}
alt={'tbd'}
/>
</div>
{/* <div>
<img src={mailIcon} alt={'mail icon for email'}/>

View file

@ -9,11 +9,6 @@ $sidePanelLabelFontColor: #171716;
font-weight: 600;
}
@mixin thresholdExceeded {
font-size: medium;
@include u-margin-top('05');
}
.versionInfo {
padding: .5rem 1rem .5rem 1.2rem;
font-size: medium;
@ -25,7 +20,6 @@ $sidePanelLabelFontColor: #171716;
flex-direction: column;
}
.categorization {
display: flex;
flex-direction: column;
@ -48,19 +42,45 @@ $sidePanelLabelFontColor: #171716;
}
}
.showThresholdExceed {
@include thresholdExceeded;
display: block;
}
.hideThresholdExceed {
@include thresholdExceeded;
visibility: hidden;
.showCategoriesExceed {
font-size: small;
@include u-margin-top('05');
}
.sendFeedbackBtn {
.showThresholdExceed {
font-size: small;
@include u-margin-top('05');
}
.sendFeedbackLink {
@include u-margin-top(2);
.sendFeedbackBtn{
@include u-text("blue-70v");
@include u-bg("yellow-20v");
height: 40px;
&:hover {
@include u-bg("yellow-20");
@include u-text("gray-90");
}
.buttonContainer{
display: flex;
.buttonText{
@include u-margin-right(1);
}
.buttonImage{
width: 21px;
margin-top: -3px;
filter: invert(13%) sepia(76%) saturate(5142%) hue-rotate(192deg) brightness(80%) contrast(106%);
}
}
}
}
}
@ -82,14 +102,6 @@ $sidePanelLabelFontColor: #171716;
}
}
// The following class is used in the AccordionItems in the AreaDetail component.
// The Accordion component (parent of AccordionItems) requires some CSS overrides.
// Local styling is not allowing the override.
// The override is needed to push into the bounds of the Accordion component's styles.
// To override this, in globals.scss, we set the this .categoryHeader's vertical alignment by
// setting styles in:
// .usa-accordion__content > *:first-child
// This first child of the accordion content is the category header:
.categoryHeader {
display: flex;
justify-content: space-between;
@ -98,4 +110,17 @@ $sidePanelLabelFontColor: #171716;
@include u-bg('gray-cool-5');
@include u-padding-left(2.5);
@include u-padding-right(2);
}
}
.categorySpacer {
@include u-bg('gray-cool-3');
@include typeset('sans', '2xs', 2);
@include u-text('bold');
margin: 0 -20px 1rem -20px;
@include u-padding-top(2);
@include u-padding-bottom(2);
@include u-padding-left(2.5);
}

View file

@ -10,9 +10,14 @@ declare namespace MapModuleScssNamespace {
isInFocus:string;
versionInfo: string;
showThresholdExceed:string;
hideThresholdExceed:string;
showCategoriesExceed: string;
categoryHeader:string;
sendFeedbackLink: string;
sendFeedbackBtn: string;
buttonContainer: string;
buttonText: string;
buttonImage: string;
categorySpacer: string;
}
}

View file

@ -1,3 +1,4 @@
import AreaDetail from './AreaDetail';
export default AreaDetail;

View file

@ -7,7 +7,7 @@ import * as constants from '../../../data/constants';
describe('rendering of the AreaDetail', () => {
const properties = {
[constants.POVERTY_PROPERTY_PERCENTILE]: .12,
[constants.POVERTY_BELOW_100_PERCENTILE]: .12,
[constants.HIGH_SCHOOL_PROPERTY_PERCENTILE]: .98,
[constants.LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE]: .97,
[constants.UNEMPLOYMENT_PROPERTY_PERCENTILE]: .96,
@ -15,8 +15,10 @@ describe('rendering of the AreaDetail', () => {
[constants.SCORE_PROPERTY_HIGH]: .95,
[constants.GEOID_PROPERTY]: 98729374234,
[constants.TOTAL_POPULATION]: 3435435,
[constants.POVERTY_PROPERTY_PERCENTILE]: .19,
[constants.POVERTY_BELOW_200_PERCENTILE]: .19,
[constants.SIDE_PANEL_STATE]: constants.SIDE_PANEL_STATE_VALUES.NATION,
[constants.COUNT_OF_CATEGORIES_DISADV]: 5,
[constants.TOTAL_NUMBER_OF_DISADVANTAGE_INDICATORS]: 3,
};
@ -49,7 +51,7 @@ describe('rendering of the AreaDetail', () => {
[constants.ISLAND_AREAS_UNEMPLOYMENT_LOW_HS_EDU_PERCENTILE_FIELD]: .9,
[constants.ISLAND_AREAS_POVERTY_LOW_HS_EDU_PERCENTILE_FIELD]: .8,
[constants.ISLAND_AREAS_LOW_MEDIAN_INCOME_LOW_HS_EDU_PERCENTILE_FIELD]: .6,
[constants.ISLAND_AREAS_LOW_HS_EDU_PERCENTILE_FIELD]: .5,
[constants.ISLAND_AREAS_POVERTY_LOW_HS_EDU_PERCENTILE_FIELD]: .5,
[constants.SIDE_PANEL_STATE]: constants.SIDE_PANEL_STATE_VALUES.ISLAND_AREAS,
};

View file

@ -34,12 +34,10 @@ exports[`rendering of the Categories checks if component renders 1`] = `
<strong>
identified as disadvantaged
</strong>
</p>
<p>
<strong>
IF
</strong>
@ -61,8 +59,6 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
expected population loss rate
</a>
</p>
<p>
@ -76,11 +72,11 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
low income
</a>
AND at or below 20% for
AND 80% or more of adults 15 or older are not enrolled in
<a
href="#high-ed-enroll-rate"
>
higher ed enrollment rate
higher education
</a>
@ -97,12 +93,10 @@ exports[`rendering of the Categories checks if component renders 1`] = `
<strong>
identified as disadvantaged
</strong>
</p>
<p>
<strong>
IF
</strong>
@ -118,8 +112,6 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
PM2.5 in the air
</a>
</p>
<p>
@ -133,11 +125,11 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
low income
</a>
AND at or below 20% for
AND 80% or more of adults 15 or older are not enrolled in
<a
href="#high-ed-enroll-rate"
>
higher ed enrollment rate
higher education
</a>
@ -154,7 +146,7 @@ exports[`rendering of the Categories checks if component renders 1`] = `
<strong>
identified as disadvantaged
</strong>
</p>
<p>
@ -190,11 +182,11 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
low income
</a>
AND at or below 20% for
AND 80% or more of adults 15 or older are not enrolled in
<a
href="#high-ed-enroll-rate"
>
higher ed enrollment rate
higher education
</a>
@ -211,12 +203,10 @@ exports[`rendering of the Categories checks if component renders 1`] = `
<strong>
identified as disadvantaged
</strong>
</p>
<p>
<strong>
IF
</strong>
@ -232,15 +222,12 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
median home value
</a>
is at or less than
the 90th percentile OR at or above the 90th percentile for the
is at or less than the 90th percentile OR at or above the 90th percentile for the
<a
href="#house-burden"
>
housing cost burden
</a>
</p>
<p>
@ -254,11 +241,11 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
low income
</a>
AND at or below 20% for
AND 80% or more of adults 15 or older are not enrolled in
<a
href="#high-ed-enroll-rate"
>
higher ed enrollment rate
higher education
</a>
@ -275,12 +262,10 @@ exports[`rendering of the Categories checks if component renders 1`] = `
<strong>
identified as disadvantaged
</strong>
</p>
<p>
<strong>
IF
</strong>
@ -302,8 +287,6 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
proximity to Risk Management Plan (RMP) facilities
</a>
</p>
<p>
@ -317,11 +300,11 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
low income
</a>
AND at or below 20% for
AND 80% or more of adults 15 or older are not enrolled in
<a
href="#high-ed-enroll-rate"
>
higher ed enrollment rate
higher education
</a>
@ -338,12 +321,10 @@ exports[`rendering of the Categories checks if component renders 1`] = `
<strong>
identified as disadvantaged
</strong>
</p>
<p>
<strong>
IF
</strong>
@ -353,8 +334,6 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
wastewater discharge
</a>
</p>
<p>
@ -368,11 +347,11 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
low income
</a>
AND at or below 20% for
AND 80% or more of adults 15 or older are not enrolled in
<a
href="#high-ed-enroll-rate"
>
higher ed enrollment rate
higher education
</a>
@ -389,12 +368,10 @@ exports[`rendering of the Categories checks if component renders 1`] = `
<strong>
identified as disadvantaged
</strong>
</p>
<p>
<strong>
IF
</strong>
@ -422,8 +399,6 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
low life expectancy
</a>
</p>
<p>
@ -437,11 +412,11 @@ exports[`rendering of the Categories checks if component renders 1`] = `
>
low income
</a>
AND at or below 20% for
AND 80% or more of adults 15 or older are not enrolled in
<a
href="#high-ed-enroll-rate"
>
higher ed enrollment rate
higher education
</a>
@ -458,12 +433,10 @@ exports[`rendering of the Categories checks if component renders 1`] = `
<strong>
identified as disadvantaged
</strong>
</p>
<p>
<strong>
IF
</strong>
@ -474,21 +447,18 @@ exports[`rendering of the Categories checks if component renders 1`] = `
low median income
</a>
as a percentage of area median income OR
<a
href="#ling-iso"
>
linguistic isolation
</a>
OR
OR
<a
href="#unemploy"
>
unemployment
</a>
OR
percent individuals in households at or below 100% Federal
OR percent individuals in households at or below 100% Federal
<a
href="#poverty"
>
@ -498,23 +468,20 @@ exports[`rendering of the Categories checks if component renders 1`] = `
</p>
<p>
<strong>
AND
</strong>
is at or less than 90% for
10% or more of adults 25 or older have not attained a
<a
href="#high-school"
>
high school degree attainment rate
high school degree
</a>
for adults 25 years and older AND
at or below 20% for
AND 80% or more of adults 15 or older are not enrolled in
<a
href="#high-ed-enroll-rate"
>
higher ed enrollment rate
higher education
</a>

View file

@ -12,12 +12,10 @@ exports[`rendering of the CategoryCard checks if component renders 1`] = `
<strong>
identified as disadvantaged
</strong>
</p>
<p>
<strong>
IF
</strong>
@ -39,8 +37,6 @@ exports[`rendering of the CategoryCard checks if component renders 1`] = `
>
expected population loss rate
</a>
</p>
<p>
@ -54,11 +50,11 @@ exports[`rendering of the CategoryCard checks if component renders 1`] = `
>
low income
</a>
AND at or below 20% for
AND 80% or more of adults 15 or older are not enrolled in
<a
href="#high-ed-enroll-rate"
>
higher ed enrollment rate
higher education
</a>

View file

@ -0,0 +1,80 @@
/* eslint-disable valid-jsdoc */
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import * as styles from './datasetCard.module.scss';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
interface IDatasetCardProps {
datasetCardProps: METHODOLOGY_COPY.IIndicators
}
/**
* This component will take in a card and render a dataset card
*
* @param {IDatasetCardProps}
* @return {JSX.Element}
*/
const DatasetCard = ({datasetCardProps}:IDatasetCardProps) => {
const intl = useIntl();
return (
<div className={styles.datasetCard} id={datasetCardProps.domID}>
{/* Dataset header */}
<h3 className={styles.datasetCardIndicator}>{datasetCardProps.indicator}</h3>
{/* Dataset description */}
<div className={styles.datasetCardDescription}>
{datasetCardProps.description}
</div>
{/* Dataset note */}
{datasetCardProps.note && <div className={styles.datasetCardDescription}>
<p>{datasetCardProps.note}</p>
</div>}
<ul className={styles.datasetCardList}>
{/* Dataset Used in */}
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.USED_IN)}
</span>
{datasetCardProps.usedIn}
</li>
{/* Dataset Responsible Party */}
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.RESP_PARTY)}
</span>
{datasetCardProps.responsibleParty}
</li>
{datasetCardProps.sources.map((dataSource, index) => (
<React.Fragment key={index}>
{/* Dataset Source */}
<li className={styles.datasetCardListItemSource}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.SOURCE)}
</span>
{dataSource.source}
</li>
{/* Dataset Available for */}
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.AVAILABLE_FOR)}
</span>
{dataSource.availableFor}
</li>
</React.Fragment>
))}
</ul>
</div>
);
};
export default DatasetCard;

View file

@ -1,68 +1,3 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import * as styles from './datasetCard.module.scss';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
interface IDatasetCardProps {
datasetCardProps: METHODOLOGY_COPY.IIndicators
}
const DatasetCard = ({datasetCardProps}:IDatasetCardProps) => {
const intl = useIntl();
return (
<div className={styles.datasetCard} id={datasetCardProps.domID}>
{/* Dataset header */}
<h3 className={styles.datasetCardIndicator}>{datasetCardProps.indicator}</h3>
{/* Dataset description */}
<div className={styles.datasetCardDescription}>
{datasetCardProps.description}
</div>
<ul className={styles.datasetCardList}>
{/* Dataset Used in */}
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.USED_IN)}
</span>
{datasetCardProps.usedIn}
</li>
{/* Dataset Responsible Party */}
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.RESP_PARTY)}
</span>
{datasetCardProps.responsibleParty}
</li>
{datasetCardProps.sources.map((dataSource) => (
<>
{/* Dataset Source */}
<li className={styles.datasetCardListItemSource}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.SOURCE)}
</span>
{dataSource.source}
</li>
{/* Dataset Available for */}
<li className={styles.datasetCardListItem}>
<span className={styles.datasetCardLabels}>
{intl.formatMessage(METHODOLOGY_COPY.DATASET_CARD_LABELS.AVAILABLE_FOR)}
</span>
{dataSource.availableFor}
</li>
</>
))}
</ul>
</div>
);
};
import DatasetCard from './DatasetCard';
export default DatasetCard;

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../../test/testHelpers';
import DatasetCard from '../../DatasetCard';
import DatasetCard from '../DatasetCard';
import * as METHODOLOGY_COPY from '../../../data/copy/methodology';

View file

@ -34,8 +34,8 @@ const DatasetContainer = () => {
<Grid row>
<Grid col={12}>
<div className={styles.datasetCardsContainer}>
{METHODOLOGY_COPY.INDICATORS.map((card) => <DatasetCard
key={card.indicator}
{METHODOLOGY_COPY.INDICATORS.map((card, index) => <DatasetCard
key={index}
datasetCardProps={card}
/>)}
</div>

View file

@ -101,11 +101,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
id="high-ed-enroll-rate"
>
<h3>
Higher ed enrollment rate
Higher education non-enrollment
</h3>
<div>
Percent of people who are currently enrolled in college or graduate school.
Percent of people 15 or older who are not currently enrolled in college, university, or graduate school.
</div>
<ul>
@ -140,7 +140,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<span>
Available for:
</span>
All U.S. states and the District of Columbia
All U.S. states, the District of Columbia, and Puerto Rico
</li>
</ul>
</div>
@ -380,8 +380,6 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<span>
Source:
</span>
<a
class="usa-link usa-link--external"
data-cy=""
@ -391,8 +389,7 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
>
Fusion of model and monitor data
</a>
from 2017 as compiled by EPAs EJSCREEN, sourced from EPA National Air
Toxics Assessment (NATA) and the U.S. Department of Transportation (DOT) traffic data
from 2017 as compiled by EPAs EJSCREEN, sourced from EPA National Air Toxics Assessment (NATA) and the U.S. Department of Transportation (DOT) traffic data
</li>
<li>
@ -995,23 +992,19 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<div>
Average number of years of life a person who has attained a given age can expect to live.
<p>
<strong>
Note:
</strong>
Unlike most of the other datasets, high values of this data indicate low burdens. For
percentile calculations of burden, the percentile is calculated in reverse order, so that the
census tract with the highest life expectancy relative to area life expectancy (lowest burden
on this measure) is at the 0th percentile, and the census tract with the lowest life
expectancy relative to area life expectancy (highest burden on this measure) is at the
100th percentile. Census tracts with the highest number have the lowest life expectancy.
</p>
</div>
<div>
<p>
<strong>
Note:
</strong>
The percentiles for this dataset have been reversed so that census tracts with lower numbers have higher life expectancies and the census tracts with higher numbers have lower life expectancy when compared to life expectancy in the area.
</p>
</div>
<ul>
<li>
<span>
@ -1057,23 +1050,19 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
<div>
Median income of the census tract calculated as a percent of the areas median income.
<p>
<strong>
Note:
</strong>
Unlike most of the other datasets, high values of this data indicate low burdens. For
percentile calculations of burden, the percentile is calculated in reverse order, so that the
census tract with the highest median income relative to area median income (lowest burden on this
measure) is at the 0th percentile, and the census tract with the lowest median income relative to
area median income (highest burden on this measure) is at the 100th percentile. Census tracts with
the highest number have the lowest median income.
</p>
</div>
<div>
<p>
<strong>
Note:
</strong>
The percentiles for this dataset have been reversed so that census tracts with lower numbers have higher median incomes and census tracts with the higher numbers have lower median income when compared to area median income.
</p>
</div>
<ul>
<li>
<span>
@ -1319,12 +1308,11 @@ exports[`rendering of the DatasetContainer checks if various text fields are vis
id="high-school"
>
<h3>
High school degree attainment rate
High school degree non-attainment
</h3>
<div>
Percent of people ages 25 years or older in a census tract whose
education level is less than a high school diploma.
Percent of people age 25 years or older in a census tract whose education level is less than a high school diploma.
</div>
<ul>

View file

@ -0,0 +1,27 @@
@use '../../styles/design-system.scss' as *;
// styles for all burden containers
@mixin baseBurdenContainer {
display: flex;
@include u-text('bold');
.burdenQuestion {
flex: 0 1 77%;
@include typeset('sans', '2xs', 2);
}
.burdenValue {
margin-left: 2.2rem;
}
}
// styles for first burden container
.exceedBurdenContainer {
@include baseBurdenContainer();
@include u-padding-bottom(1);
}
// styles for second burden container
// .exceedBurdenContainer ~ .exceedBurdenContainer {
// @include baseBurdenContainer();
// }

View file

@ -0,0 +1,14 @@
declare namespace ExceedBurdenNamespace {
export interface IExceedBurden {
exceedBurdenContainer: string;
burdenQuestion: string;
burdenValue: string;
}
}
declare const ExceedBurdenModule: ExceedBurdenNamespace.IExceedBurden & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: ExceedBurdenNamespace.IExceedBurden;
};
export = ExceedBurdenModule;

View file

@ -0,0 +1,45 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import ExceedBurden from './ExceedBurden';
import * as EXPLORE_COPY from '../../data/copy/explore';
describe('test rendering of Exceeds one or more burdens when', () => {
it('is burdended', () => {
const {asFragment} = render(
<LocalizedComponent>
<ExceedBurden text={EXPLORE_COPY.SIDE_PANEL_SPACERS.EXCEED_ONE_OR_MORE} isBurdened={true}/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
it('is NOT burdended', () => {
const {asFragment} = render(
<LocalizedComponent>
<ExceedBurden text={EXPLORE_COPY.SIDE_PANEL_SPACERS.EXCEED_ONE_OR_MORE} isBurdened={false}/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
});
describe('test rendering of Exceeds both socioeco burdens when', () => {
it('is burdended', () => {
const {asFragment} = render(
<LocalizedComponent>
<ExceedBurden text={EXPLORE_COPY.SIDE_PANEL_SPACERS.EXCEED_ONE_OR_MORE} isBurdened={true}/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
it('is NOT burdended', () => {
const {asFragment} = render(
<LocalizedComponent>
<ExceedBurden text={EXPLORE_COPY.SIDE_PANEL_SPACERS.EXCEED_BOTH_SOCIO} isBurdened={false}/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,23 @@
import React from 'react';
import * as styles from './ExceedBurden.module.scss';
import * as EXPLORE_COPY from '../../data/copy/explore';
interface IExceedBurden {
text: React.ReactElement;
isBurdened: boolean;
}
const ExceedBurden = ({text, isBurdened}:IExceedBurden) => {
return (
<div className={styles.exceedBurdenContainer}>
<div className={styles.burdenQuestion}>
{text}
</div>
<div className={styles.burdenValue}>
{isBurdened ? EXPLORE_COPY.SIDE_PANEL_SPACERS.YES : EXPLORE_COPY.SIDE_PANEL_SPACERS.NO}
</div>
</div>
);
};
export default ExceedBurden;

View file

@ -0,0 +1,53 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test rendering of Exceeds both socioeco burdens when is NOT burdended 1`] = `
<DocumentFragment>
<div>
<div>
At or above both associated thresholds?
</div>
<div>
No
</div>
</div>
</DocumentFragment>
`;
exports[`test rendering of Exceeds both socioeco burdens when is burdended 1`] = `
<DocumentFragment>
<div>
<div>
At or above at least one threshold?
</div>
<div>
Yes
</div>
</div>
</DocumentFragment>
`;
exports[`test rendering of Exceeds one or more burdens when is NOT burdended 1`] = `
<DocumentFragment>
<div>
<div>
At or above at least one threshold?
</div>
<div>
No
</div>
</div>
</DocumentFragment>
`;
exports[`test rendering of Exceeds one or more burdens when is burdended 1`] = `
<DocumentFragment>
<div>
<div>
At or above at least one threshold?
</div>
<div>
Yes
</div>
</div>
</DocumentFragment>
`;

View file

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

View file

@ -4,14 +4,12 @@
@mixin indicator {
display: flex;
flex-direction: column;
@include u-padding-bottom(3);
&:last-child {
border-bottom: none;
@include u-padding-bottom(0);
}
@include u-padding-top(1.5);
@include u-padding-bottom(1.5);
.indicatorRow {
display: flex;
justify-content: space-between;
@media screen and (max-width: $mobileBreakpoint) {
flex: 1 0 40%;
@ -21,15 +19,16 @@
}
.indicatorName {
flex: 0 1 77%;
// flex: 0 1 77%;
flex-basis: 60%;
display: flex;
flex-direction: column;
@include typeset('sans', '2xs', 2);
@include u-text('bold');
@include u-text('medium');
.indicatorDesc {
@include typeset('sans', '3xs', 2);
@include u-text('normal');
@include u-text('thin');
max-width: 12rem;
@include u-margin-top(0);
@media screen and (max-width: 1024px) {
@ -38,13 +37,47 @@
}
}
.indicatorValue {
margin-left: 2.2rem;
.indicatorSuperscript {
top: -0.2em
.indicatorValueCol {
display: flex;
flex-direction: column;
.indicatorValueRow {
display: flex;
align-self: end;
.indicatorValue {
margin-left: 2.2rem;
.indicatorSuperscript {
top: -0.2em
}
}
.indicatorArrow {
margin-bottom: -.375rem;
img {
max-width: none;
height: 1.5rem;
width: 1.5rem;
}
.unavailable {
opacity: .2;
}
}
}
}
.indicatorValueSubText{
display: flex;
flex-direction: column;
align-self: flex-end;
text-align: right;
@include typeset('sans', '3xs', 2);
@include u-text('thin');
}
}
}
}
@ -55,5 +88,27 @@
.disadvantagedIndicator {
@include indicator;
@include u-text('blue-warm-70v');
@include u-bg('blue-warm-10');
// A darker bg color:
// background-color: #D2DAE3;
// Add a border
// border: 1px solid #1A4480;
margin: 0 -20px 1px -20px;
@include u-padding-left(2.5);
@include u-padding-right(2.5);
// Overwrite indicator mixin with bolder fonts for disadv. indicator
.indicatorRow {
.indicatorName {
@include u-text('bold');
.indicatorDesc {
@include u-text('normal');
}
}
}
}

View file

@ -4,8 +4,13 @@ declare namespace IndicatorNamespace {
indicatorBoxAdditional:string;
indicatorRow:string;
indicatorName:string;
indicatorValueCol:string;
indicatorValueRow:string;
indicatorValue:string;
indicatorSuperscript:string;
indicatorArrow:string;
unavailable:string;
indicatorValueSubText:string;
indicatorDesc:string;
disadvantagedIndicator:string;
}

View file

@ -1,13 +1,18 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import {render, screen} from '@testing-library/react';
import {LocalizedComponent} from '../../test/testHelpers';
import Indicator, {readablePercentile} from './Indicator';
import {indicatorInfo} from '../AreaDetail';
import Indicator, {IndicatorValueIcon, IndicatorValueSubText, DisplayStatUnit} from './Indicator';
import {indicatorInfo} from '../AreaDetail/AreaDetail';
import * as EXPLORE_COPY from '../../data/copy/explore';
const highSchool:indicatorInfo = {
label: 'some label',
description: 'some description',
value: 97,
isDisadvagtaged: true,
isPercent: true,
threshold: 20,
};
describe('rendering of the Indicator', () => {
@ -22,9 +27,160 @@ describe('rendering of the Indicator', () => {
});
});
describe('tests the readablePercentile function', () => {
expect(readablePercentile(.98)).toEqual(98);
expect(readablePercentile(.07)).toEqual(7);
expect(readablePercentile(.123)).toEqual(12);
expect(readablePercentile(.789)).toEqual(79);
describe('test rendering of Indicator value icons', () => {
it('renders the up arrow when value is above threshold', () => {
const {asFragment} = render(
<LocalizedComponent>
<IndicatorValueIcon
value={90}
isAboveThresh={true}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
screen.getByAltText(EXPLORE_COPY.SIDE_PANEL_VALUES.IMG_ALT_TEXT.ARROW_UP.defaultMessage);
});
it('renders the down arrow when the value is above the threshold', () => {
const {asFragment} = render(
<LocalizedComponent>
<IndicatorValueIcon
value={13}
isAboveThresh={false}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
screen.getByAltText(EXPLORE_COPY.SIDE_PANEL_VALUES.IMG_ALT_TEXT.ARROW_DOWN.defaultMessage);
});
it('renders the down arrow when the value is zero', () => {
const {asFragment} = render(
<LocalizedComponent>
<IndicatorValueIcon
value={0}
isAboveThresh={false}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
screen.getByAltText(EXPLORE_COPY.SIDE_PANEL_VALUES.IMG_ALT_TEXT.ARROW_DOWN.defaultMessage);
});
it('renders the unavailable icon when the value is null', () => {
const {asFragment} = render(
<LocalizedComponent>
<IndicatorValueIcon
value={null}
isAboveThresh={false}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
screen.getByAltText(EXPLORE_COPY.SIDE_PANEL_VALUES.IMG_ALT_TEXT.UNAVAILABLE.defaultMessage);
});
});
describe('test rendering of Indicator value sub-text', () => {
it('renders the "above 90 percentile"', () => {
const {asFragment} = render(
<LocalizedComponent>
<IndicatorValueSubText
value={95}
isAboveThresh={true}
threshold={90}
isPercent={false}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
console.log(asFragment());
});
it('renders the "below 90 percentile"', () => {
const {asFragment} = render(
<LocalizedComponent>
<IndicatorValueSubText
value={89}
isAboveThresh={false}
threshold={90}
isPercent={false}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
it('renders the "data is not available"', () => {
const {asFragment} = render(
<LocalizedComponent>
<IndicatorValueSubText
value={null}
isAboveThresh={false}
threshold={90}
isPercent={false}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
});
describe('test that the unit suffix renders correctly', ()=> {
it('renders correctly when the value is a percentile', () => {
const lowLife:indicatorInfo = {
label: 'some label',
description: 'some description',
value: 97,
isDisadvagtaged: true,
isPercent: false,
threshold: 20,
};
const {asFragment} = render(
<LocalizedComponent>
<DisplayStatUnit
indicator={lowLife}
displayStat={90}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
it('renders correctly when the value is a percent', () => {
const lowLife:indicatorInfo = {
label: 'some label',
description: 'some description',
value: 97,
isDisadvagtaged: true,
isPercent: true,
threshold: 20,
};
const {asFragment} = render(
<LocalizedComponent>
<DisplayStatUnit
indicator={lowLife}
displayStat={90}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
it('renders correctly when the value is a null', () => {
const lowLife:indicatorInfo = {
label: 'some label',
description: 'some description',
value: null,
isDisadvagtaged: true,
isPercent: false,
};
const {asFragment} = render(
<LocalizedComponent>
<DisplayStatUnit
indicator={lowLife}
displayStat={null}
/>
</LocalizedComponent>,
);
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -1,18 +1,126 @@
import React from 'react';
import {indicatorInfo} from '../AreaDetail';
import {useIntl} from 'gatsby-plugin-intl';
import {indicatorInfo} from '../AreaDetail/AreaDetail';
import * as styles from './Indicator.module.scss';
import * as constants from '../../data/constants';
import * as EXPLORE_COPY from '../../data/copy/explore';
// @ts-ignore
import downArrow from '/node_modules/uswds/dist/img/usa-icons/arrow_downward.svg';
// @ts-ignore
import upArrow from '/node_modules/uswds/dist/img/usa-icons/arrow_upward.svg';
// @ts-ignore
import unAvailable from '/node_modules/uswds/dist/img/usa-icons/do_not_disturb.svg';
interface IIndicator {
indicator: indicatorInfo,
}
export const readablePercentile = (percentile: number | null) => {
return percentile !== null ? Math.round(percentile * 100) : 'N/A';
interface IIndicatorValueIcon {
value: number | null,
isAboveThresh: boolean,
};
interface IIndicatorValueSubText {
value: number | null,
isAboveThresh: boolean,
threshold: number,
isPercent: boolean | undefined,
}
interface IDisplayStatUnit {
indicator: indicatorInfo,
displayStat: number | null,
}
/**
* This component will determine what indicator's icon should be (arrowUp, arrowDown or unavailable) and
* return the appropriate JSX.
*
* @param {number | null} props
* @return {JSX.Element}
*/
export const IndicatorValueIcon = ({value, isAboveThresh}: IIndicatorValueIcon) => {
const intl = useIntl();
if (value == null) {
return <img className={styles.unavailable}
src={unAvailable}
alt={intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_VALUES.IMG_ALT_TEXT.UNAVAILABLE)}
/>;
} else {
return isAboveThresh ?
<img
src={upArrow}
alt={intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_VALUES.IMG_ALT_TEXT.ARROW_UP)}
/> :
<img
src={downArrow}
alt={intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_VALUES.IMG_ALT_TEXT.ARROW_DOWN)}
/>;
}
};
/**
* This component will determine the sub-text of the indicator's value, some examples could be
* "above 90th percentile"
* "below 20 percent"
* "data is not available"
*
* Todo: refactor into single component, add to i18n and add to tests
*
* @return {JSX.Element}
*/
export const IndicatorValueSubText = ({value, isAboveThresh, threshold, isPercent}:IIndicatorValueSubText) => {
return value == null ?
<div>
{EXPLORE_COPY.SIDE_PANEL_VALUES.UNAVAILBLE_MSG}
</div> :
<React.Fragment>
<div>
{
isAboveThresh ?
EXPLORE_COPY.SIDE_PANEL_VALUES.ABOVE :
EXPLORE_COPY.SIDE_PANEL_VALUES.BELOW
}
{`${threshold ? threshold : 90}`}
{!isPercent && `th`}
</div>
<div>
{
isPercent ?
EXPLORE_COPY.SIDE_PANEL_VALUES.PERCENT :
EXPLORE_COPY.SIDE_PANEL_VALUES.PERCENTILE
}
</div>
</React.Fragment>;
};
/**
* This component will return the value suffix as either a percent or
* ordinal value of the displayed statistic
*
* @return {JSX.Element}
*/
export const DisplayStatUnit = ({indicator, displayStat}:IDisplayStatUnit) => {
if (indicator.value !== null) {
return indicator.isPercent ?
<span>{`%`}</span> :
<sup className={styles.indicatorSuperscript}>
<span>{getSuperscriptOrdinal(displayStat)}</span>
</sup>;
} else {
return <React.Fragment></React.Fragment>;
}
};
// Todo: Add internationalization to superscript ticket #582
export const getSuperscriptOrdinal = (percentile: number | string) => {
export const getSuperscriptOrdinal = (percentile: number | string | null) => {
if (percentile === null) return '';
if (typeof percentile === 'number') {
const englishOrdinalRules = new Intl.PluralRules('en', {
type: 'ordinal',
@ -29,26 +137,66 @@ export const getSuperscriptOrdinal = (percentile: number | string) => {
}
};
/**
* This component will return the list element which will be the indicator row in the side panel
*
* @param {IIndicator} indicator
* @return {JSX.Element}
*/
const Indicator = ({indicator}:IIndicator) => {
// Convert the decimal value to a stat to display
const displayStat = indicator.value !== null ? Math.round(indicator.value * 100) : null;
// If the threshold exists, set it, otherwise set it to the default value
const threshold = indicator.threshold ? indicator.threshold : constants.DEFAULT_THRESHOLD_PERCENTILE;
// A boolean to represent if the indicator is above or below the threshold
const isAboveThresh = displayStat !== null && displayStat >= threshold ? true : false;
return (
<li
className={indicator.isDisadvagtaged ? styles.disadvantagedIndicator : styles.indicatorBoxMain}
data-cy={'indicatorBox'}>
data-cy={'indicatorBox'}
data-testid='indicator-box'>
<div className={styles.indicatorRow}>
{/* Indicator name and description*/}
<div className={styles.indicatorName}>
{indicator.label}
<div className={styles.indicatorDesc}>
{indicator.description}
</div>
</div>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
{indicator.isPercent ?
<span>{`%`}</span> :
<sup className={styles.indicatorSuperscript}>
<span>{getSuperscriptOrdinal(readablePercentile(indicator.value))}</span>
</sup>
}
{/* Indicator value, icon and subtext */}
<div className={styles.indicatorValueCol}>
<div className={styles.indicatorValueRow}>
{/* Indicator value */}
<div className={styles.indicatorValue}>
{displayStat}
<DisplayStatUnit indicator={indicator} displayStat={displayStat}/>
</div>
{/* Indicator icon - up arrow, down arrow, or unavailable */}
<div className={styles.indicatorArrow}>
<IndicatorValueIcon
value={displayStat}
isAboveThresh={isAboveThresh}
/>
</div>
</div>
{/* Indicator sub-text */}
<div className={styles.indicatorValueSubText}>
<IndicatorValueSubText
value={displayStat}
isAboveThresh={isAboveThresh}
threshold={threshold}
isPercent={indicator.isPercent}
/>
</div>
</div>
</div>
</li>

View file

@ -4,6 +4,7 @@ exports[`rendering of the Indicator checks if component renders 1`] = `
<DocumentFragment>
<li
data-cy="indicatorBox"
data-testid="indicator-box"
>
<div>
<div>
@ -13,14 +14,116 @@ exports[`rendering of the Indicator checks if component renders 1`] = `
</div>
</div>
<div>
9700
<sup>
<span>
th
</span>
</sup>
<div>
<div>
9700
<span>
%
</span>
</div>
<div>
<img
alt="an icon for the up arrow"
src="test-file-stub"
/>
</div>
</div>
<div>
<div>
above 20
</div>
<div>
percent
</div>
</div>
</div>
</div>
</li>
</DocumentFragment>
`;
exports[`test rendering of Indicator value icons renders the down arrow when the value is above the threshold 1`] = `
<DocumentFragment>
<img
alt="an icon for the down arrow"
src="test-file-stub"
/>
</DocumentFragment>
`;
exports[`test rendering of Indicator value icons renders the down arrow when the value is zero 1`] = `
<DocumentFragment>
<img
alt="an icon for the down arrow"
src="test-file-stub"
/>
</DocumentFragment>
`;
exports[`test rendering of Indicator value icons renders the unavailable icon when the value is null 1`] = `
<DocumentFragment>
<img
alt="an icon to represent data is unavailable"
src="test-file-stub"
/>
</DocumentFragment>
`;
exports[`test rendering of Indicator value icons renders the up arrow when value is above threshold 1`] = `
<DocumentFragment>
<img
alt="an icon for the up arrow"
src="test-file-stub"
/>
</DocumentFragment>
`;
exports[`test rendering of Indicator value sub-text renders the "above 90 percentile" 1`] = `
<DocumentFragment>
<div>
above 90th
</div>
<div>
percentile
</div>
</DocumentFragment>
`;
exports[`test rendering of Indicator value sub-text renders the "below 90 percentile" 1`] = `
<DocumentFragment>
<div>
below 90th
</div>
<div>
percentile
</div>
</DocumentFragment>
`;
exports[`test rendering of Indicator value sub-text renders the "data is not available" 1`] = `
<DocumentFragment>
<div>
data is not available
</div>
</DocumentFragment>
`;
exports[`test that the unit suffix renders correctly renders correctly when the value is a null 1`] = `<DocumentFragment />`;
exports[`test that the unit suffix renders correctly renders correctly when the value is a percent 1`] = `
<DocumentFragment>
<span>
%
</span>
</DocumentFragment>
`;
exports[`test that the unit suffix renders correctly renders correctly when the value is a percentile 1`] = `
<DocumentFragment>
<sup>
<span>
th
</span>
</sup>
</DocumentFragment>
`;

View file

@ -66,3 +66,7 @@
}
}
}
.alert {
@include u-margin-top(4);
}

View file

@ -9,6 +9,7 @@ declare namespace J40HeaderNamespace {
title2BetaPill: string;
betaPill: string;
navLinks: string;
alert: string;
}
}

View file

@ -5,6 +5,7 @@ import {
NavMenuButton,
PrimaryNav,
Grid,
Alert,
} from '@trussworks/react-uswds';
import BetaBanner from '../BetaBanner';
import J40MainGridContainer from '../J40MainGridContainer';
@ -16,6 +17,8 @@ import siteLogo from '../../images/j40-logo-v2.png';
import * as styles from './J40Header.module.scss';
import * as COMMON_COPY from '../../data/copy/common';
const isAlertValid = new Date < COMMON_COPY.ALERTS.EXPIRATION_DATE;
const J40Header = () => {
const intl = useIntl();
const [mobileNavOpen, setMobileNavOpen] = useState(false);
@ -67,8 +70,6 @@ const J40Header = () => {
<GovernmentBanner />
<BetaBanner/>
{/* Remove Usabilty Banner testing deployment to main again!*/}
{/* Logo and Navigation */}
<J40MainGridContainer>
@ -106,6 +107,17 @@ const J40Header = () => {
</Grid>
</J40MainGridContainer>
{/* Alert */}
{isAlertValid && <J40MainGridContainer>
<Alert
className={styles.alert}
type="info"
heading={intl.formatMessage(COMMON_COPY.ALERTS.CENSUS_TRACT.TITLE)}>
{COMMON_COPY.ALERTS.CENSUS_TRACT_DESCRIPTION}
</Alert>
</J40MainGridContainer>
}
</Header>
);
};

View file

@ -268,6 +268,40 @@ exports[`rendering of the J40Header checks if component renders 1`] = `
</div>
</div>
</div>
<div
class="grid-container-desktop-lg"
data-testid="gridContainer"
>
<div
class="usa-alert usa-alert--info"
data-testid="alert"
>
<div
class="usa-alert__body"
>
<h4
class="usa-alert__heading"
>
Improvements to the map on the Explore the tool page
</h4>
<p
class="usa-alert__text"
>
View improvements made to the display of the information for each census tract and
<a
class="usa-link usa-link--external"
data-cy=""
href="mailto:Screeningtool-Support@omb.eop.gov"
rel="noreferrer"
target="_blank"
>
send feedback
</a>
.
</p>
</div>
</div>
</div>
</header>
</DocumentFragment>
`;

View file

@ -27,12 +27,13 @@ import AreaDetail from './AreaDetail';
import MapInfoPanel from './mapInfoPanel';
import MapSearch from './MapSearch';
import TerritoryFocusControl from './territoryFocusControl';
import {getOSBaseMap} from '../data/getOSBaseMap';
// Styles and constants
import {getOSBaseMap} from '../data/getOSBaseMap';
import 'maplibre-gl/dist/maplibre-gl.css';
import * as constants from '../data/constants';
import * as styles from './J40Map.module.scss';
import * as COMMON_COPY from '../data/copy/common';
declare global {
@ -54,6 +55,49 @@ export interface IDetailViewInterface {
properties: constants.J40Properties,
};
/**
* This function will determine the URL for the map tiles. It will read in a string that will designate either
* high or low tiles. It will allow to overide the URL to the pipeline staging tile URL via feature flag.
* Lastly, it allows to set the tiles to be local or via the CDN as well.
*
* @param {string} tilesetName
* @returns {string}
*/
export const featureURLForTilesetName = (tilesetName: string): string => {
const flags = useFlags();
const pipelineStagingBaseURL = `https://justice40-data.s3.amazonaws.com/data-pipeline-staging`;
const XYZ_SUFFIX = '{z}/{x}/{y}.pbf';
if ('stage_hash' in flags) {
// Check if the stage_hash is valid
const regex = /^[0-9]{4}\/[a-f0-9]{40}$/;
if (!regex.test(flags['stage_hash'])) {
console.error(COMMON_COPY.CONSOLE_ERROR.STAGE_URL);
}
return `${pipelineStagingBaseURL}/${flags['stage_hash']}/data/score/tiles/${tilesetName}/${XYZ_SUFFIX}`;
} else {
// The feature tile base URL and path can either point locally or the CDN.
// This is selected based on the DATA_SOURCE env variable.
const featureTileBaseURL = process.env.DATA_SOURCE === 'local' ?
process.env.GATSBY_LOCAL_TILES_BASE_URL :
process.env.GATSBY_CDN_TILES_BASE_URL;
const featureTilePath = process.env.DATA_SOURCE === 'local' ?
process.env.GATSBY_DATA_PIPELINE_SCORE_PATH_LOCAL :
process.env.GATSBY_DATA_PIPELINE_SCORE_PATH;
return [
featureTileBaseURL,
featureTilePath,
process.env.GATSBY_MAP_TILES_PATH,
tilesetName,
XYZ_SUFFIX,
].join('/');
}
};
const J40Map = ({location}: IJ40Interface) => {
/**
@ -304,7 +348,7 @@ const J40Map = ({location}: IJ40Interface) => {
id={constants.LOW_ZOOM_SOURCE_NAME}
type="vector"
promoteId={constants.GEOID_PROPERTY}
tiles={[constants.FEATURE_TILE_LOW_ZOOM_URL]}
tiles={[featureURLForTilesetName('low')]}
maxzoom={constants.GLOBAL_MAX_ZOOM_LOW}
minzoom={constants.GLOBAL_MIN_ZOOM_LOW}
>
@ -330,7 +374,7 @@ const J40Map = ({location}: IJ40Interface) => {
id={constants.HIGH_ZOOM_SOURCE_NAME}
type="vector"
promoteId={constants.GEOID_PROPERTY}
tiles={[constants.FEATURE_TILE_HIGH_ZOOM_URL]}
tiles={[featureURLForTilesetName('high')]}
maxzoom={constants.GLOBAL_MAX_ZOOM_HIGH}
minzoom={constants.GLOBAL_MIN_ZOOM_HIGH}
>

View file

@ -17,6 +17,12 @@ interface ILanguageProps {
isDesktop: boolean
}
/**
* Language component that will allow the user to change languages
*
* @param {boolean} isDesktop
* @return {JSX.Element | null}
*/
const Language = ({isDesktop}:ILanguageProps) => {
const flags = useFlags();

View file

@ -1,20 +0,0 @@
@use '../../styles/design-system.scss' as *;
.lowIncomeContainer {
border: 1px solid #DFE1E2;
@include u-margin-top(4);
@include u-padding-left(4);
@include u-padding-right(3);
@include u-padding-bottom(4);
.lowIncomeTitle {
@include typeset('sans', 'xs', 3);
@include u-text('semibold');
}
.lowIncomeText {
@include typeset('sans', 'xs', 3);
@include u-text('light');
}
};

View file

@ -1,14 +0,0 @@
declare namespace LowIncomeNamespace {
export interface ILowIncomeScss {
lowIncomeContainer: string;
lowIncomeTitle: string;
lowIncomeText: string;
}
}
declare const LowIncomeScssModule: LowIncomeNamespace.ILowIncomeScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: LowIncomeNamespace.ILowIncomeScss;
};
export = LowIncomeScssModule;

View file

@ -1,26 +0,0 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import * as METHODOLOGY_COPY from '../../data/copy/methodology';
import * as styles from './LowIncome.module.scss';
const LowIncome = () => {
const intl = useIntl();
return (
<div className={styles.lowIncomeContainer}>
<p className={styles.lowIncomeTitle}>
<sup>*</sup>
{' '}
{intl.formatMessage(METHODOLOGY_COPY.LOW_INCOME.HEADING)}
</p>
<p className={styles.lowIncomeText}>
{intl.formatMessage(METHODOLOGY_COPY.LOW_INCOME.INFO)}
</p>
</div>
);
};
export default LowIncome;

View file

@ -1,20 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the LowIncome checks if component renders 1`] = `
<DocumentFragment>
<div>
<p>
<sup>
*
</sup>
Low Income
</p>
<p>
At or above 65th percentile for percent of census tract population of households where household
income is at or below 200% of the Federal poverty level
</p>
</div>
</DocumentFragment>
`;

View file

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

View file

@ -17,29 +17,10 @@ const MapWrapper = ({location}: IMapWrapperProps) => {
<J40Map location={location}/>
</Grid>
<Grid row>
<Grid col={7}>
<div className={styles.mapCaptionTextLink}>
{EXPLORE_COPY.DOWNLOAD_DRAFT.PARAGRAPH_1}
</div>
</Grid>
</Grid>
<Grid row>
<Grid col={7}>
<h2>{EXPLORE_COPY.NOTE_ON_TERRITORIES.INTRO}</h2>
<p>{EXPLORE_COPY.NOTE_ON_TERRITORIES.PARA_1}</p>
<p>{EXPLORE_COPY.NOTE_ON_TERRITORIES.PARA_2}</p>
<p>{EXPLORE_COPY.NOTE_ON_TERRITORIES.PARA_3}</p>
<p>{EXPLORE_COPY.NOTE_ON_TERRITORIES.PARA_4}</p>
</Grid>
</Grid>
<Grid row>
<Grid col={7}>
<h2>{EXPLORE_COPY.NOTE_ON_TRIBAL_NATIONS.INTRO}</h2>
<p>{EXPLORE_COPY.NOTE_ON_TRIBAL_NATIONS.PARA_1}</p>
</Grid>
<Grid desktop={{col: 7}} tablet={{col: 10}} col={12}>
<div className={styles.mapCaptionTextLink}>
{EXPLORE_COPY.DOWNLOAD_DRAFT.PARAGRAPH_1}
</div>
</Grid>
</>
);

View file

@ -12,18 +12,18 @@ exports[`rendering of the MethodologyFormula checks if component renders 1`] = `
<p>
<span>
<strong>
IF
</span>
</strong>
the census tract is above the threshold for one or more environmental or climate indicators
</p>
<p>
<span>
<strong>
AND
</span>
</strong>
the census tract is above the threshold for the socioeconomic indicators
</p>

View file

@ -1,10 +1,11 @@
@use '../../styles/design-system.scss' as *;
.tagContainer {
align-self: flex-end;
@include u-margin-bottom(1);
@include u-margin-right(1);
.tag {
@include u-bg("yellow-20v");
@include u-bg("gray-cool-10");
color: black;
border-radius: 5px;
@include u-text('bold');
@ -12,6 +13,8 @@
}
.container {
display: flex;
flex-direction: column;
@include u-padding-top(2.5);
.link, .link:visited {

View file

@ -1,6 +1,5 @@
import React from 'react';
import {Link} from 'gatsby';
import {useIntl} from 'gatsby-plugin-intl';
import {useIntl, Link} from 'gatsby-plugin-intl';
import {Button, Tag} from '@trussworks/react-uswds';
import * as styles from './PublicEngageButton.module.scss';

View file

@ -8,11 +8,11 @@ exports[`rendering of the PublicEngageButton checks if component renders 1`] = `
class="usa-tag"
data-testid="tag"
>
NEW
UPDATED
</span>
</div>
<a
href="/public-engagement"
href="/en/public-engagement"
>
<button
class="usa-button usa-button--icon"

View file

@ -19,8 +19,9 @@ export interface IPublicEvent {
DESC: JSX.Element,
NUMBER: Number,
IMAGE: React.ReactElement | string,
EXPIRED_IMG: React.ReactElement | string,
FIELDS: JSX.Element,
REG_LINK: string,
REG_LINK?: string | null,
DATA_CY: string,
}
}
@ -29,24 +30,27 @@ export interface IPublicEvent {
const PublicEvent = ({event}:IPublicEvent) => {
const intl = useIntl();
const eventName = event.NUMBER === 0 ?
`CEJST ${intl.formatMessage(event.NAME)}` :
`CEJST ${intl.formatMessage(event.NAME)} #${event.NUMBER}`;
const isEventExpired = new Date() > event.DATE;
return (
<CollectionItem
variantComponent={
<CollectionThumbnail src={event.IMAGE} alt="Alt text" />
<CollectionThumbnail src={isEventExpired ? event.EXPIRED_IMG : event.IMAGE} alt="Alt text" />
}>
{/* Heading */}
<CollectionHeading>
<LinkTypeWrapper
linkText={`CEJST
${intl.formatMessage(event.NAME)}
#${event.NUMBER}
`}
{isEventExpired ? eventName : <LinkTypeWrapper
linkText={eventName}
internal={false}
url={event.REG_LINK}
openUrlNewTab={true}
dataCy={event.DATA_CY}
/>
/>}
</CollectionHeading>
{/* Description */}
@ -65,7 +69,7 @@ const PublicEvent = ({event}:IPublicEvent) => {
{/* Registration Link */}
<CollectionDescription className={styles.description}>
<a href={event.REG_LINK} target={'_blank'} rel="noreferrer">
<Button type='button'>
<Button type='button' disabled={isEventExpired ? true : false}>
{intl.formatMessage(PUBLIC_ENGAGE_COPY.EVENT_FIELDS.REG_LINK)}
</Button>
</a>

View file

@ -16,18 +16,7 @@ exports[`rendering of the PublicEvent checks if component renders 1`] = `
<h3
class="usa-collection__heading"
>
<a
class="usa-link usa-link--external"
data-cy="mar-9-reg-link-block"
href="https://pitc.zoomgov.com/webinar/register/WN_D-Om_xXhTtiLv71y3Rr1CQ"
rel="noreferrer"
target="_blank"
>
CEJST
training session
#1
</a>
CEJST training session #1
</h3>
<p
class="usa-collection__description"
@ -59,6 +48,7 @@ exports[`rendering of the PublicEvent checks if component renders 1`] = `
<button
class="usa-button"
data-testid="button"
disabled=""
type="button"
>
Registration link

View file

@ -0,0 +1,21 @@
@use '../../styles/design-system.scss' as *;
.sidePanelInfoContainer {
display: flex;
flex-direction: column;
@include u-padding-right(4);
@include u-padding-left(4);
@include u-padding-bottom(4);
.sidePanelInfoHeading {
@include u-padding-top(2);
font-size: x-large;
line-height: 1.9rem;
}
.sidePanelInfoIcon {
@include u-height(5);
@include u-margin-top(4);
@include u-margin-bottom(-2)
}
}

View file

@ -1,12 +1,8 @@
declare namespace MapIntroductionModuleScssNamespace {
export interface IMapIntroductionModuleScss {
mapIntroContainer: string;
mapIntroHeader: string;
mapIntroText: string;
mapIntroLightbulb: string;
didYouKnowBox: string
didYouKnow: string
didYouKnowText: string
sidePanelInfoContainer: string;
sidePanelInfoHeading: string;
sidePanelInfoIcon: string;
}
}

View file

@ -1,16 +1,16 @@
import * as React from 'react';
import {render} from '@testing-library/react';
import SidePanelInfo from './SidePanelInfo';
import {LocalizedComponent} from '../../test/testHelpers';
import LowIncome from './LowIncome';
describe('rendering of the LowIncome', () => {
describe('rendering of the component', () => {
const {asFragment} = render(
<LocalizedComponent>
<LowIncome />
<SidePanelInfo />
</LocalizedComponent>,
);
it('checks if component renders', () => {
it('expects the render to match snapshot', () => {
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -0,0 +1,60 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
// @ts-ignore
import puzzle from '../../images/sidePanelIcons/puzzle.svg';
// @ts-ignore
import bellCurve from '../../images/sidePanelIcons/bellCurve.svg';
// @ts-ignore
import pieChart from '../../images/sidePanelIcons/pieChart.svg';
// @ts-ignore
import upDown from '../../images/sidePanelIcons/upDown.svg';
import * as styles from './SidePanelInfo.module.scss';
import * as EXPLORE_COPY from '../../data/copy/explore';
const MapIntroduction = () => {
const intl = useIntl();
return (
<aside className={styles.sidePanelInfoContainer}>
<header tabIndex={0} className={styles.sidePanelInfoHeading}>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.TITLE)}
</header>
<p tabIndex={0}>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.PARA1)}
</p>
<img tabIndex={0} className={styles.sidePanelInfoIcon}
src={puzzle}
alt={intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.ALT_TEXT_ICON1)}
/>
<p tabIndex={0}>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.PARA2)}
</p>
<img tabIndex={0} className={styles.sidePanelInfoIcon}
src={bellCurve}
alt={intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.ALT_TEXT_ICON2)}
/>
<p tabIndex={0}>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.PARA3)}
</p>
<img tabIndex={0} className={styles.sidePanelInfoIcon}
src={pieChart}
alt={intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.ALT_TEXT_ICON3)}
/>
<p tabIndex={0}>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.PARA4)}
</p>
<img tabIndex={0} className={styles.sidePanelInfoIcon}
src={upDown}
alt={intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.ALT_TEXT_ICON4)}
/>
<p tabIndex={0}>
{EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE_PARA5}
</p>
</aside>
);
};
export default MapIntroduction;

View file

@ -0,0 +1,76 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering of the component expects the render to match snapshot 1`] = `
<DocumentFragment>
<aside>
<header
tabindex="0"
>
Things to know
</header>
<p
tabindex="0"
>
This tool identifies communities that are marginalized, underserved, and overburdened by pollution. These communities are located in census tracts that are at or above the thresholds in one or more of eight categories of criteria.
</p>
<img
alt="
An icon that has depicts pieces of a block selected mimicing the census block census tracts
"
src="test-file-stub"
tabindex="0"
/>
<p
tabindex="0"
>
The tool uses census tracts that represent about 4,000 people, which is the smallest unit of geography for which consistent data can be displayed on the tool.
</p>
<img
alt="
An icon that a bell curve or gaussian distribution
"
src="test-file-stub"
tabindex="0"
/>
<p
tabindex="0"
>
The tool ranks each census tract using percentiles that show how much burden each tract experiences relative to all other tracts, for each criteria.
</p>
<img
alt="
An icon that depicts a part of pie chart being removed
"
src="test-file-stub"
tabindex="0"
/>
<p
tabindex="0"
>
Percentages are used for certain variables, i.e. those relating to high school achievement rate and to the share of individuals not currently enrolled in higher education.
</p>
<img
alt="
An icon that has an up arrow and a down arrow
"
src="test-file-stub"
tabindex="0"
/>
<p
tabindex="0"
>
Thresholds for each category determine if a tract should be identified as disadvantaged because it has exceeded a certain value for the relevant indicators.
</p>
</aside>
</DocumentFragment>
`;

View file

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

View file

@ -8,29 +8,74 @@ exports[`simulate app starting up, no click on map should match the snapshot of
class="someClassName"
>
<aside>
<header>
Zoom and select a census tract to view data
<header
tabindex="0"
>
Things to know
</header>
<div>
<img
alt="icon showing a lightbulb"
src="test-file-stub"
/>
<div>
<div>
Did you know?
</div>
<cite>
A census tract is generally between 1,200 to 8,000 people, with an average size of 4,000 people.
Census tracts are small, relatively permanent subdivisions of a county defined by the
U.S. Census Bureau and usually cover a contiguous area. The census tract level currently represents the
smallest geographical unit for which publicly-available and nationally-consistent datasets can
be consistently displayed on the tool.
<p
tabindex="0"
>
This tool identifies communities that are marginalized, underserved, and overburdened by pollution. These communities are located in census tracts that are at or above the thresholds in one or more of eight categories of criteria.
</cite>
</div>
</div>
</p>
<img
alt="
An icon that has depicts pieces of a block selected mimicing the census block census tracts
"
src="test-file-stub"
tabindex="0"
/>
<p
tabindex="0"
>
The tool uses census tracts that represent about 4,000 people, which is the smallest unit of geography for which consistent data can be displayed on the tool.
</p>
<img
alt="
An icon that a bell curve or gaussian distribution
"
src="test-file-stub"
tabindex="0"
/>
<p
tabindex="0"
>
The tool ranks each census tract using percentiles that show how much burden each tract experiences relative to all other tracts, for each criteria.
</p>
<img
alt="
An icon that depicts a part of pie chart being removed
"
src="test-file-stub"
tabindex="0"
/>
<p
tabindex="0"
>
Percentages are used for certain variables, i.e. those relating to high school achievement rate and to the share of individuals not currently enrolled in higher education.
</p>
<img
alt="
An icon that has an up arrow and a down arrow
"
src="test-file-stub"
tabindex="0"
/>
<p
tabindex="0"
>
Thresholds for each category determine if a tract should be identified as disadvantaged because it has exceeded a certain value for the relevant indicators.
</p>
</aside>
</div>
</DocumentFragment>

View file

@ -1,6 +1,6 @@
import React from 'react';
import MapIntroduction from './mapIntroduction';
import AreaDetail from './AreaDetail';
import SidePanelInfo from './SidePanelInfo';
interface IMapInfoPanelProps {
className: string,
@ -13,7 +13,7 @@ const MapInfoPanel = ({className, featureProperties, selectedFeatureId}:IMapInfo
<div className={className} >
{(featureProperties && selectedFeatureId ) ?
<AreaDetail properties={featureProperties} /> :
<MapIntroduction />
<SidePanelInfo />
}
</div>
);

View file

@ -1,34 +0,0 @@
.mapIntroContainer {
padding: 20px 20px;
}
.mapIntroHeader {
font-size: xx-large;
line-height: 1.9rem;
padding-top: 0.8rem;
padding-left: 0.3rem;
}
.mapIntroText {
display: flex;
margin-top: 2.4rem;
}
.mapIntroLightbulb {
flex: 1 0 10%;
align-self: flex-start;
}
.didYouKnowBox {
padding-left: 0.4rem;
padding-top: 0.2rem;
font-size: large;
}
.didYouKnow {
font-weight: 600;
}
.didYouKnowText {
width: 95%;
padding-top: 0.3rem;
line-height: 1.5rem;
}

View file

@ -1,16 +0,0 @@
import * as React from 'react';
import {render, screen} from '@testing-library/react';
import MapIntroduction from './mapIntroduction';
import {LocalizedComponent} from '../../src/test/testHelpers';
describe('rendering of the component', () => {
render(
<LocalizedComponent>
<MapIntroduction />
</LocalizedComponent>,
);
it('renders the title', () => {
expect(screen.getByRole('banner')).toHaveTextContent('Zoom and select a census tract to view data');
});
});

View file

@ -1,32 +0,0 @@
import React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
// @ts-ignore
import lightbulbIcon from '/node_modules/uswds/dist/img/usa-icons/lightbulb_outline.svg';
import * as styles from './mapIntroduction.module.scss';
import * as EXPLORE_COPY from '../data/copy/explore';
const MapIntroduction = () => {
const intl = useIntl();
return (
<aside className={styles.mapIntroContainer}>
<header className={styles.mapIntroHeader}>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.TITLE)}
</header>
<div className={styles.mapIntroText}>
<img className={styles.mapIntroLightbulb} src={lightbulbIcon} alt={'icon showing a lightbulb'}/>
<div className={styles.didYouKnowBox}>
<div className={styles.didYouKnow}>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.DID_YOU_KNOW)}
</div>
<cite className={styles.didYouKnowText}>
{intl.formatMessage(EXPLORE_COPY.SIDE_PANEL_INITIAL_STATE.CBG_DEFINITION)}
</cite>
</div>
</div>
</aside>
);
};
export default MapIntroduction;

View file

@ -1,18 +1,20 @@
@use '../styles/design-system.scss' as *;
@mixin baseTerritoryFocus {
position: absolute;
left: .75em;
z-index: 1;
}
.territoryFocusContainer {
// styles for mobile-lg (480px) and greater widths
@include at-media('mobile-lg') {
position: absolute;
left: .75em;
@include baseTerritoryFocus;
top: units(card-lg);
z-index: 10;
}
};
position: absolute;
left: .75em;
@include baseTerritoryFocus;
top: units(9);
z-index: 10;
}