adds map side panel (#406)

* initial map side panel

* componentize MapSidePanel

* remove selection from J40Map

* adds isFeatureSelected to toggle component

* filters data from server for client UI

* styling and refactor

* added TODO

* adds styling to intro and pairing feedback

* add mobile styling

* adds popup back to fs feature flag

* adds tests and aria roles

* makes mobile content same as desktop

* prettier update

* initial e2e mapSidePanel test

* adds cypress tests on desktop and mobile

* adds sass util and updates cypress tests

* cleans up tests

* reverts tsconfig file

* fixes map alignment

* renaming and using constants

* renaming sidePanel to infoPanel

* intl messaging

* adds snapshot testing and utility sass file

* PR feedback
- adds intl messages
- adds data-cy attr to cy tests
- snapshot testing for unit tests
- fixes bug where side panel extends past the map
- moves all wrapper content in MapWrapper

* logs isMobile to troubleshoot deployed PR

* adds react-device-detect for isMobile detection

* adds new instance of map for mobile

* adds instance

* adds isMobile to state

* tests the fix for mobile map view on PR

* PR review feedback
- localize MapIntroduction
- update snapshot tests
- QA feedback
- constants.isMobile points to react-device-detect
This commit is contained in:
Vim 2021-07-27 12:05:25 -07:00 committed by GitHub
commit 36f43b2d44
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1430 additions and 27185 deletions

View file

@ -1,4 +1,5 @@
/* eslint-disable no-unused-vars */
// External Libs:
import React, {MouseEvent, useRef, useState} from 'react';
import {Map, MapboxGeoJSONFeature, LngLatBoundsLike} from 'maplibre-gl';
import ReactMapGL, {
@ -11,13 +12,20 @@ import ReactMapGL, {
FlyToInterpolator,
FullscreenControl,
MapRef} from 'react-map-gl';
import {makeMapStyle} from '../data/mapStyle';
import AreaDetail from './areaDetail';
import bbox from '@turf/bbox';
import * as d3 from 'd3-ease';
import {useFlags} from '../contexts/FlagContext';
import TerritoryFocusControl from './territoryFocusControl';
import {isMobile} from 'react-device-detect';
// Contexts:
import {useFlags} from '../contexts/FlagContext';
// Components:
import TerritoryFocusControl from './territoryFocusControl';
import MapInfoPanel from './mapInfoPanel';
import AreaDetail from './areaDetail';
// Styles and constants
import {makeMapStyle} from '../data/mapStyle';
import 'maplibre-gl/dist/maplibre-gl.css';
import * as constants from '../data/constants';
import * as styles from './J40Map.module.scss';
@ -31,13 +39,14 @@ declare global {
}
interface IDetailViewInterface {
export interface IDetailViewInterface {
latitude: number
longitude: number
zoom: number
properties: constants.J40Properties,
};
const J40Map = () => {
const [viewport, setViewport] = useState<ViewportProps>({
latitude: constants.DEFAULT_CENTER[0],
@ -49,6 +58,8 @@ const J40Map = () => {
const [detailViewData, setDetailViewData] = useState<IDetailViewInterface>();
const [transitionInProgress, setTransitionInProgress] = useState<boolean>(false);
const [geolocationInProgress, setGeolocationInProgress] = useState<boolean>(false);
const [isMobileMapState, setIsMobileMapState] = useState<boolean>(false);
const mapRef = useRef<MapRef>(null);
const flags = useFlags();
@ -66,6 +77,7 @@ const J40Map = () => {
padding: 40,
},
);
// If we've selected a new feature, set 'selected' to false
if (selectedFeature && feature.id !== selectedFeature.id) {
setMapSelected(selectedFeature, false);
@ -90,6 +102,8 @@ const J40Map = () => {
if (typeof window !== 'undefined' && window.Cypress && mapRef.current) {
window.underlyingMap = mapRef.current.getMap();
}
if (isMobile) setIsMobileMapState(true);
};
@ -110,6 +124,7 @@ const J40Map = () => {
});
};
const setMapSelected = (feature:MapboxGeoJSONFeature, isSelected:boolean) : void => {
// The below can be confirmed during debug with:
// mapRef.current.getFeatureState({"id":feature.id, "source":feature.source, "sourceLayer":feature.sourceLayer})
@ -125,6 +140,7 @@ const J40Map = () => {
}
};
const onClickTerritoryFocusButton = (event: MouseEvent<HTMLButtonElement>) => {
const buttonID = event.target && (event.target as HTMLElement).id;
@ -164,16 +180,15 @@ const J40Map = () => {
};
return (
<>
<div className={styles.mapAndInfoPanelContainer}>
<ReactMapGL
{...viewport}
className={styles.mapContainer}
mapStyle={makeMapStyle(flags)}
minZoom={constants.GLOBAL_MIN_ZOOM}
maxZoom={constants.GLOBAL_MAX_ZOOM}
mapOptions={{hash: true}}
width="100%"
height="52vw"
height={isMobileMapState ? '44vh' : '100%'}
dragRotate={false}
touchRotate={false}
interactiveLayerIds={[constants.HIGH_SCORE_LAYER_NAME]}
@ -183,8 +198,9 @@ const J40Map = () => {
onTransitionStart={onTransitionStart}
onTransitionEnd={onTransitionEnd}
ref={mapRef}
data-cy={'reactMapGL'}
>
{(detailViewData && !transitionInProgress) && (
{('fs' in flags && detailViewData && !transitionInProgress) && (
<Popup
className={styles.j40Popup}
tipSize={5}
@ -198,7 +214,6 @@ const J40Map = () => {
<AreaDetail properties={detailViewData.properties} />
</Popup>
)}
<NavigationControl
showCompass={false}
className={styles.navigationControl}
@ -214,7 +229,12 @@ const J40Map = () => {
<TerritoryFocusControl onClickTerritoryFocusButton={onClickTerritoryFocusButton}/>
{'fs' in flags ? <FullscreenControl className={styles.fullscreenControl}/> :'' }
</ReactMapGL>
</>
<MapInfoPanel
className={styles.mapInfoPanel}
featureProperties={detailViewData?.properties}
selectedFeatureId={selectedFeature?.id}
/>
</div>
);
};