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

@ -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>
`;