From 1d3af3023b396bf33a159582f5ac10b23e479806 Mon Sep 17 00:00:00 2001 From: Vim USDS Date: Sat, 6 Aug 2022 03:14:38 -0700 Subject: [PATCH] Add tribal info to side panel and feature selection - create a state variable to keep track of weather or not the layer was toggled - allow mapInfoPanel to reset on layer switch - allow AreaDetail to show census and tribal info - allow LayerSelector to set layer toggled - Add selectedFeature to both MapTribal and MapTract components - create various tribal constants for styling - i18n constants --- .../src/components/AreaDetail/AreaDetail.tsx | 187 ++++++++++-------- .../AreaDetail/tests/areaDetail.test.tsx | 7 +- client/src/components/J40Map.tsx | 33 +++- .../LayerSelector/LayerSelector.test.tsx | 4 +- .../LayerSelector/LayerSelector.tsx | 10 +- .../MapTractLayers/MapTractLayers.tsx | 11 +- .../MapTribalLayer/MapTribalLayer.tsx | 41 +++- client/src/components/mapInfoPanel.tsx | 26 ++- client/src/data/constants.tsx | 8 +- client/src/data/copy/explore.tsx | 9 + client/src/intl/en.json | 4 + 11 files changed, 228 insertions(+), 112 deletions(-) diff --git a/client/src/components/AreaDetail/AreaDetail.tsx b/client/src/components/AreaDetail/AreaDetail.tsx index 1fdb081c..54523960 100644 --- a/client/src/components/AreaDetail/AreaDetail.tsx +++ b/client/src/components/AreaDetail/AreaDetail.tsx @@ -22,6 +22,7 @@ import mailIcon from '/node_modules/uswds/dist/img/usa-icons/mail_outline.svg'; interface IAreaDetailProps { properties: constants.J40Properties, hash: string[], + isCensusLayerSelected: boolean, } /** @@ -60,7 +61,8 @@ export interface ICategory { isExceed1MoreBurden: boolean | null, isExceedBothSocioBurdens: boolean | null, } -const AreaDetail = ({properties, hash}: IAreaDetailProps) => { + +const AreaDetail = ({properties, hash, isCensusLayerSelected}: IAreaDetailProps) => { const intl = useIntl(); // console.log the properties of the census that is selected: @@ -72,6 +74,7 @@ const AreaDetail = ({properties, hash}: IAreaDetailProps) => { const countyName = properties[constants.COUNTY_NAME] ? properties[constants.COUNTY_NAME] : "N/A"; const stateName = properties[constants.STATE_NAME] ? properties[constants.STATE_NAME] : "N/A"; const sidePanelState = properties[constants.SIDE_PANEL_STATE]; + const landAreaName = properties[constants.LAND_AREA_NAME]; const isCommunityFocus = score >= constants.SCORE_BOUNDARY_THRESHOLD; @@ -572,96 +575,110 @@ const AreaDetail = ({properties, hash}: IAreaDetailProps) => { {EXPLORE_COPY.SIDE_PANEL_VERION.TITLE} - {/* Census Info */} - + { + isCensusLayerSelected ? ( + <> + {/* Census Info */} + - {/* Disadvantaged? */} -
+ {/* Disadvantaged? */} +
- {/* Questions asking if disadvantaged? */} -
- {EXPLORE_COPY.COMMUNITY.IS_FOCUS} -
- - {/* YES with Dot or NO with no Dot */} -
- {isCommunityFocus ? - <> -

{EXPLORE_COPY.COMMUNITY.OF_FOCUS}

- - : -

{EXPLORE_COPY.COMMUNITY.NOT_OF_FOCUS}

- } -
- - {/* Number of categories exceeded */} -
- {EXPLORE_COPY.numberOfCategoriesExceeded(properties[constants.COUNT_OF_CATEGORIES_DISADV])} -
- - {/* Number of thresholds exceeded */} - {/*
- {EXPLORE_COPY.numberOfThresholdsExceeded(properties[constants.TOTAL_NUMBER_OF_DISADVANTAGE_INDICATORS])} -
*/} - {/* Send Feedback button */} - - - -
+ {/* YES with Dot or NO with no Dot */} +
+ {isCommunityFocus ? + <> +

{EXPLORE_COPY.COMMUNITY.OF_FOCUS}

+ + : +

{EXPLORE_COPY.COMMUNITY.NOT_OF_FOCUS}

+ } +
+ + {/* Number of categories exceeded */} +
+ {EXPLORE_COPY.numberOfCategoriesExceeded(properties[constants.COUNT_OF_CATEGORIES_DISADV])} +
+ + {/* Number of thresholds exceeded */} + {/*
+ {EXPLORE_COPY.numberOfThresholdsExceeded(properties[constants.TOTAL_NUMBER_OF_DISADVANTAGE_INDICATORS])} +
*/} + {/* Send Feedback button */} + + + +
+ + ) : ( + + )} + {/* All category accordions in this component */} - + {isCensusLayerSelected && } ); diff --git a/client/src/components/AreaDetail/tests/areaDetail.test.tsx b/client/src/components/AreaDetail/tests/areaDetail.test.tsx index 1ffff386..232c8bf4 100644 --- a/client/src/components/AreaDetail/tests/areaDetail.test.tsx +++ b/client/src/components/AreaDetail/tests/areaDetail.test.tsx @@ -5,6 +5,7 @@ import {LocalizedComponent} from '../../../test/testHelpers'; import * as constants from '../../../data/constants'; +// Todo: Update tests to take into account tribal layer selected describe('rendering of the AreaDetail', () => { const properties = { [constants.POVERTY_BELOW_100_PERCENTILE]: .12, @@ -27,7 +28,7 @@ describe('rendering of the AreaDetail', () => { it('checks if indicators for NATION is present', () => { const {asFragment} = render( - + , ); expect(asFragment()).toMatchSnapshot(); @@ -41,7 +42,7 @@ describe('rendering of the AreaDetail', () => { const {asFragment} = render( - + , ); expect(asFragment()).toMatchSnapshot(); @@ -59,7 +60,7 @@ describe('rendering of the AreaDetail', () => { const {asFragment} = render( - + , ); expect(asFragment()).toMatchSnapshot(); diff --git a/client/src/components/J40Map.tsx b/client/src/components/J40Map.tsx index 109684a3..d2be5e86 100644 --- a/client/src/components/J40Map.tsx +++ b/client/src/components/J40Map.tsx @@ -85,6 +85,10 @@ const J40Map = ({location}: IJ40Interface) => { const [geolocationInProgress, setGeolocationInProgress] = useState(false); const [isMobileMapState, setIsMobileMapState] = useState(false); const [censusSelected, setCensusSelected] = useState(true); + + // In order to detect that the layer has been toggled (between census and tribal), + // this state variable will hold that information + const [layerToggled, setLayerToggled] = useState(false); const {width: windowWidth} = useWindowSize(); const mapRef = useRef(null); @@ -156,6 +160,9 @@ const J40Map = ({location}: IJ40Interface) => { } } else { // This else clause will fire when the ID is null or empty. This is the case where the map is clicked + + setLayerToggled(false); + // @ts-ignore const feature = event.features && event.features[0]; @@ -308,7 +315,11 @@ const J40Map = ({location}: IJ40Interface) => { {/* This will allow to select between the census tract layer and the tribal lands layer */} - + {/** * The ReactMapGL component's props are grouped by the API's documentation. The component also has @@ -356,9 +367,15 @@ const J40Map = ({location}: IJ40Interface) => { {/* Load either the Tribal layer or census layer */} { - censusSelected ? - : - + censusSelected ? + : + } {/* This will add the navigation controls of the zoom in and zoom out buttons */} @@ -392,7 +409,11 @@ const J40Map = ({location}: IJ40Interface) => { onClose={setDetailViewData} captureScroll={true} > - + )} {'fs' in flags ? :'' } @@ -406,6 +427,8 @@ const J40Map = ({location}: IJ40Interface) => { featureProperties={detailViewData?.properties} selectedFeatureId={selectedFeature?.id} hash={zoomLatLngHash} + isCensusLayerSelected={censusSelected} + layerToggled={layerToggled} /> diff --git a/client/src/components/LayerSelector/LayerSelector.test.tsx b/client/src/components/LayerSelector/LayerSelector.test.tsx index 7b8dd240..76667a90 100644 --- a/client/src/components/LayerSelector/LayerSelector.test.tsx +++ b/client/src/components/LayerSelector/LayerSelector.test.tsx @@ -7,7 +7,7 @@ describe('rendering of the LayerSelector', () => { it('checks if component renders census tracts selected', () => { const {asFragment} = render( - {}}/> + {}} setLayerToggled={() =>{}}/> , ); expect(asFragment()).toMatchSnapshot(); @@ -16,7 +16,7 @@ describe('rendering of the LayerSelector', () => { it('checks if component renders tribal selected', () => { const {asFragment} = render( - {}}/> + {}} setLayerToggled={()=> {}}/> , ); expect(asFragment()).toMatchSnapshot(); diff --git a/client/src/components/LayerSelector/LayerSelector.tsx b/client/src/components/LayerSelector/LayerSelector.tsx index 933133c8..638fcacc 100644 --- a/client/src/components/LayerSelector/LayerSelector.tsx +++ b/client/src/components/LayerSelector/LayerSelector.tsx @@ -10,9 +10,10 @@ import * as EXPLORE_COPY from '../../data/copy/explore'; interface ILayerSelector { censusSelected: boolean, setCensusSelected: Dispatch, + setLayerToggled: Dispatch, } -const LayerSelector = ({censusSelected, setCensusSelected}:ILayerSelector) => { +const LayerSelector = ({censusSelected, setCensusSelected, setLayerToggled}:ILayerSelector) => { const intl = useIntl(); /** @@ -38,6 +39,13 @@ const LayerSelector = ({censusSelected, setCensusSelected}:ILayerSelector) => { } }, [width]); + // Anytime the censusSelected state variable changes, set the LayerToggled state + // variable + useEffect( () => { + setLayerToggled(true); + }, [censusSelected]); + + // Handles toggle of tracts and tribal layer selection const buttonClickHandler = (event) => { if (event.target.id === 'census' && !censusSelected) { diff --git a/client/src/components/MapTractLayers/MapTractLayers.tsx b/client/src/components/MapTractLayers/MapTractLayers.tsx index 60195293..a8815dac 100644 --- a/client/src/components/MapTractLayers/MapTractLayers.tsx +++ b/client/src/components/MapTractLayers/MapTractLayers.tsx @@ -1,5 +1,6 @@ import React, {useMemo} from 'react'; import {Source, Layer} from 'react-map-gl'; +import {AnyLayer} from 'mapbox-gl'; // Contexts: import {useFlags} from '../../contexts/FlagContext'; @@ -7,10 +8,9 @@ import {useFlags} from '../../contexts/FlagContext'; import * as constants from '../../data/constants'; import * as COMMON_COPY from '../../data/copy/common'; -// Todo: Update with real types if this works: interface IMapTractLayers { - selectedFeatureId: any, - selectedFeature: any + selectedFeatureId: AnyLayer, + selectedFeature: AnyLayer, } /** @@ -56,7 +56,10 @@ export const featureURLForTilesetName = (tilesetName: string): string => { } }; -const MapTractLayers = ({selectedFeatureId, selectedFeature}: IMapTractLayers) => { +const MapTractLayers = ({ + selectedFeatureId, + selectedFeature, +}: IMapTractLayers) => { const filter = useMemo(() => ['in', constants.GEOID_PROPERTY, selectedFeatureId], [selectedFeature]); return ( diff --git a/client/src/components/MapTribalLayer/MapTribalLayer.tsx b/client/src/components/MapTribalLayer/MapTribalLayer.tsx index 37baa07c..e2519b53 100644 --- a/client/src/components/MapTribalLayer/MapTribalLayer.tsx +++ b/client/src/components/MapTribalLayer/MapTribalLayer.tsx @@ -1,8 +1,14 @@ -import React from 'react'; +import React, {useMemo} from 'react'; import {Source, Layer} from 'react-map-gl'; +import {AnyLayer} from 'mapbox-gl'; import * as constants from '../../data/constants'; +interface IMapTribalLayers { + selectedFeatureId: AnyLayer, + selectedFeature: AnyLayer, +} + /** * This function will determine the URL for the tribal tiles. * @return {string} @@ -18,7 +24,12 @@ export const tribalURL = (): string => { ].join('/'); }; -const MapTribalLayer = () => { +const MapTribalLayer = ({ + selectedFeatureId, + selectedFeature, +}: IMapTribalLayers) => { + const tribalSelectionFilter = useMemo(() => ['in', constants.TRIBAL_ID, selectedFeatureId], [selectedFeature]); + return ( { maxzoom={constants.TRIBAL_MAX_ZOOM} > - {/* Low zoom layer - prioritized features only */} + {/* Tribal layer */} { source-layer={constants.SCORE_SOURCE_LAYER} type='line' paint={{ - 'line-color': constants.TRIBAL_BORDER_COLOR, + 'line-color': constants.FEATURE_BORDER_COLOR, 'line-width': constants.FEATURE_BORDER_WIDTH, 'line-opacity': constants.FEATURE_BORDER_OPACITY, }} @@ -58,8 +69,9 @@ const MapTribalLayer = () => { {/* Tribal layer - border styling around the selected feature */} { }} minzoom={constants.TRIBAL_MIN_ZOOM} /> + + {/* Alaska layer */} + {/* // Todo: Figure out why this isn't working */} + ); }; diff --git a/client/src/components/mapInfoPanel.tsx b/client/src/components/mapInfoPanel.tsx index 1a454448..c0ec0687 100644 --- a/client/src/components/mapInfoPanel.tsx +++ b/client/src/components/mapInfoPanel.tsx @@ -7,13 +7,33 @@ interface IMapInfoPanelProps { featureProperties: { [key:string]: string | number } | undefined, selectedFeatureId: string | number | undefined hash: string[], + isCensusLayerSelected: boolean, + layerToggled: boolean, // indicates if census layer or tribal layer has been toggled } -const MapInfoPanel = ({className, featureProperties, selectedFeatureId, hash}:IMapInfoPanelProps) => { +const MapInfoPanel = ({ + className, + featureProperties, + selectedFeatureId, + hash, + isCensusLayerSelected, + layerToggled, +}:IMapInfoPanelProps) => { return (
- {(featureProperties && selectedFeatureId ) ? - : + {/* The tertiary conditional statement below will control the side panel state. Currently + there are two states, namely showing the AreaDetail or SidePanelInfo. When a feature + is selected, on - for example - the census tract layer, and if the Tribal Layer is the selected + the Side Panel should revert back to the SidePanelInfo. + + A new boolean called layerToggle captures that a layer has been selected and to render + the SidePanelInfo component */} + {(featureProperties && selectedFeatureId && !layerToggled) ? + : }
diff --git a/client/src/data/constants.tsx b/client/src/data/constants.tsx index c30b4e28..572ad9c9 100644 --- a/client/src/data/constants.tsx +++ b/client/src/data/constants.tsx @@ -27,6 +27,7 @@ export type J40Properties = { [key: string]: any }; // Tribal signals export const TRIBAL_ID = 'tribalId'; +export const LAND_AREA_NAME = 'landAreaName'; // Set the threshold percentile used by most indicators in the side panel export const DEFAULT_THRESHOLD_PERCENTILE = 90; @@ -202,6 +203,7 @@ export const LOW_ZOOM_LAYER_ID = 'low-zoom-layer-id'; export const FEATURE_BORDER_LAYER_ID = 'feature-border-layer-id'; export const SELECTED_FEATURE_BORDER_LAYER_ID = 'selected-feature-border-layer-id'; export const TRIBAL_LAYER_ID = 'tribal-layer-id'; +export const SELECTED_TRIBAL_FEATURE_BORDER_LAYER_ID = 'selected-feature-tribal-border-layer-id'; // Used in layer filters: export const SCORE_PROPERTY_LOW = 'M_SCORE'; @@ -234,9 +236,9 @@ export const FEATURE_BORDER_COLOR = '#4EA5CF'; export const SELECTED_FEATURE_BORDER_COLOR = '#1A4480'; export const PRIORITIZED_FEATURE_FILL_COLOR = '#768FB3'; -export const TRIBAL_BORDER_COLOR = '#0000FF'; -export const SELECTED_TRIBAL_BORDER_COLOR = '#FF0000'; -export const TRIBAL_FILL_COLOR = '#00FF00'; +export const TRIBAL_BORDER_COLOR = '##4EA5CF'; +export const SELECTED_TRIBAL_BORDER_COLOR = '#1A4480'; +export const TRIBAL_FILL_COLOR = '#768FB3'; // Widths diff --git a/client/src/data/copy/explore.tsx b/client/src/data/copy/explore.tsx index 52439cf3..ac4c1849 100644 --- a/client/src/data/copy/explore.tsx +++ b/client/src/data/copy/explore.tsx @@ -300,6 +300,15 @@ export const SIDE_PANEL_CBG_INFO = defineMessages({ }, }); +export const SIDE_PANEL_TRIBAL_INFO = defineMessages({ + LAND_AREA_NAME: { + id: 'explore.map.page.side.panel.tribalInfo.landAreaName', + defaultMessage: 'Land Area Name:', + description: `Navigate to the explore the map page. Click on Tribal Lands, when the map is in view, + click on the map. The side panel will show the land area name of the feature selected`, + }, +}); + export const COMMUNITY = { OF_FOCUS: