mirror of
https://github.com/DOI-DO/j40-cejst-2.git
synced 2025-07-30 09:11:18 -07:00
Fix territory shortcuts when census tract is selected (#1082)
* Refactor map click event architecture - combine territory map clickHandlers - centers AS on the map * Center US on the map - make the east and west coast both viewable - make clicking on the 48, show the same zoom/lat/long as initial map - centers Hawaii on map * Update link to map performance * Explicitly show links as the links return a 403 * Removes link and spells link out
This commit is contained in:
parent
beb0eea5cc
commit
356e16950f
8 changed files with 223 additions and 167 deletions
|
@ -9,53 +9,53 @@ describe('Does the map zoom and adjust to lat/long correctly?', () => {
|
|||
cy.get('.mapboxgl-ctrl-icon.mapboxgl-ctrl-zoom-in').click({force: true});
|
||||
cy.url().should('include', '#4');
|
||||
});
|
||||
// it('should show the correct lat/lng coordinates in the URL',
|
||||
// {
|
||||
// retries: {
|
||||
// runMode: 3,
|
||||
// openMode: 3,
|
||||
// },
|
||||
// defaultCommandTimeout: 4000,
|
||||
// execTimeout: 10000,
|
||||
// taskTimeout: 10000,
|
||||
// pageLoadTimeout: 10000,
|
||||
// requestTimeout: 5000,
|
||||
// responseTimeout: 10000,
|
||||
// },
|
||||
// () => {
|
||||
// cy.getMap().then((map) => {
|
||||
// cy.panTo(map, [-77.9, 35.04]);
|
||||
// cy.url().should('include', '#4/35.04/-77.9');
|
||||
// });
|
||||
// });
|
||||
it('should show the correct lat/lng coordinates in the URL',
|
||||
{
|
||||
retries: {
|
||||
runMode: 3,
|
||||
openMode: 3,
|
||||
},
|
||||
defaultCommandTimeout: 4000,
|
||||
execTimeout: 10000,
|
||||
taskTimeout: 10000,
|
||||
pageLoadTimeout: 10000,
|
||||
requestTimeout: 5000,
|
||||
responseTimeout: 10000,
|
||||
},
|
||||
() => {
|
||||
cy.getMap().then((map) => {
|
||||
cy.panTo(map, [-77.9, 35.04]);
|
||||
cy.url().should('include', '#4/35.04/-77.9');
|
||||
});
|
||||
});
|
||||
|
||||
// This test hangs intermittently (30% of the time) need to investigate why
|
||||
// it('allows user to specify alternative starting URL',
|
||||
// {
|
||||
// retries: {
|
||||
// runMode: 3,
|
||||
// openMode: 3,
|
||||
// },
|
||||
// defaultCommandTimeout: 4000,
|
||||
// execTimeout: 10000,
|
||||
// taskTimeout: 10000,
|
||||
// pageLoadTimeout: 10000,
|
||||
// requestTimeout: 5000,
|
||||
// responseTimeout: 10000,
|
||||
// },
|
||||
// () => {
|
||||
// const [expectedZoom, expectedLat, expectedLng] = [12.05, 41.40965, -75.65978];
|
||||
// const expectedURL = `http://localhost:8000/en/cejst/#${expectedZoom}/${expectedLat}/${expectedLng}`;
|
||||
// cy.visit(expectedURL);
|
||||
// cy.getMap().then((map) => {
|
||||
// cy.waitForMapIdle(map);
|
||||
// cy.url().should('equal', expectedURL);
|
||||
// const actualZoom = map.getZoom();
|
||||
// const actualCenter = map.getCenter();
|
||||
// expect(actualCenter.lat).to.eq(expectedLat);
|
||||
// expect(actualCenter.lng).to.eq(expectedLng);
|
||||
// expect(actualZoom).to.eq(expectedZoom);
|
||||
// });
|
||||
// });
|
||||
it('allows user to specify alternative starting URL',
|
||||
{
|
||||
retries: {
|
||||
runMode: 3,
|
||||
openMode: 3,
|
||||
},
|
||||
defaultCommandTimeout: 4000,
|
||||
execTimeout: 10000,
|
||||
taskTimeout: 10000,
|
||||
pageLoadTimeout: 10000,
|
||||
requestTimeout: 5000,
|
||||
responseTimeout: 10000,
|
||||
},
|
||||
() => {
|
||||
const [expectedZoom, expectedLat, expectedLng] = [12.05, 41.40965, -75.65978];
|
||||
const expectedURL = `http://localhost:8000/en/cejst/#${expectedZoom}/${expectedLat}/${expectedLng}`;
|
||||
cy.visit(expectedURL);
|
||||
cy.getMap().then((map) => {
|
||||
cy.waitForMapIdle(map);
|
||||
cy.url().should('equal', expectedURL);
|
||||
const actualZoom = map.getZoom();
|
||||
const actualCenter = map.getCenter();
|
||||
expect(actualCenter.lat).to.eq(expectedLat);
|
||||
expect(actualCenter.lng).to.eq(expectedLng);
|
||||
expect(actualZoom).to.eq(expectedZoom);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,32 +1,48 @@
|
|||
// / <reference types="Cypress" />
|
||||
|
||||
describe('Will it zoom into territories correctly?', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('macbook-13');
|
||||
cy.visit('http://localhost:8000/en/cejst');
|
||||
});
|
||||
|
||||
// The below values all assume a 13-inch MB as set in viewport above.
|
||||
// Values will be different for different screens
|
||||
|
||||
// Removing z as each tests has variance on it's value
|
||||
const tests = {
|
||||
'Lower 48': '/38.07/-95.87',
|
||||
|
||||
// Todo: Understand what causes these two to hang intermittently ticket #579
|
||||
// 'Puerto Rico': '/18.2/-66.583',
|
||||
// 'Alaska': '/63.28/-162.39',
|
||||
// 'Hawaii': '5.35/20.574/-161.438',
|
||||
};
|
||||
|
||||
for (const [territory, xy] of Object.entries(tests)) {
|
||||
it(`Can focus on ${territory} `, () => {
|
||||
cy.getMap().then((map) => {
|
||||
cy.get(`[aria-label="Focus on ${territory}"]`).click();
|
||||
cy.waitForMapIdle(map);
|
||||
cy.log(cy.url());
|
||||
cy.url().should('include', xy);
|
||||
describe('Will it zoom into territories correctly?',
|
||||
{
|
||||
retries: {
|
||||
runMode: 3,
|
||||
openMode: 3,
|
||||
},
|
||||
defaultCommandTimeout: 4000,
|
||||
execTimeout: 10000,
|
||||
taskTimeout: 10000,
|
||||
pageLoadTimeout: 10000,
|
||||
requestTimeout: 5000,
|
||||
responseTimeout: 10000,
|
||||
},
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
cy.viewport('macbook-13');
|
||||
cy.visit('http://localhost:8000/en/cejst');
|
||||
});
|
||||
|
||||
// The below values all assume a 13-inch MB as set in viewport above.
|
||||
// Values will be different for different screens
|
||||
|
||||
// Removing z as each tests has variance on it's value
|
||||
const tests = {
|
||||
'Lower 48': '3/33.47/-97.5',
|
||||
|
||||
// Todo: Understand what causes these two to hang intermittently ticket #579
|
||||
// 'Puerto Rico': '7.71/18.2/-66.583',
|
||||
// 'Alaska': '3/63.28/-162.39',
|
||||
// 'American Samoa': '6.55/-13.804/-171.117',
|
||||
// 'Commonwealth of Northern Mariana Islands': '5.98/16.901/145.472',
|
||||
// 'Hawaii': '5.73/20.657/-157.697',
|
||||
};
|
||||
for (let i=0; i<1; i++) {
|
||||
for (const [territory, zxy] of Object.entries(tests)) {
|
||||
it(`Can focus on ${territory} `, () => {
|
||||
cy.getMap().then((map) => {
|
||||
cy.get(`[aria-label="Focus on ${territory}"]`).click();
|
||||
cy.waitForMapIdle(map);
|
||||
cy.log(cy.url());
|
||||
cy.url().should('include', zxy);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
@ -28,6 +28,7 @@ export interface indicatorInfo {
|
|||
const AreaDetail = ({properties}:IAreaDetailProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
// console.log the properties of the census that is selected:
|
||||
console.log("Area Detail properies: ", properties);
|
||||
|
||||
const score = properties[constants.SCORE_PROPERTY_HIGH] ? properties[constants.SCORE_PROPERTY_HIGH] as number : 0;
|
||||
|
@ -38,10 +39,6 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
|
|||
|
||||
const isCommunityFocus = score >= constants.SCORE_BOUNDARY_PRIORITIZED;
|
||||
|
||||
// const sidePanelFeedbackHref = `
|
||||
// mailto:screeningtool.feedback@usds.gov?subject=Feedback on Census Tract: ${blockGroup}
|
||||
// `;
|
||||
|
||||
// Define each indicator in the side panel with constants from copy file (for intl)
|
||||
// Indicators are grouped by category
|
||||
const expAgLoss:indicatorInfo = {
|
||||
|
@ -382,8 +379,9 @@ const AreaDetail = ({properties}:IAreaDetailProps) => {
|
|||
totalCount: constants.TOTAL_NUMBER_OF_INDICATORS,
|
||||
}}/>
|
||||
</div>
|
||||
{/* eslint-disable-next-line max-len */}
|
||||
{/* <a className={styles.feedbackLink} href={sidePanelFeedbackHref}>{EXPLORE_COPY.COMMUNITY.SEND_FEEDBACK}</a> */}
|
||||
{/* <a className={styles.feedbackLink} href={sidePanelFeedbackHref}>
|
||||
{EXPLORE_COPY.COMMUNITY.SEND_FEEDBACK}
|
||||
</a> */}
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable valid-jsdoc */
|
||||
/* eslint-disable no-unused-vars */
|
||||
// External Libs:
|
||||
import React, {useRef, useState, useMemo} from 'react';
|
||||
|
@ -55,12 +56,23 @@ export interface IDetailViewInterface {
|
|||
|
||||
|
||||
const J40Map = ({location}: IJ40Interface) => {
|
||||
// Hash portion of URL is of the form #zoom/lat/lng
|
||||
/**
|
||||
* Initializes the zoom, and the map's center point (lat, lng) via the URL hash #{z}/{lat}/{long}
|
||||
* where:
|
||||
* z = zoom
|
||||
* lat = map center's latitude
|
||||
* long = map center's longitude
|
||||
*/
|
||||
const [zoom, lat, lng] = location.hash.slice(1).split('/');
|
||||
|
||||
/**
|
||||
* If the URL has no #{z}/{lat}/{long} specified in the hash, then set the map's intial viewport state
|
||||
* to use constants. This is so that we can load URLs with certain zoom/lat/long specified:
|
||||
*/
|
||||
const [viewport, setViewport] = useState<ViewportProps>({
|
||||
latitude: lat && parseFloat(lat) || constants.DEFAULT_CENTER[0],
|
||||
longitude: lng && parseFloat(lng) || constants.DEFAULT_CENTER[1],
|
||||
zoom: zoom && parseFloat(zoom) || constants.GLOBAL_MIN_ZOOM,
|
||||
latitude: lat && parseFloat(lat) ? parseFloat(lat) : constants.DEFAULT_CENTER[0],
|
||||
longitude: lng && parseFloat(lng) ? parseFloat(lng) : constants.DEFAULT_CENTER[1],
|
||||
zoom: zoom && parseFloat(zoom) ? parseFloat(zoom) : constants.GLOBAL_MIN_ZOOM,
|
||||
});
|
||||
|
||||
const [selectedFeature, setSelectedFeature] = useState<MapboxGeoJSONFeature>();
|
||||
|
@ -76,37 +88,102 @@ const J40Map = ({location}: IJ40Interface) => {
|
|||
const selectedFeatureId = (selectedFeature && selectedFeature.id) || '';
|
||||
const filter = useMemo(() => ['in', constants.GEOID_PROPERTY, selectedFeatureId], [selectedFeature]);
|
||||
|
||||
const onClick = (event: MapEvent) => {
|
||||
const feature = event.features && event.features[0];
|
||||
if (feature) {
|
||||
const [minLng, minLat, maxLng, maxLat] = bbox(feature);
|
||||
const newViewPort = new WebMercatorViewport({height: viewport.height!, width: viewport.width!});
|
||||
const {longitude, latitude, zoom} = newViewPort.fitBounds(
|
||||
[
|
||||
[minLng, minLat],
|
||||
[maxLng, maxLat],
|
||||
],
|
||||
{
|
||||
padding: 40,
|
||||
},
|
||||
);
|
||||
if (feature.id !== selectedFeatureId) {
|
||||
setSelectedFeature(feature);
|
||||
console.log(feature.properties);
|
||||
} else {
|
||||
setSelectedFeature(undefined);
|
||||
|
||||
/**
|
||||
* This function will return the bounding box of the current map. Comment in when needed.
|
||||
* {
|
||||
* _ne: {lng:number, lat:number}
|
||||
* _sw: {lng:number, lat:number}
|
||||
* }
|
||||
* @returns {LngLatBounds}
|
||||
*/
|
||||
const getCurrentMapBoundingBox = () => {
|
||||
return mapRef.current ? console.log('mapRef getBounds(): ', mapRef.current.getMap().getBounds()) : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This onClick event handler will listen and handle clicks on the map. It will listen for clicks on the
|
||||
* territory controls and it will listen to clicks on the map.
|
||||
*
|
||||
* It will NOT listen to clicks into the search field or the zoom controls. These clickHandlers are
|
||||
* captured in their own respective components.
|
||||
*/
|
||||
const onClick = (event: MapEvent | React.MouseEvent<HTMLButtonElement>) => {
|
||||
// Stop all propagation / bubbling / capturing
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
getCurrentMapBoundingBox();
|
||||
|
||||
// Check if the click is for territories. Given the territories component's design, it can be
|
||||
// guaranteed that each territory control will have an id. We use this ID to determine
|
||||
// if the click is coming from a territory control
|
||||
if (event.target && (event.target as HTMLElement).id) {
|
||||
const buttonID = event.target && (event.target as HTMLElement).id;
|
||||
|
||||
switch (buttonID) {
|
||||
case '48':
|
||||
goToPlace(constants.LOWER_48_BOUNDS);
|
||||
break;
|
||||
case 'AK':
|
||||
goToPlace(constants.ALASKA_BOUNDS);
|
||||
break;
|
||||
case 'HI':
|
||||
goToPlace(constants.HAWAII_BOUNDS);
|
||||
break;
|
||||
case 'PR':
|
||||
goToPlace(constants.PUERTO_RICO_BOUNDS);
|
||||
break;
|
||||
case 'GU':
|
||||
goToPlace(constants.GUAM_BOUNDS);
|
||||
break;
|
||||
case 'AS':
|
||||
goToPlace(constants.AMERICAN_SAMOA_BOUNDS);
|
||||
break;
|
||||
case 'MP':
|
||||
goToPlace(constants.MARIANA_ISLAND_BOUNDS);
|
||||
break;
|
||||
case 'VI':
|
||||
goToPlace(constants.US_VIRGIN_ISLANDS_BOUNDS);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// This else clause will fire when the ID is null or empty. This is the case where the map is clicked
|
||||
const feature = event.features && event.features[0];
|
||||
console.log(feature);
|
||||
if (feature) {
|
||||
const [minLng, minLat, maxLng, maxLat] = bbox(feature);
|
||||
const newViewPort = new WebMercatorViewport({height: viewport.height!, width: viewport.width!});
|
||||
const {longitude, latitude, zoom} = newViewPort.fitBounds(
|
||||
[
|
||||
[minLng, minLat],
|
||||
[maxLng, maxLat],
|
||||
],
|
||||
{
|
||||
padding: 40,
|
||||
},
|
||||
);
|
||||
if (feature.id !== selectedFeatureId) {
|
||||
setSelectedFeature(feature);
|
||||
} else {
|
||||
setSelectedFeature(undefined);
|
||||
}
|
||||
const popupInfo = {
|
||||
longitude: longitude,
|
||||
latitude: latitude,
|
||||
zoom: zoom,
|
||||
properties: feature.properties,
|
||||
};
|
||||
goToPlace([
|
||||
[minLng, minLat],
|
||||
[maxLng, maxLat],
|
||||
]);
|
||||
setDetailViewData(popupInfo);
|
||||
}
|
||||
const popupInfo = {
|
||||
longitude: longitude,
|
||||
latitude: latitude,
|
||||
zoom: zoom,
|
||||
properties: feature.properties,
|
||||
};
|
||||
goToPlace([
|
||||
[minLng, minLat],
|
||||
[maxLng, maxLat],
|
||||
]);
|
||||
setDetailViewData(popupInfo);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -251,7 +328,7 @@ const J40Map = ({location}: IJ40Interface) => {
|
|||
onClick={onClickGeolocate}
|
||||
/> : ''}
|
||||
{geolocationInProgress ? <div>Geolocation in progress...</div> : ''}
|
||||
<TerritoryFocusControl goToPlace={goToPlace}/>
|
||||
<TerritoryFocusControl onClick={onClick}/>
|
||||
{'fs' in flags ? <FullscreenControl className={styles.fullscreenControl}/> :'' }
|
||||
|
||||
</ReactMapGL>
|
||||
|
|
|
@ -1,54 +1,18 @@
|
|||
import {useIntl} from 'gatsby-plugin-intl';
|
||||
import React from 'react';
|
||||
import {LngLatBoundsLike} from 'mapbox-gl';
|
||||
import {MapEvent} from 'react-map-gl';
|
||||
|
||||
import * as styles from './territoryFocusControl.module.scss';
|
||||
import * as EXPLORE_COPY from '../data/copy/explore';
|
||||
import * as constants from '../data/constants';
|
||||
|
||||
interface ITerritoryFocusControl {
|
||||
goToPlace(bounds: LngLatBoundsLike): void;
|
||||
onClick(event: MapEvent | React.MouseEvent<HTMLButtonElement>):void;
|
||||
}
|
||||
|
||||
|
||||
const TerritoryFocusControl = ({goToPlace}: ITerritoryFocusControl) => {
|
||||
const TerritoryFocusControl = ({onClick}: ITerritoryFocusControl) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const onClickTerritoryFocusButton = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.stopPropagation();
|
||||
const buttonID = event.target && (event.target as HTMLElement).id;
|
||||
|
||||
switch (buttonID) {
|
||||
case '48':
|
||||
goToPlace(constants.LOWER_48_BOUNDS);
|
||||
break;
|
||||
case 'AK':
|
||||
goToPlace(constants.ALASKA_BOUNDS);
|
||||
break;
|
||||
case 'HI':
|
||||
goToPlace(constants.HAWAII_BOUNDS);
|
||||
break;
|
||||
case 'PR':
|
||||
goToPlace(constants.PUERTO_RICO_BOUNDS);
|
||||
break;
|
||||
case 'GU':
|
||||
goToPlace(constants.GUAM_BOUNDS);
|
||||
break;
|
||||
case 'AS':
|
||||
goToPlace(constants.AMERICAN_SAMOA_BOUNDS);
|
||||
break;
|
||||
case 'MP':
|
||||
goToPlace(constants.MARIANA_ISLAND_BOUNDS);
|
||||
break;
|
||||
case 'VI':
|
||||
goToPlace(constants.US_VIRGIN_ISLANDS_BOUNDS);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const territories = [
|
||||
{
|
||||
short: intl.formatMessage(EXPLORE_COPY.MAP.LOWER48_SHORT),
|
||||
|
@ -102,7 +66,8 @@ const TerritoryFocusControl = ({goToPlace}: ITerritoryFocusControl) => {
|
|||
<button
|
||||
id={territory.short}
|
||||
key={territory.short}
|
||||
onClick={(e) => onClickTerritoryFocusButton(e)}
|
||||
// onClickCapture={(e) => onClickTerritoryFocusButton(e)}
|
||||
onClickCapture={(e) => onClick(e)}
|
||||
className={'mapboxgl-ctrl-icon ' + territoriesIconClassName[index]}
|
||||
aria-label={intl.formatMessage(
|
||||
{
|
||||
|
|
|
@ -128,15 +128,15 @@ export const GLOBAL_MAX_ZOOM_HIGHLIGHT = 22;
|
|||
export const GLOBAL_MIN_ZOOM_HIGH = 7;
|
||||
export const GLOBAL_MAX_ZOOM_HIGH = 11;
|
||||
|
||||
// Bounds
|
||||
// Bounds - these bounds can be obtained by using the getCurrentMapBoundingBox() function in the map
|
||||
export const GLOBAL_MAX_BOUNDS: LngLatBoundsLike = [
|
||||
[-180.118306, 5.499550],
|
||||
[-65.0, 83.162102],
|
||||
];
|
||||
|
||||
export const LOWER_48_BOUNDS: LngLatBoundsLike = [
|
||||
[-124.7844079, 24.7433195],
|
||||
[-66.9513812, 49.3457868],
|
||||
[-134.943542, 1.301806],
|
||||
[-60.060729, 57.050462],
|
||||
];
|
||||
|
||||
export const ALASKA_BOUNDS: LngLatBoundsLike = [
|
||||
|
@ -145,8 +145,8 @@ export const ALASKA_BOUNDS: LngLatBoundsLike = [
|
|||
];
|
||||
|
||||
export const HAWAII_BOUNDS: LngLatBoundsLike = [
|
||||
[-168.118306, 18.748115],
|
||||
[-154.757881, 22.378413],
|
||||
[-161.174534, 17.652170],
|
||||
[-154.218940, 23.603623],
|
||||
];
|
||||
|
||||
export const PUERTO_RICO_BOUNDS: LngLatBoundsLike = [
|
||||
|
@ -165,8 +165,8 @@ export const MARIANA_ISLAND_BOUNDS: LngLatBoundsLike = [
|
|||
];
|
||||
|
||||
export const AMERICAN_SAMOA_BOUNDS: LngLatBoundsLike = [
|
||||
[-171.089874, -14.548699],
|
||||
[-168.1433, -11.046934],
|
||||
[-172.589874, -15.548699],
|
||||
[-169.6433, -12.046934],
|
||||
];
|
||||
|
||||
export const US_VIRGIN_ISLANDS_BOUNDS: LngLatBoundsLike = [
|
||||
|
@ -174,7 +174,7 @@ export const US_VIRGIN_ISLANDS_BOUNDS: LngLatBoundsLike = [
|
|||
[-64.2704123, 18.7495796],
|
||||
];
|
||||
|
||||
export const DEFAULT_CENTER = [32.4687126, -86.502136];
|
||||
export const DEFAULT_CENTER = [33.4687126, -97.502136];
|
||||
|
||||
// Opacity
|
||||
export const DEFAULT_LAYER_OPACITY = 0.6;
|
||||
|
|
|
@ -128,7 +128,7 @@ export const MAP = defineMessages({
|
|||
},
|
||||
AS_LONG: {
|
||||
id: 'map.territoryFocus.american.samoa.long',
|
||||
defaultMessage: 'American Somoa',
|
||||
defaultMessage: 'American Samoa',
|
||||
description: 'The full name indicating the bounds of American Somoa',
|
||||
},
|
||||
MP_SHORT: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue