Skip to content

Commit c6b5b41

Browse files
feat: add geolocation control (#282)
1 parent 3ec440c commit c6b5b41

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

src/components/map/index.spec.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { render, screen, waitFor, fireEvent } from '@testing-library/react';
33
import userEvent from '@testing-library/user-event';
44
import { MapComponent } from './index';
55

6+
const mockToast = vi.hoisted(() => ({
7+
error: vi.fn(),
8+
}));
9+
610
const mockMapRef = {
711
getCenter: vi.fn(() => ({ lng: 13.4, lat: 52.5 })),
812
getZoom: vi.fn(() => 10),
@@ -45,6 +49,22 @@ vi.mock('react-map-gl/maplibre', () => ({
4549
NavigationControl: vi.fn(() => (
4650
<div data-testid="navigation-control">Nav</div>
4751
)),
52+
GeolocateControl: vi.fn(({ onError }) => (
53+
<div data-testid="geolocate-control">
54+
<button
55+
data-testid="trigger-geolocate-error"
56+
onClick={() => onError?.({ PERMISSION_DENIED: false })}
57+
>
58+
Trigger Error
59+
</button>
60+
<button
61+
data-testid="trigger-geolocate-permission-denied"
62+
onClick={() => onError?.({ PERMISSION_DENIED: true })}
63+
>
64+
Trigger Permission Denied
65+
</button>
66+
</div>
67+
)),
4868
useMap: vi.fn(() => ({ current: mockMapRef })),
4969
}));
5070

@@ -212,6 +232,10 @@ vi.mock('@/utils/heightgraph', () => ({
212232
buildHeightgraphData: vi.fn(() => []),
213233
}));
214234

235+
vi.mock('sonner', () => ({
236+
toast: mockToast,
237+
}));
238+
215239
describe('MapComponent', () => {
216240
let localStorageMock: Record<string, string>;
217241

@@ -247,6 +271,11 @@ describe('MapComponent', () => {
247271
expect(screen.getByTestId('navigation-control')).toBeInTheDocument();
248272
});
249273

274+
it('should render geolocate control', () => {
275+
render(<MapComponent />);
276+
expect(screen.getByTestId('geolocate-control')).toBeInTheDocument();
277+
});
278+
250279
it('should render draw control', () => {
251280
render(<MapComponent />);
252281
expect(screen.getByTestId('draw-control')).toBeInTheDocument();
@@ -388,4 +417,30 @@ describe('MapComponent', () => {
388417
expect(screen.queryByTestId('map-info-popup')).not.toBeInTheDocument();
389418
});
390419
});
420+
421+
describe('GeolocateControl error handling', () => {
422+
it('should show default error toast when geolocate fails', async () => {
423+
const user = userEvent.setup();
424+
render(<MapComponent />);
425+
426+
await user.click(screen.getByTestId('trigger-geolocate-error'));
427+
428+
expect(mockToast.error).toHaveBeenCalledWith(
429+
"We couldn't get your location. Please try again."
430+
);
431+
});
432+
433+
it('should show permission denied error toast when location permission is denied', async () => {
434+
const user = userEvent.setup();
435+
render(<MapComponent />);
436+
437+
await user.click(
438+
screen.getByTestId('trigger-geolocate-permission-denied')
439+
);
440+
441+
expect(mockToast.error).toHaveBeenCalledWith(
442+
"We couldn't get your location. Please check your browser settings and allow location access."
443+
);
444+
});
445+
});
391446
});

src/components/map/index.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
Popup,
77
type MapRef,
88
NavigationControl,
9+
GeolocateControl,
10+
type GeolocateErrorEvent,
911
} from 'react-map-gl/maplibre';
1012
import type { MaplibreTerradrawControl } from '@watergis/maplibre-gl-terradraw';
1113
import type maplibregl from 'maplibre-gl';
@@ -52,6 +54,7 @@ import {
5254
useIsochronesQuery,
5355
useReverseGeocodeIsochrones,
5456
} from '@/hooks/use-isochrones-queries';
57+
import { toast } from 'sonner';
5558

5659
const { center, zoom: zoom_initial } = getInitialMapPosition();
5760

@@ -601,6 +604,16 @@ export const MapComponent = () => {
601604
setRouteHoverPopup(null);
602605
}, []);
603606

607+
const handleGeolocateError = useCallback((error: GeolocateErrorEvent) => {
608+
let defaultMessage = "We couldn't get your location. Please try again.";
609+
if (error.PERMISSION_DENIED) {
610+
defaultMessage =
611+
"We couldn't get your location. Please check your browser settings and allow location access.";
612+
}
613+
614+
toast.error(defaultMessage);
615+
}, []);
616+
604617
return (
605618
<Map
606619
ref={mapRef}
@@ -623,6 +636,7 @@ export const MapComponent = () => {
623636
id="mainMap"
624637
>
625638
<NavigationControl />
639+
<GeolocateControl onError={handleGeolocateError} />
626640
<DrawControl onUpdate={updateExcludePolygons} controlRef={drawRef} />
627641
<MapStyleControl
628642
customStyleData={customStyleData}

0 commit comments

Comments
 (0)