Skip to content

Commit

Permalink
BHBC-1937 - Enhance MapContainer with changes from BioHub (#827)
Browse files Browse the repository at this point in the history
BHBC-1937: Ported MapContainer improvements over from platform:
 * Created new React components to encapsulate Map components and `wfs-utils` helper for rendering and selecting regional/mu layers
 * Updated tests and added type defs for map util functions.
  • Loading branch information
curtisupshall authored Oct 13, 2022
1 parent e7776b7 commit 01d1fab
Show file tree
Hide file tree
Showing 40 changed files with 1,389 additions and 928 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,6 @@ dist
.n8n
n8n/.n8n
n8n/.config

# IDE custom workspace settings
.vscode/settings.json
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ n8n-setup: | build-n8n-setup run-n8n-setup ## Performs all commands necessary to
n8n-export: | build-n8n-export run-n8n-export ## Performs all commands necessary to export the latest n8n credentials and workflows
clamav: | build-clamav run-clamav ## Performs all commands necessary to run clamav

fix: | lint-fix format-fix ## Performs both lint-fix and format-fix commands

## ------------------------------------------------------------------------------
## Setup/Cleanup Commands
## ------------------------------------------------------------------------------
Expand Down
15 changes: 12 additions & 3 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
"@testing-library/user-event": "~12.6.0",
"@types/geojson": "~7946.0.7",
"@types/jest": "~26.0.20",
"@types/leaflet": "~1.5.23",
"@types/leaflet": "^1.8.0",
"@types/leaflet-draw": "^1.0.5",
"@types/leaflet-fullscreen": "~1.0.6",
"@types/lodash-es": "~4.17.4",
"@types/node": "~14.14.31",
Expand Down
11 changes: 6 additions & 5 deletions app/src/components/boundary/MapBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import MapContainer from 'components/map/MapContainer';
import { ProjectSurveyAttachmentValidExtensions } from 'constants/attachments';
import { FormikContextType } from 'formik';
import { Feature } from 'geojson';
import { LatLngBoundsExpression } from 'leaflet';
import get from 'lodash-es/get';
import React, { useEffect, useState } from 'react';
import {
Expand Down Expand Up @@ -55,7 +56,7 @@ export interface IMapBoundaryProps {
name: string;
title: string;
mapId: string;
bounds: any[];
bounds: LatLngBoundsExpression | undefined;
formikProps: FormikContextType<any>;
}

Expand All @@ -74,7 +75,7 @@ const MapBoundary: React.FC<IMapBoundaryProps> = (props) => {

const [openUploadBoundary, setOpenUploadBoundary] = useState(false);
const [shouldUpdateBounds, setShouldUpdateBounds] = useState<boolean>(false);
const [updatedBounds, setUpdatedBounds] = useState<any[][] | undefined>(undefined);
const [updatedBounds, setUpdatedBounds] = useState<LatLngBoundsExpression | undefined>(undefined);
const [selectedLayer, setSelectedLayer] = useState('');
const [inferredLayersInfo, setInferredLayersInfo] = useState<IInferredLayers>({
parks: [],
Expand Down Expand Up @@ -180,10 +181,10 @@ const MapBoundary: React.FC<IMapBoundaryProps> = (props) => {
<Box position="relative" height={500}>
<MapContainer
mapId={mapId}
geometryState={{
geometry: get(values, name),
setGeometry: (newGeo: Feature[]) => setFieldValue(name, newGeo)
drawControls={{
initialFeatures: get(values, name)
}}
onDrawChange={(newGeo: Feature[]) => setFieldValue(name, newGeo)}
bounds={(shouldUpdateBounds && updatedBounds) || bounds}
selectedLayer={selectedLayer}
setInferredLayersInfo={setInferredLayersInfo}
Expand Down
64 changes: 37 additions & 27 deletions app/src/components/map/MapContainer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import bbox from '@turf/bbox';
import { Feature } from 'geojson';
import { createMemoryHistory } from 'history';
import { useBiohubApi } from 'hooks/useBioHubApi';
import { LatLngBoundsExpression } from 'leaflet';
import React from 'react';
import { Router } from 'react-router-dom';
import MapContainer, { INonEditableGeometries } from './MapContainer';
Expand Down Expand Up @@ -47,7 +48,7 @@ describe('MapContainer', () => {
});
});

const geometry: Feature[] = [
const initialFeatures: Feature[] = [
{
type: 'Feature',
id: 'myGeo',
Expand All @@ -68,7 +69,7 @@ describe('MapContainer', () => {
}
}
];
const setGeometry = jest.fn();
const onDrawChange = jest.fn();

mockBiohubApi().external.get.mockResolvedValue({
features: []
Expand All @@ -79,7 +80,7 @@ describe('MapContainer', () => {

test('matches the snapshot with geometries being passed in', () => {
const { asFragment } = render(
<MapContainer mapId="myMap" classes={classes} geometryState={{ geometry, setGeometry }} />
<MapContainer mapId="myMap" classes={classes} drawControls={{ initialFeatures }} onDrawChange={onDrawChange} />
);

expect(asFragment()).toMatchSnapshot();
Expand All @@ -106,7 +107,8 @@ describe('MapContainer', () => {
<MapContainer
mapId="myMap"
classes={classes}
geometryState={{ geometry, setGeometry }}
drawControls={{ initialFeatures }}
onDrawChange={onDrawChange}
nonEditableGeometries={nonEditableGeometries}
/>
);
Expand Down Expand Up @@ -143,7 +145,8 @@ describe('MapContainer', () => {
<MapContainer
mapId="myMap"
classes={classes}
geometryState={{ geometry, setGeometry }}
drawControls={{ initialFeatures }}
onDrawChange={onDrawChange}
nonEditableGeometries={nonEditableGeometries}
/>
</Router>
Expand All @@ -153,46 +156,47 @@ describe('MapContainer', () => {
});

test('matches the snapshot with draw controls hidden', () => {
const { asFragment } = render(<MapContainer mapId="myMap" classes={classes} hideDrawControls={true} />);
const { asFragment } = render(<MapContainer mapId="myMap" classes={classes} />);

expect(asFragment()).toMatchSnapshot();
});

test('draws a marker successfully on the map and updates the geometry', () => {
const { getByText, getByRole } = render(
<MapContainer
mapId="myMap"
classes={classes}
geometryState={{ geometry, setGeometry }}
showDrawControls={{ marker: true }}
/>
<MapContainer mapId="myMap" classes={classes} drawControls={{ initialFeatures }} onDrawChange={onDrawChange} />
);

fireEvent.click(getByText('Draw a marker'));

// Click on existing geometry on map to place a marker in that location
fireEvent.click(getByRole('presentation'));

expect(setGeometry).toHaveBeenCalled();
expect(onDrawChange).toHaveBeenCalled();
});

test('sets the bounds of the geo being passed in successfully', () => {
const bboxCoords = bbox(geometry[0]);
const bounds = [
const bboxCoords = bbox(initialFeatures[0]);
const bounds: LatLngBoundsExpression = [
[bboxCoords[1], bboxCoords[0]],
[bboxCoords[3], bboxCoords[2]]
];

const { asFragment } = render(
<MapContainer mapId="myMap" classes={classes} geometryState={{ geometry, setGeometry }} bounds={bounds} />
<MapContainer
mapId="myMap"
classes={classes}
drawControls={{ initialFeatures }}
onDrawChange={onDrawChange}
bounds={bounds}
/>
);

expect(asFragment()).toMatchSnapshot();
});

test('edits geometries as expected', async () => {
const { getByText } = render(
<MapContainer mapId="myMap" classes={classes} geometryState={{ geometry, setGeometry }} />
<MapContainer mapId="myMap" classes={classes} drawControls={{ initialFeatures }} onDrawChange={onDrawChange} />
);

fireEvent.click(getByText('Edit layers'));
Expand All @@ -201,12 +205,12 @@ describe('MapContainer', () => {

fireEvent.click(getByText('Save'));

expect(setGeometry).toHaveBeenCalledWith(geometry);
expect(onDrawChange).toHaveBeenCalledWith(initialFeatures);
});

test('deletes geometries currently present on the map successfully when user confirms', async () => {
const { getByText } = render(
<MapContainer mapId="myMap" classes={classes} geometryState={{ geometry, setGeometry }} />
<MapContainer mapId="myMap" classes={classes} drawControls={{ initialFeatures }} onDrawChange={onDrawChange} />
);

fireEvent.click(getByText('Delete layers'));
Expand All @@ -215,18 +219,20 @@ describe('MapContainer', () => {
fireEvent.click(getByText('Clear All'));
});

render(<MapContainer mapId="myMap" classes={classes} geometryState={{ geometry, setGeometry }} />);
render(
<MapContainer mapId="myMap" classes={classes} drawControls={{ initialFeatures }} onDrawChange={onDrawChange} />
);

await waitFor(() => {
fireEvent.click(getByText('Yes'));
});

expect(setGeometry).toHaveBeenCalledWith([]);
expect(onDrawChange).toHaveBeenCalledWith([]);
});

test('does not delete geometries present on the map when user does not confirm by clicking no', async () => {
const { getByText } = render(
<MapContainer mapId="myMap" classes={classes} geometryState={{ geometry, setGeometry }} />
<MapContainer mapId="myMap" classes={classes} drawControls={{ initialFeatures }} onDrawChange={onDrawChange} />
);

fireEvent.click(getByText('Delete layers'));
Expand All @@ -235,18 +241,20 @@ describe('MapContainer', () => {
fireEvent.click(getByText('Clear All'));
});

render(<MapContainer mapId="myMap" classes={classes} geometryState={{ geometry, setGeometry }} />);
render(
<MapContainer mapId="myMap" classes={classes} drawControls={{ initialFeatures }} onDrawChange={onDrawChange} />
);

await waitFor(() => {
fireEvent.click(getByText('No'));
});

expect(setGeometry).toHaveBeenCalledWith(geometry);
expect(onDrawChange).toHaveBeenCalledWith(initialFeatures);
});

test('does not delete geometries present on the map when user does not confirm by clicking out of the dialog', async () => {
const { getByText, getAllByRole } = render(
<MapContainer mapId="myMap" classes={classes} geometryState={{ geometry, setGeometry }} />
<MapContainer mapId="myMap" classes={classes} drawControls={{ initialFeatures }} onDrawChange={onDrawChange} />
);

fireEvent.click(getByText('Delete layers'));
Expand All @@ -255,14 +263,16 @@ describe('MapContainer', () => {
fireEvent.click(getByText('Clear All'));
});

render(<MapContainer mapId="myMap" classes={classes} geometryState={{ geometry, setGeometry }} />);
render(
<MapContainer mapId="myMap" classes={classes} drawControls={{ initialFeatures }} onDrawChange={onDrawChange} />
);

await waitFor(() => {
// Get the backdrop, then get the firstChild because this is where the event listener is attached
//@ts-ignore
fireEvent.click(getAllByRole('presentation')[0].firstChild);
});

expect(setGeometry).toHaveBeenCalledWith(geometry);
expect(onDrawChange).toHaveBeenCalledWith(initialFeatures);
});
});
Loading

0 comments on commit 01d1fab

Please sign in to comment.