Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/WG-260 add map control bar #303

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3a62e0c
Keep track of mouse position using context/hook
nathanfranklin Jan 6, 2025
71c4b69
Add docstr
nathanfranklin Jan 6, 2025
efe0d47
Add useFeatureLoadingState hook
nathanfranklin Jan 6, 2025
e5ce2f8
Add map hooks to index
nathanfranklin Jan 6, 2025
2952b2c
Add position tracker to Map
nathanfranklin Jan 6, 2025
e76786a
Add missing index
nathanfranklin Jan 6, 2025
01524f0
Update package-lock.json
nathanfranklin Jan 9, 2025
5dc6c71
Add useFeatureLoadingState to index
nathanfranklin Jan 9, 2025
9e6e771
Add MapControlBar
nathanfranklin Jan 9, 2025
6754387
Merge branch 'main' into task/WG-260-add-map-control-bar
nathanfranklin Jan 9, 2025
92b30a7
Add hook to get projects users and use in control bar
nathanfranklin Jan 10, 2025
9f4511d
Improve project errors and handle switching to private map
nathanfranklin Jan 13, 2025
4fc4b51
Add useDesignProject hook
nathanfranklin Jan 15, 2025
14c48bb
Rework ds project type and fixture
nathanfranklin Jan 15, 2025
971ab25
Merge branch 'main' into task/WG-260-add-map-control-bar
nathanfranklin Jan 15, 2025
cac32c5
Update package-lock.json after merge main
nathanfranklin Jan 16, 2025
0b1c936
Rework styling/layout
nathanfranklin Jan 17, 2025
1f9e5b4
update to new react-query and lint issues
nathanfranklin Jan 17, 2025
d0d97b7
Remove unused properties
nathanfranklin Jan 17, 2025
1b39c68
Remove error as handled elswhere
nathanfranklin Jan 17, 2025
860b459
Format file
nathanfranklin Jan 17, 2025
04593a3
Improve style
nathanfranklin Jan 17, 2025
5824c61
Rework useCurrentFeatures
nathanfranklin Jan 17, 2025
c4b77f3
Fix tests
nathanfranklin Jan 17, 2025
066c04b
Refactor return type of useCurrentFeatures
nathanfranklin Jan 17, 2025
7150338
Update useCurrentFeatures test
nathanfranklin Jan 17, 2025
ab23abb
Add tests
nathanfranklin Jan 18, 2025
c35e6ef
Add tests
nathanfranklin Jan 21, 2025
8df1f44
Add usefull comment to see request mocks
nathanfranklin Jan 21, 2025
42889a9
Fix test issue
nathanfranklin Jan 21, 2025
69ffd5b
Fix some lint errors
nathanfranklin Jan 21, 2025
ec0852f
Fix some lint warnings
nathanfranklin Jan 21, 2025
a2acede
Merge branch 'main' into task/WG-260-add-map-control-bar
nathanfranklin Jan 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion react/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { testDevConfiguration } from '@hazmapper/__fixtures__/appConfigurationFixture';
import { server } from '@hazmapper/test/testUtil';
import { server, testQueryClient } from '@hazmapper/test/testUtil';

/***** A) Setup the configuration used for unit testing *****/
jest.mock('@hazmapper/hooks/environment/getLocalAppConfiguration', () => ({
Expand Down Expand Up @@ -52,6 +52,9 @@ export function shouldIgnoreError(args: any[]): boolean {
/***** C) Setup testing and also ensure that we are mocking things in tests *****/

beforeAll(() => {
// Uncomment next line to see all handlers
// console.log('Registered handlers:', server.listHandlers());

// Establish mocking of APIs before all tests
server.listen({
onUnhandledRequest: 'error',
Expand All @@ -77,6 +80,11 @@ beforeAll(() => {
});
});

// Clear the React Query cache before each test
beforeEach(() => {
testQueryClient.clear();
});

// Reset any runtime request handlers we may add during the tests
afterEach(() => server.resetHandlers());

Expand Down
7,793 changes: 2,777 additions & 5,016 deletions react/package-lock.json

Large diffs are not rendered by default.

130 changes: 64 additions & 66 deletions react/src/__fixtures__/projectFixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,82 +5,80 @@ import {
ProjectRequest,
} from '../types';

export const designSafeProjectMock: DesignSafeProject = {
uuid: 'proj-uuid',
name: 'designsafe.project',
created: '2019-10-31T16:04:26.327Z',
lastUpdated: '2019-10-31T16:04:30.163Z',
associationIds: [],
value: {
dois: [],
coPis: [],
title: 'Sample DesignSafe Project',
users: [
{
inst: 'University of Texas at Austin (utexas.edu)',
role: 'pi',
email: '[email protected]',
fname: 'Fixture First Name',
lname: 'Fixture Last Name',
username: 'fixture1Username',
},
{
inst: 'University of Texas at Austin (utexas.edu)',
role: 'co_pi',
email: '[email protected]',
fname: 'Tester',
lname: 'Test',
username: 'fixture2Username',
},
],
authors: [],
frTypes: [],
nhEvent: '',
nhTypes: [],
fileObjs: [],
fileTags: [],
keywords: [],
nhEvents: [],
dataTypes: [],
projectId: 'proj-id',
tombstone: false,
facilities: [],
nhLatitude: '',
nhLocation: '',
description: 'Map Test description required.',
nhLongitude: '',
projectType: 'None',
teamMembers: [],
awardNumbers: [],
guestMembers: [],
hazmapperMaps: [
{
name: 'Hazmapper_TestProject',
path: '/',
uuid: '620aeaf4-f813-4b90-ba52-bc87cfa7b07b',
deployment: 'production',
},
],
referencedData: [],
associatedProjects: [],
},
};

export const projectMock: Project = {
id: 1,
uuid: 'abc123',
name: 'Sample Project',
description: 'A sample project for testing purposes.',
public: true,
system_file: 'sample-file',
system_id: 'sample-id',
system_id:
'project-1234' /* 'project-' prefix implies its a DesignSafe project system */,
system_path: '/path/to/sample',
deletable: true,
streetview_instances: null,
ds_project: {
uuid: 'proj-uuid',
projectId: 'proj-id',
title: 'Sample DesignSafe Project',
value: {
dois: [],
coPis: [],
title: 'Hazmapper V3 PROD Map Test 2024.08.07',
users: [
{
inst: 'University of Texas at Austin (utexas.edu)',
role: 'pi',
email: '[email protected]',
fname: 'Fixture First Name',
lname: 'Fixture Last Name',
username: 'fixture1Username',
},
{
inst: 'University of Texas at Austin (utexas.edu)',
role: 'co_pi',
email: '[email protected]',
fname: 'Tester',
lname: 'Test',
username: 'fixture2Username',
},
],
authors: [],
frTypes: [],
nhEvent: '',
nhTypes: [],
fileObjs: [],
fileTags: [],
keywords: [],
nhEvents: [],
dataTypes: [],
projectId: 'PRJ-5566',
tombstone: false,
facilities: [],
nhLatitude: '',
nhLocation: '',
description: 'Map Test description required.',
nhLongitude: '',
projectType: 'None',
teamMembers: [],
awardNumbers: [],
guestMembers: [],
hazmapperMaps: [
{
name: 'Hazmapper_TestProject',
path: '/',
uuid: '620aeaf4-f813-4b90-ba52-bc87cfa7b07b',
deployment: 'production',
},
],
referencedData: [],
associatedProjects: [],
},
},
};

export const designSafeProjectMock: DesignSafeProject = {
uuid: 'proj-uuid',
projectId: 'proj-id',
title: 'Sample DesignSafe Project',
value: {},
ds_project: designSafeProjectMock,
};

export const designSafeProjectCollectionMock: DesignSafeProjectCollection = {
Expand Down
1 change: 1 addition & 0 deletions react/src/__fixtures__/usersFixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const users = [{ id: 1, username: 'user' }];
54 changes: 35 additions & 19 deletions react/src/components/FeatureFileTree/FeatureFileTree.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { fireEvent, waitFor } from '@testing-library/react';
import { fireEvent, waitFor, act } from '@testing-library/react';
import { http, HttpResponse } from 'msw';
import FeatureFileTree from './FeatureFileTree';
import { server, renderInTest } from '@hazmapper/test/testUtil';
Expand All @@ -24,11 +24,13 @@ describe('FeatureFileTree', () => {
jest.clearAllMocks();
});

it('renders feature list correctly', () => {
const { getByText } = renderInTest(
<FeatureFileTree {...defaultTreeProps} />
);
it('renders feature list correctly', async () => {
let rendered;
await act(async () => {
rendered = renderInTest(<FeatureFileTree {...defaultTreeProps} />);
});

const { getByText } = rendered;
expect(getByText('foo')).toBeDefined();
expect(getByText('image1.JPG')).toBeDefined();
expect(getByText('image2.JPG')).toBeDefined();
Expand All @@ -48,37 +50,51 @@ describe('FeatureFileTree', () => {
)
);

const { getByTestId } = renderInTest(
<FeatureFileTree {...defaultTreeProps} />,
`/?selectedFeature=${featureId}`
);
let rendered;
await act(async () => {
rendered = renderInTest(
<FeatureFileTree {...defaultTreeProps} />,
`/?selectedFeature=${featureId}`
);
});

// Find and click delete button (as featured is selected)
const { getByTestId } = rendered;
const deleteButton = getByTestId('delete-feature-button');
fireEvent.click(deleteButton);
await act(async () => {
fireEvent.click(deleteButton);
});

await waitFor(() => {
expect(wasDeleted).toBeTruthy();
});
});

it('does not show delete button for public projects', () => {
const { queryByTestId } = renderInTest(
<FeatureFileTree {...defaultTreeProps} isPublicView={true} />,
'/?selectedFeature=1'
);
it('does not show delete button for public projects', async () => {
let rendered;
await act(async () => {
rendered = renderInTest(
<FeatureFileTree {...defaultTreeProps} isPublicView={true} />,
'/?selectedFeature=1'
);
});

// Verify delete button is not present
const { queryByTestId } = rendered;
const deleteButton = queryByTestId('delete-feature-button');
expect(deleteButton).toBeNull();
});

it('does not show delete button when no feature is selected', () => {
const { queryByTestId } = renderInTest(
<FeatureFileTree {...defaultTreeProps} isPublicView={false} />
);
it('does not show delete button when no feature is selected', async () => {
let rendered;
await act(async () => {
rendered = renderInTest(
<FeatureFileTree {...defaultTreeProps} isPublicView={false} />
);
});

// Verify delete button is not present
const { queryByTestId } = rendered;
const deleteButton = queryByTestId('delete-feature-button');
expect(deleteButton).toBeNull();
});
Expand Down
8 changes: 7 additions & 1 deletion react/src/components/Map/Map.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ import { renderInTest } from '@hazmapper/test/testUtil';
import Map from './Map';
import { tileServerLayers } from '../../__fixtures__/tileServerLayerFixture';
import { featureCollection } from '../../__fixtures__/featuresFixture';
import { MapPositionProvider } from '@hazmapper/context/MapContext';

test('renders map', () => {
const { getByText } = renderInTest(
<Map baseLayers={tileServerLayers} featureCollection={featureCollection} />
<MapPositionProvider>
<Map
baseLayers={tileServerLayers}
featureCollection={featureCollection}
/>
</MapPositionProvider>
);
expect(getByText(/Map/)).toBeDefined();
});
20 changes: 11 additions & 9 deletions react/src/components/Map/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { useFeatureSelection } from '@hazmapper/hooks';
import { MAP_CONFIG } from './config';
import FitBoundsHandler from './FitBoundsHandler';
import PositionTracker from './PositionTracker';
import { createMarkerIcon, createClusterIcon } from './markerCreators';
import { calculatePointCloudMarkerPosition } from './utils';

Expand Down Expand Up @@ -60,15 +61,15 @@ const LeafletMap: React.FC<LeafletMapProps> = ({
baseLayers = [],
featureCollection,
}) => {
const { selectedFeatureId, setSelectedFeatureId } = useFeatureSelection();
const { setSelectedFeatureId } = useFeatureSelection();

const handleFeatureClick = useCallback(
(feature: any) => {
setSelectedFeatureId(feature.id);

//TODO handle clicking on streetview https://tacc-main.atlassian.net/browse/WG-392
},
[selectedFeatureId]
[setSelectedFeatureId]
);

const activeBaseLayers = useMemo(
Expand All @@ -81,19 +82,19 @@ const LeafletMap: React.FC<LeafletMapProps> = ({
streetviewFeatures: Feature[];
}

// Initial accumulator state
const initialAccumulator: FeatureAccumulator = {
generalGeoJsonFeatures: [],
markerFeatures: [],
streetviewFeatures: [],
};

const {
generalGeoJsonFeatures,
markerFeatures,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
streetviewFeatures /* Add streetview support https://tacc-main.atlassian.net/browse/WG-392 */,
} = useMemo(() => {
// Initial accumulator state
const initialAccumulator: FeatureAccumulator = {
generalGeoJsonFeatures: [],
markerFeatures: [],
streetviewFeatures: [],
};

return featureCollection.features.reduce<FeatureAccumulator>(
(accumulator, feature: Feature) => {
if (feature.geometry.type === FeatureType.Point) {
Expand Down Expand Up @@ -203,6 +204,7 @@ const LeafletMap: React.FC<LeafletMapProps> = ({
{/* Handles zooming to a specific feature or to all features */}
<FitBoundsHandler featureCollection={featureCollection} />
<ZoomControl position="bottomright" />
<PositionTracker />
</MapContainer>
);
};
Expand Down
13 changes: 13 additions & 0 deletions react/src/components/Map/PositionTracker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useMapMousePositionWriter } from '@hazmapper/hooks';

/**
* Component that tracks mouse position on a Leaflet map
* Must be used inside a react-leaflet's MapContainer to write position state
* Position can be read elsewhere using useMapMousePosition hook
*/
const PositionTracker = () => {
useMapMousePositionWriter();
return null;
};

export default PositionTracker;
Loading
Loading