Website copy layout styling updates for Tuesday launch (#685)

* Add basic accordion in AreaDetail

* Refactor AreaDetail to use a Grid layout

- adds useWindowSize to detect window resizes for mobile view
- Map and AreaDetail to use Grid
- removes some component styling from J40
- updates snapshot
- MapWrapper to use Grid

* Add custom Accordion styling

- make J40 map a 9:3 Grid layout split
- override native Accordion heading styles
- make the Accordion multi-selectable
- add some dummy data for indicators

* Update AreaDetail to match design

- remove styles in AreaDetail
- increase height of MapInfoPanel
- add Accordian items (indicators)
- updates snapshot

* Add a Beta Tag to the logo

* Change the line height on indicators descriptions

* Update package-lock after the rebase

* Remove threshold from MapLegend

- move feature selected border color to utils
- remove all tooltip logic
- remove all styles associated with tooltips
- add legend label and descript to constants
- refactor tests to be snapshots

* Add borders between additional indicators

* Modify copy and update styles

- add the ordinal superscript back
- update the copy
- update the snapshots

* Add additional indicators keys

* Connect indicator keys to the UI

- update the areaDetail snapshot

* Render additional indicators accordion open onLoad

- update snapshot

* Update copy on About page

* Update copy on indicator descriptions

- update snapshots

* Update the "How you can help section"

- update the snapshot

* Add a comma to "ZIP file will contain..."

* Add the Datasets section to the methodology page

- update snapshot

* Update Methodology process list to trussworks

- remove custom process list
- remove custom CSS from global file
- change copy

* Modify layout of Methodology to using Grid

- modify Dataset section to use Grid
- remove outdated component CSS
- update the snapshot

* Update copy based on product feedback

- update snapshots

* Remove Accordions

- updates snapshots
- white CBG groups will show "Not community of focus"
This commit is contained in:
Vim 2021-09-16 10:21:25 -07:00 committed by GitHub
commit 522872037a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 2029 additions and 1208 deletions

View file

@ -1,9 +1,11 @@
/* eslint-disable quotes */
// External Libs:
import * as React from 'react';
import {useIntl} from 'gatsby-plugin-intl';
import {defineMessages} from 'react-intl';
// Components:
// import {Accordion} from '@trussworks/react-uswds';
// Styles and constants
import * as styles from './areaDetail.module.scss';
@ -13,7 +15,6 @@ export const readablePercentile = (percentile: number) => {
return Math.round(percentile * 100);
};
// Todo: Add internationalization to superscript ticket #582
const getSuperscriptOrdinal = (percentile: number) => {
const englishOrdinalRules = new Intl.PluralRules('en', {
@ -30,18 +31,19 @@ const getSuperscriptOrdinal = (percentile: number) => {
return suffixes[englishOrdinalRules.select(percentile)];
};
// Todo VS: remove threshold data
export const getCategorization = (percentile: number) => {
let categorization;
let categoryCircleStyle;
if (percentile >= constants.SCORE_BOUNDARY_PRIORITIZED ) {
categorization = 'Prioritized';
categorization = 'Community of focus';
categoryCircleStyle = styles.prioritized;
} else if (constants.SCORE_BOUNDARY_THRESHOLD <= percentile && percentile < constants.SCORE_BOUNDARY_PRIORITIZED) {
categorization = 'Threshold';
categorization = 'Not a community of focus';
categoryCircleStyle = styles.threshold;
} else {
categorization = 'Non-prioritized';
categorization = 'Not a community of focus';
categoryCircleStyle = styles.nonPrioritized;
}
return [categorization, categoryCircleStyle];
@ -54,11 +56,6 @@ interface IAreaDetailProps {
const AreaDetail = ({properties}:IAreaDetailProps) => {
const intl = useIntl();
const messages = defineMessages({
cumulativeIndexScore: {
id: 'areaDetail.priorityInfo.cumulativeIndexScore',
defaultMessage: 'Cumulative Index Score',
description: 'the cumulative score of the feature selected',
},
percentile: {
id: 'areaDetail.priorityInfo.percentile',
defaultMessage: 'percentile',
@ -91,7 +88,7 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
},
indicatorColumnHeader: {
id: 'areaDetail.indicators.indicatorColumnHeader',
defaultMessage: 'Indicators',
defaultMessage: 'Indicator',
description: 'the population of the feature selected',
},
percentileColumnHeader: {
@ -104,9 +101,14 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
defaultMessage: 'Poverty',
description: 'Household income is less than or equal to twice the federal "poverty level"',
},
areaMedianIncome: {
id: 'areaDetail.indicator.areaMedianIncome',
defaultMessage: 'Area Median Income',
description: 'calculated as percent of the area median income',
},
education: {
id: 'areaDetail.indicator.education',
defaultMessage: 'Education',
defaultMessage: 'Education, less than high school',
description: 'Percent of people age 25 or older that didnt get a high school diploma',
},
linguisticIsolation: {
@ -120,10 +122,71 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
defaultMessage: 'Unemployment rate',
description: 'Number of unemployed people as a percentage of the labor force',
},
asthma: {
id: 'areaDetail.indicator.asthma',
defaultMessage: 'Asthma',
description: 'have asthma or been diagnosed by a doctor to have asthma',
},
diabetes: {
id: 'areaDetail.indicator.diabetes',
defaultMessage: 'Diabetes',
description: 'diabetes from dr or nurse',
},
dieselPartMatter: {
id: 'areaDetail.indicator.dieselPartMatter',
defaultMessage: 'Diesel particulate matter',
description: 'Diesel particulate matter level in air',
},
energyBurden: {
id: 'areaDetail.indicator.energyBurden',
defaultMessage: 'Energy burden',
description: 'Average annual energy cost ($) divided by household income',
},
femaRisk: {
id: 'areaDetail.indicator.femaRisk',
defaultMessage: 'FEMA Risk index',
description: 'Risk based on 18 natural hazard types, in addition to a'+
"community's social vulnerability and community resilience",
},
heartDisease: {
id: 'areaDetail.indicator.heartDisease',
defaultMessage: 'Heart Disease',
description: 'People ages 18 and up who report ever having been told by a' +
'doctor, nurse, or other health professionals that they had angina or coronary heart disease',
},
houseBurden: {
id: 'areaDetail.indicator.houseBurden',
defaultMessage: 'Housing Burden',
description: 'Households that are low income and spend more than 30% of their income to housing costs',
defaultMessage: 'Housing cost burden',
description: 'People ages 18 and up who report having been told by a doctor,' +
' nurse, or other health professionals that they have diabetes other than diabetes during pregnancy',
},
leadPaint: {
id: 'areaDetail.indicator.leadPaint',
defaultMessage: 'Lead paint',
description: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
},
lifeExpect: {
id: 'areaDetail.indicator.lifeExpect',
defaultMessage: 'Life expectancy',
description: 'Estimated years of life expectancy',
},
pm25: {
id: 'areaDetail.indicator.pm25',
defaultMessage: 'PM2.5',
description: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
},
trafficVolume: {
id: 'areaDetail.indicator.trafficVolume',
defaultMessage: 'Traffic proximity and volume',
description: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
},
wasteWater: {
id: 'areaDetail.indicator.wasteWater',
defaultMessage: 'Wastewater discharge',
description: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
},
});
@ -140,9 +203,10 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
}
// Todo: Ticket #367 will be replacing descriptions with YAML file
const povertyInfo:indicatorInfo = {
label: intl.formatMessage(messages.poverty),
description: 'Household income is less than or equal to twice the federal "poverty level"',
const areaMedianIncome:indicatorInfo = {
label: intl.formatMessage(messages.areaMedianIncome),
description: 'Median income of the census block group calculated as a percent of the metropolitan'+
" areas or state's median income",
value: properties[constants.POVERTY_PROPERTY_PERCENTILE],
};
const eduInfo:indicatorInfo = {
@ -150,44 +214,102 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
description: 'Percent of people age 25 or older that didnt get a high school diploma',
value: properties[constants.EDUCATION_PROPERTY_PERCENTILE],
};
const linIsoInfo:indicatorInfo = {
label: intl.formatMessage(messages.linguisticIsolation),
description: 'Households in which all members speak a non-English language and speak English less than "very well"',
value: properties[constants.LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE],
const poverty:indicatorInfo = {
label: intl.formatMessage(messages.poverty),
description: 'Household income is less than or equal to the federal "poverty level"',
value: properties[constants.POVERTY_PROPERTY_PERCENTILE],
};
const umemployInfo:indicatorInfo = {
label: intl.formatMessage(messages.unemployment),
description: 'Number of unemployed people as a percentage of the labor force',
value: properties[constants.UNEMPLOYMENT_PROPERTY_PERCENTILE],
// const linIsoInfo:indicatorInfo = {
// label: intl.formatMessage(messages.linguisticIsolation),
// eslint-disable-next-line max-len
// description: 'Households in which all members speak a non-English language and speak English less than "very well"',
// value: properties[constants.LINGUISTIC_ISOLATION_PROPERTY_PERCENTILE],
// };
// const umemployInfo:indicatorInfo = {
// label: intl.formatMessage(messages.unemployment),
// description: 'Number of unemployed people as a percentage of the labor force',
// value: properties[constants.UNEMPLOYMENT_PROPERTY_PERCENTILE],
// };
const asthma:indicatorInfo = {
label: intl.formatMessage(messages.asthma),
description: 'People who answer “yes” to both of the questions: “Have you ever been told by' +
' a doctor nurse, or other health professional that you have asthma?” and “Do you still have asthma?"',
value: properties[constants.ASTHMA_PERCENTILE],
};
const diabetes:indicatorInfo = {
label: intl.formatMessage(messages.diabetes),
description: 'People ages 18 and up who report having been told by a doctor, nurse, or other' +
' health professionals that they have diabetes other than diabetes during pregnancy',
value: properties[constants.DIABETES_PERCENTILE],
};
const dieselPartMatter:indicatorInfo = {
label: intl.formatMessage(messages.dieselPartMatter),
description: 'Mixture of particles that is part of diesel exhaust in the air',
value: properties[constants.DIESEL_MATTER_PERCENTILE],
};
const lifeExpect:indicatorInfo = {
label: intl.formatMessage(messages.lifeExpect),
description: 'Estimated years of life expectancy',
value: properties[constants.LIFE_PERCENTILE],
};
const energyBurden:indicatorInfo = {
label: intl.formatMessage(messages.energyBurden),
description: 'Average annual energy cost ($) divided by household income',
value: properties[constants.ENERGY_PERCENTILE],
};
const pm25:indicatorInfo = {
label: intl.formatMessage(messages.pm25),
description: 'Fine inhalable particles, with diameters that are generally 2.5 micrometers and smaller',
value: properties[constants.PM25_PERCENTILE],
};
const leadPaint:indicatorInfo = {
label: intl.formatMessage(messages.leadPaint),
description: 'Housing units built pre-1960, used as an indicator of potential'+
' lead paint exposure in homes',
value: properties[constants.LEAD_PAINT_PERCENTILE],
};
const trafficVolume:indicatorInfo = {
label: intl.formatMessage(messages.trafficVolume),
description: 'Count of vehicles (average annual daily traffic) at major roads within 500 meters,' +
' divided by distance in meters',
value: properties[constants.TRAFFIC_PERCENTILE],
};
const wasteWater:indicatorInfo = {
label: intl.formatMessage(messages.wasteWater),
description: 'Toxic concentrations at stream segments within 500 meters divided by distance in' +
' kilometers',
value: properties[constants.WASTEWATER_PERCENTILE],
};
const femaRisk:indicatorInfo = {
label: intl.formatMessage(messages.femaRisk),
description: 'Expected Annual Loss Score, which is the average economic loss in dollars' +
' resulting from natural hazards each year.',
value: properties[constants.FEMA_PERCENTILE],
};
const heartDisease:indicatorInfo = {
label: intl.formatMessage(messages.heartDisease),
description: 'People ages 18 and up who report ever having been told by a' +
' doctor, nurse, or other health professionals that they had angina or coronary heart disease',
value: properties[constants.HEART_PERCENTILE],
};
const houseBurden:indicatorInfo = {
label: intl.formatMessage(messages.houseBurden),
description: 'Households that are low income and spend more than 30% of their income to housing costs',
description: 'People ages 18 and up who report having been told by a doctor,' +
' nurse, or other health professionals that they have diabetes other than diabetes during pregnancy',
value: properties[constants.HOUSING_BURDEN_PROPERTY_PERCENTILE],
};
const indicators = [povertyInfo, eduInfo, linIsoInfo, umemployInfo, houseBurden];
const indicators = [areaMedianIncome, eduInfo, poverty];
const additionalIndicators = [
asthma, diabetes, dieselPartMatter, energyBurden, femaRisk, heartDisease,
houseBurden, leadPaint, lifeExpect, pm25, trafficVolume, wasteWater,
];
const [categorization, categoryCircleStyle] = getCategorization(score);
return (
<aside className={styles.areaDetailContainer} data-cy={'aside'}>
<header className={styles.topRow }>
<div className={styles.cumulativeIndexScore}>
<div className={styles.topRowTitle}>{intl.formatMessage(messages.cumulativeIndexScore)}</div>
<div className={styles.score} data-cy={'score'}>{`${readablePercentile(score)}`}
<sup className={styles.scoreSuperscript}><span>th</span></sup>
</div>
<div className={styles.topRowSubTitle}>{intl.formatMessage(messages.percentile)}</div>
</div>
<div className={styles.categorization}>
<h6 className={styles.topRowTitle}>{intl.formatMessage(messages.categorization)}</h6>
<div className={styles.priority}>
<div className={categoryCircleStyle} />
<div className={styles.prioritization}>{categorization}</div>
</div>
</div>
</header>
<ul className={styles.censusRow}>
<li>
<span className={styles.censusLabel}>{intl.formatMessage(messages.censusBlockGroup)} </span>
@ -206,27 +328,129 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
<span className={styles.censusText}>{population.toLocaleString()}</span>
</li>
</ul>
<div className={styles.categorization}>
<div className={styles.priority}>
<div className={categoryCircleStyle} />
<h3>{categorization}</h3>
</div>
</div>
<div className={styles.divider}>
<h6>{intl.formatMessage(messages.indicatorColumnHeader)}</h6>
<h6>{intl.formatMessage(messages.percentileColumnHeader)}</h6>
</div>
{indicators.map((indicator, index) => (
<li key={index} className={styles.indicatorBox} data-cy={'indicatorBox'}>
<div>
<h4>{indicator.label}</h4>
<p className={'secondary'}>
{indicator.description}
</p>
</div>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</li>
))}
<>
{
indicators.map((indicator:any, index:number) => {
return <li key={`ind${index}`} className={styles.indicatorBoxMain} data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
<>
{
additionalIndicators.map((indicator:any, index:number) => {
return <li
key={`ind${index}`}
className={styles.indicatorBoxAdditional}
data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
{/* Temporarily remove Accordions and may place back in later, removed unused
className prop as as styles are based on the id of the Accordion Item */}
{/* <Accordion
multiselectable={true}
items={
[
{
id: 'prioritization-indicators',
title: 'Indicators',
content: (
<>
{
indicators.map((indicator:any, index:number) => {
return <li key={`ind${index}`} className={styles.indicatorBoxMain} data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
),
expanded: true,
},
{
id: 'additional-indicators',
title: 'Additional indicators (not used in prioritization)',
content: (
(
<>
{
additionalIndicators.map((indicator:any, index:number) => {
return <li
key={`ind${index}`}
className={styles.indicatorBoxAdditional}
data-cy={'indicatorBox'}>
<div className={styles.indicatorRow}>
<h4 className={styles.indicatorName}>{indicator.label}</h4>
<div className={styles.indicatorValue}>
{readablePercentile(indicator.value)}
<sup className={styles.indicatorSuperscript}><span>
{getSuperscriptOrdinal(readablePercentile(indicator.value))}
</span></sup>
</div>
</div>
<p className={'secondary j40-indicator'}>
{indicator.description}
</p>
</li>;
})
}
</>
)
),
expanded: true,
},
]
}/> */}
</aside>
);