Skip to content

Commit

Permalink
WAITP-1223: Tupaia web map legend (#4724)
Browse files Browse the repository at this point in the history
* setup map legend

* update legend props

* mobile map legend

* Update MobileMapLegend.tsx

* refactor map legend

* fix console warnings
  • Loading branch information
tcaiger committed Jul 12, 2023
1 parent 45c604a commit 7f52f0a
Show file tree
Hide file tree
Showing 15 changed files with 154 additions and 88 deletions.
2 changes: 1 addition & 1 deletion packages/tupaia-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"lint": "yarn package:lint",
"lint:fix": "yarn lint --fix",
"preview": "vite preview",
"start-dev": "REACT_APP_DEPLOYMENT_NAME='dev' && vite",
"start-dev": "vite",
"start-fullstack": "npm-run-all -c -l -p start-servers start-frontend",
"start-central-server": "yarn workspace @tupaia/central-server start-dev",
"start-entity-server": "yarn workspace @tupaia/entity-server start-dev",
Expand Down
13 changes: 9 additions & 4 deletions packages/tupaia-web/src/features/Map/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { MapOverlaySelector } from './MapOverlaySelector';
import { useEntity, useMapOverlays } from '../../api/queries';
import { PolygonLayer } from './PolygonLayer';
import { MarkerLayer } from './MarkerLayer';
import { useDefaultMapOverlay } from './useDefaultMapOverlay';
import { useHiddenMapValues, useMapOverlayReport, useDefaultMapOverlay } from './utils';

const MapContainer = styled.div`
height: 100%;
Expand Down Expand Up @@ -91,13 +91,18 @@ const MapControlColumn = styled.div`

export const Map = () => {
const { projectCode, entityCode } = useParams();
const [activeTileSet, setActiveTileSet] = useState(TILE_SETS[0]);
const { data: entity } = useEntity(entityCode);

// set the map default overlay if there isn't one selected
const { mapOverlaysByCode } = useMapOverlays(projectCode, entityCode);
useDefaultMapOverlay(projectCode!, mapOverlaysByCode);

// Setup legend hidden values
const { data: measureData } = useMapOverlayReport();
const { hiddenValues, setValueHidden } = useHiddenMapValues(measureData?.serieses);

// Setup Tile Picker
const [activeTileSet, setActiveTileSet] = useState(TILE_SETS[0]);
const onTileSetChange = (tileSetKey: string) => {
setActiveTileSet(TILE_SETS.find(({ key }) => key === tileSetKey) as typeof TILE_SETS[0]);
};
Expand All @@ -107,15 +112,15 @@ export const Map = () => {
<StyledMap bounds={entity?.bounds as LeafletMapProps['bounds']} shouldSnapToPosition>
<TileLayer tileSetUrl={activeTileSet.url} showAttribution={false} />
<PolygonLayer />
<MarkerLayer />
<MarkerLayer hiddenValues={hiddenValues} />
<ZoomControl position="bottomright" />
<MapWatermark />
</StyledMap>
{/* Map Controls need to be outside the map so that the mouse events on controls don't inter wit the map */}
<MapControlWrapper>
<MapControlColumn>
<MapOverlaySelector />
<MapLegend />
<MapLegend hiddenValues={hiddenValues} setValueHidden={setValueHidden} />
</MapControlColumn>
<TilePickerWrapper>
<TilePicker
Expand Down

This file was deleted.

56 changes: 52 additions & 4 deletions packages/tupaia-web/src/features/Map/MapLegend/MapLegend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,62 @@
*/

import React from 'react';
import { Legend, LegendProps } from '@tupaia/ui-map-components';
import { MobileMapLegend } from './MobileMapLegend';
import { DesktopMapLegend } from './DesktopMapLegend';
import { useSearchParams } from 'react-router-dom';
import { MOBILE_BREAKPOINT, URL_SEARCH_PARAMS } from '../../../constants';
import { useMapOverlayReport } from '../utils';
import styled from 'styled-components';

const DesktopWrapper = styled.div`
pointer-events: auto;
@media screen and (max-width: ${MOBILE_BREAKPOINT}) {
display: none;
}
`;

const SeriesDivider = styled.div`
height: 0;
border-width: 1px;
border-style: solid;
border-color: ${({ theme }) => (theme.palette.type === 'light' ? '#00000022' : '#ffffff22')};
width: calc(100% - 2rem);
margin: 0.3rem auto 0.2rem;
@media screen and (min-width: ${MOBILE_BREAKPOINT}) {
display: none;
}
`;

export const MapLegend = ({ hiddenValues, setValueHidden }: LegendProps) => {
const [urlSearchParams] = useSearchParams();
const selectedOverlay = urlSearchParams.get(URL_SEARCH_PARAMS.MAP_OVERLAY);
const { data: overlayReportData } = useMapOverlayReport();

if (!selectedOverlay) {
return null;
}

const LegendComponent = () => (
<Legend
measureInfo={{ [selectedOverlay]: overlayReportData }}
setValueHidden={setValueHidden}
hiddenValues={hiddenValues}
currentMapOverlayCodes={[selectedOverlay]}
displayedMapOverlayCodes={[selectedOverlay]}
SeriesDivider={SeriesDivider}
/>
);

export const MapLegend = () => {
return (
<>
<MobileMapLegend />
<DesktopMapLegend />
<MobileMapLegend>
<LegendComponent />
</MobileMapLegend>
<DesktopWrapper>
<LegendComponent />
</DesktopWrapper>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
import React, { useState } from 'react';
import React, { ReactNode, useState } from 'react';
import { Close, ExpandLess } from '@material-ui/icons';
import { Button, IconButton } from '@tupaia/ui-components';
import styled from 'styled-components';
import { MOBILE_BREAKPOINT } from '../../../constants';

const Wrapper = styled.div`
position: absolute;
bottom: 1rem;
pointer-events: auto;
bottom: 0.8rem;
right: 1rem;
padding: 0;
@media screen and (min-width: ${MOBILE_BREAKPOINT}) {
display: none;
}
Expand All @@ -30,10 +32,11 @@ const ExpandIcon = styled(ExpandLess)`
`;

const ExpandedLegend = styled.div`
display: block;
background-color: ${({ theme }) => theme.mobile.background};
height: 20rem;
width: 12rem;
border-radius: 0.5rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
`;

const CloseButton = styled(IconButton).attrs({
Expand All @@ -44,7 +47,7 @@ const CloseButton = styled(IconButton).attrs({
right: 0;
`;

export const MobileMapLegend = () => {
export const MobileMapLegend = ({ children }: { children: ReactNode }) => {
const [expanded, setExpanded] = useState(false);
const toggleExpanded = () => {
setExpanded(!expanded);
Expand All @@ -56,6 +59,7 @@ export const MobileMapLegend = () => {
<CloseButton onClick={toggleExpanded} aria-label="Close legend">
<Close />
</CloseButton>
{children}
</ExpandedLegend>
) : (
<MapLegendExpandButton onClick={toggleExpanded}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import { useParams } from 'react-router';
import { Accordion, Typography, AccordionSummary, AccordionDetails } from '@material-ui/core';
import { ExpandMore, Layers } from '@material-ui/icons';
import { periodToMoment } from '@tupaia/utils';
import { MOBILE_BREAKPOINT, URL_SEARCH_PARAMS } from '../../../constants';
import { MOBILE_BREAKPOINT } from '../../../constants';
import { Entity } from '../../../types';
import { useMapOverlayReport, useMapOverlays } from '../../../api/queries';
import { useMapOverlays } from '../../../api/queries';
import { useMapOverlayReport } from '../utils';
import { MapOverlayList } from './MapOverlayList';
import { MapOverlaySelectorTitle } from './MapOverlaySelectorTitleSection';
import { useDateRanges } from '../../../utils';

const MaxHeightContainer = styled.div`
max-height: 100%;
Expand Down Expand Up @@ -134,15 +134,8 @@ export const DesktopMapOverlaySelector = ({
toggleOverlayLibrary,
}: DesktopMapOverlaySelectorProps) => {
const { projectCode, entityCode } = useParams();
const { hasMapOverlays, selectedOverlay } = useMapOverlays(projectCode, entityCode);
const { startDate, endDate } = useDateRanges(
URL_SEARCH_PARAMS.MAP_OVERLAY_PERIOD,
selectedOverlay,
);
const { data: mapOverlayData } = useMapOverlayReport(projectCode, entityCode, selectedOverlay, {
startDate,
endDate,
});
const { hasMapOverlays } = useMapOverlays(projectCode, entityCode);
const { data: mapOverlayData } = useMapOverlayReport();

return (
<Wrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import React from 'react';
import { useParams } from 'react-router-dom';
import { Skeleton } from '@material-ui/lab';
import { useMapOverlayReport, useMapOverlays } from '../../../api/queries';
import { useMapOverlays } from '../../../api/queries';
import { useMapOverlayReport } from '../utils';
import { DateRangePicker } from '../../../components';
import { useDateRanges } from '../../../utils';
import { URL_SEARCH_PARAMS } from '../../../constants';
Expand All @@ -23,15 +24,7 @@ export const MapOverlayDatePicker = () => {
periodGranularity,
} = useDateRanges(URL_SEARCH_PARAMS.MAP_OVERLAY_PERIOD, selectedOverlay);

const { isLoading: isLoadingMapOverlayData } = useMapOverlayReport(
projectCode,
entityCode,
selectedOverlay,
{
startDate,
endDate,
},
);
const { isLoading: isLoadingMapOverlayData } = useMapOverlayReport();

if (!showDatePicker) return null;
return (
Expand Down
11 changes: 5 additions & 6 deletions packages/tupaia-web/src/features/Map/MarkerLayer/MarkerLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import React from 'react';
import { useParams } from 'react-router';
import camelCase from 'camelcase';
import { useLocation, useNavigate } from 'react-router-dom';
import { MarkerLayer as UIMarkerLayer, MeasureData } from '@tupaia/ui-map-components';
import { LegendProps, MarkerLayer as UIMarkerLayer, MeasureData } from '@tupaia/ui-map-components';
import {
useEntitiesWithLocation,
useEntity,
useMapOverlayReport,
useMapOverlays,
useProject,
} from '../../../api/queries';
import { useMapOverlayReport } from '../utils';
import { EntityCode } from '../../../types';
import { processMeasureData } from './processMeasureData';

Expand Down Expand Up @@ -61,12 +61,12 @@ const useEntitiesByMeasureLevel = (measureLevel?: string) => {
);
};

export const MarkerLayer = () => {
export const MarkerLayer = ({ hiddenValues }: { hiddenValues: LegendProps['hiddenValues'] }) => {
const navigateToDashboard = useNavigateToDashboard();
const { projectCode, entityCode } = useParams();
const { selectedOverlay } = useMapOverlays(projectCode, entityCode);
const { data: entitiesData } = useEntitiesByMeasureLevel(selectedOverlay?.measureLevel);
const { data: mapOverlayData } = useMapOverlayReport(projectCode, entityCode, selectedOverlay);
const { data: mapOverlayData } = useMapOverlayReport();
const { data: entity } = useEntity(entityCode);

if (!entitiesData || !mapOverlayData || !entity) {
Expand All @@ -84,8 +84,7 @@ export const MarkerLayer = () => {
entitiesData,
measureData: mapOverlayData.measureData,
serieses: mapOverlayData.serieses,
// Implement this when we add the legend
hiddenValues: {},
hiddenValues,
});

return (
Expand Down
8 changes: 8 additions & 0 deletions packages/tupaia-web/src/features/Map/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

export { useDefaultMapOverlay } from './useDefaultMapOverlay';
export { useHiddenMapValues } from './useHiddenMapValues';
export { useMapOverlayReport } from './useMapOverlayReport';
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useProject } from '../../api/queries';
import { useProject } from '../../../api/queries';
import {
DEFAULT_MAP_OVERLAY_ID,
DEFAULT_PERIOD_PARAM_STRING,
URL_SEARCH_PARAMS,
} from '../../constants';
import { MapOverlayGroup, ProjectCode, EntityCode } from '../../types';
} from '../../../constants';
import { MapOverlayGroup, ProjectCode, EntityCode } from '../../../types';

// When the map overlay groups change, update the default map overlay
export const useDefaultMapOverlay = (
Expand Down
29 changes: 29 additions & 0 deletions packages/tupaia-web/src/features/Map/utils/useHiddenMapValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
import { useState } from 'react';
import { LegendProps, Series, Value } from '@tupaia/ui-map-components';

export const useHiddenMapValues = (serieses: Series[] = []) => {
const [hiddenValues, setHiddenValues] = useState({});
const [prevSerieses, setPrevSerieses] = useState(serieses);

// reset hidden values when changing overlay or entity
if (JSON.stringify(serieses) !== JSON.stringify(prevSerieses)) {
setPrevSerieses(serieses);
const hiddenByDefault = serieses.reduce((values, { hideByDefault, key }) => {
return { ...values, [key]: hideByDefault };
}, {});
setHiddenValues(hiddenByDefault);
}

const setValueHidden = (key: string, value: Value, hidden: boolean) => {
setHiddenValues((currentState: LegendProps['hiddenValues']) => ({
...currentState,
[key]: { ...currentState[key], [value!]: hidden },
}));
};

return { setValueHidden, hiddenValues };
};
25 changes: 25 additions & 0 deletions packages/tupaia-web/src/features/Map/utils/useMapOverlayReport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import { useParams } from 'react-router';
import {
useMapOverlays,
useMapOverlayReport as useMapOverlayReportQuery,
} from '../../../api/queries';
import { useDateRanges } from '../../../utils';
import { URL_SEARCH_PARAMS } from '../../../constants';

export const useMapOverlayReport = () => {
const { projectCode, entityCode } = useParams();
const { selectedOverlay } = useMapOverlays(projectCode, entityCode);
const { startDate, endDate } = useDateRanges(
URL_SEARCH_PARAMS.MAP_OVERLAY_PERIOD,
selectedOverlay,
);
return useMapOverlayReportQuery(projectCode, entityCode, selectedOverlay, {
startDate,
endDate,
});
};
Loading

0 comments on commit 7f52f0a

Please sign in to comment.