Skip to content

Commit

Permalink
fix: log a warning instead of throwing an error, make sure the map re…
Browse files Browse the repository at this point in the history
…nders anyway
  • Loading branch information
usefulthink committed Apr 18, 2024
1 parent 0753f71 commit 02a9cdc
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 34 deletions.
30 changes: 28 additions & 2 deletions src/components/__tests__/__snapshots__/map.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`throws an exception when missing center 1`] = `"Map center not set. Provide either a \`center\` or a \`defaultCenter\` prop."`;
exports[`camera configuration logs a warning message when missing configuration 1`] = `
[
"<Map> component is missing configuration. You have to provide zoom and center (via the \`zoom\`/\`defaultZoom\` and \`center\`/\`defaultCenter\` props) or specify the region to show using \`defaultBounds\`. See https://visgl.github.io/react-google-maps/docs/api-reference/components/map#required",
]
`;
exports[`throws an exception when missing zoom 1`] = `"Map zoom not set. Provide either a \`zoom\` or a \`defaultZoom\` prop."`;
exports[`camera configuration logs a warning message when missing configuration 2`] = `
[
"<Map> component is missing configuration. You have to provide zoom and center (via the \`zoom\`/\`defaultZoom\` and \`center\`/\`defaultCenter\` props) or specify the region to show using \`defaultBounds\`. See https://visgl.github.io/react-google-maps/docs/api-reference/components/map#required",
]
`;
exports[`camera configuration logs a warning message when missing configuration 3`] = `
[
"<Map> component is missing configuration. You have to provide zoom and center (via the \`zoom\`/\`defaultZoom\` and \`center\`/\`defaultCenter\` props) or specify the region to show using \`defaultBounds\`. See https://visgl.github.io/react-google-maps/docs/api-reference/components/map#required",
]
`;
exports[`camera configuration logs a warning message when missing configuration 4`] = `
[
"<Map> component is missing configuration. You have to provide zoom and center (via the \`zoom\`/\`defaultZoom\` and \`center\`/\`defaultCenter\` props) or specify the region to show using \`defaultBounds\`. See https://visgl.github.io/react-google-maps/docs/api-reference/components/map#required",
]
`;
exports[`camera configuration logs a warning message when missing configuration 5`] = `
[
"<Map> component is missing configuration. You have to provide zoom and center (via the \`zoom\`/\`defaultZoom\` and \`center\`/\`defaultCenter\` props) or specify the region to show using \`defaultBounds\`. See https://visgl.github.io/react-google-maps/docs/api-reference/components/map#required",
]
`;
exports[`throws an exception when rendering outside API provider 1`] = `"<Map> can only be used inside an <ApiProvider> component."`;
73 changes: 50 additions & 23 deletions src/components/__tests__/map.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import {render, screen, waitFor} from '@testing-library/react';
import {initialize, mockInstances} from '@googlemaps/jest-mocks';
import '@testing-library/jest-dom';

import {Map as GoogleMap} from '../map';
import {Map as GoogleMap, MapProps} from '../map';
import {APIProviderContext, APIProviderContextValue} from '../api-provider';
import {APILoadingStatus} from '../../libraries/api-loading-status';
import mocked = jest.mocked;

jest.mock('../../libraries/google-maps-api-loader');

Expand Down Expand Up @@ -88,24 +89,6 @@ test('throws an exception when rendering outside API provider', () => {
).toThrowErrorMatchingSnapshot();
});

test('throws an exception when missing center', () => {
// mute react error-message in test output
jest.spyOn(console, 'error').mockImplementation(() => {});

expect(() =>
render(<GoogleMap zoom={8} />, {wrapper})
).toThrowErrorMatchingSnapshot();
});

test('throws an exception when missing zoom', () => {
// mute react error-message in test output
jest.spyOn(console, 'error').mockImplementation(() => {});

expect(() =>
render(<GoogleMap center={{lat: 53.55, lng: 10.05}} />, {wrapper})
).toThrowErrorMatchingSnapshot();
});

describe('creating and updating map instance', () => {
let rerender: (ui: React.ReactElement) => void;

Expand Down Expand Up @@ -176,15 +159,59 @@ describe('creating and updating map instance', () => {
});
});

describe('map events and event-props', () => {
test.todo('events dispatched by the map are received via event-props');
});
describe('camera configuration', () => {
test.each([
[{}, true],
[{center: {lat: 0, lng: 0}}, true],
[{defaultCenter: {lat: 0, lng: 0}}, true],
[{zoom: 1}, true],
[{defaultZoom: 1}, true],
[{defaultBounds: {north: 1, east: 2, south: 3, west: 4}}, false],
[{defaultCenter: {lat: 0, lng: 0}, zoom: 0}, false],
[{center: {lat: 0, lng: 0}, zoom: 0}, false],
[{center: {lat: 0, lng: 0}, defaultZoom: 0}, false]
])(
'logs a warning message when missing configuration',
(props: MapProps, expectWarningMessage: boolean) => {
// mute warning in test output
const consoleWarn = jest
.spyOn(console, 'warn')
.mockImplementation(() => {});

render(<GoogleMap {...props} />, {wrapper});

if (expectWarningMessage)
expect(consoleWarn.mock.lastCall).toMatchSnapshot();
else expect(consoleWarn).not.toHaveBeenCalled();
}
);

test('makes sure that map renders without viewport configuration', async () => {
// mute warning in test output
console.warn = jest.fn();

render(<GoogleMap />, {wrapper});
await waitFor(() => expect(screen.getByTestId('map')).toBeInTheDocument());

expect(createMapSpy).toHaveBeenCalled();

const mapInstance = mocked(mockInstances.get(google.maps.Map).at(0)!);
expect(mapInstance.fitBounds).toHaveBeenCalledWith({
east: 180,
north: 90,
south: -90,
west: -180
});
});

describe('camera updates', () => {
test.todo('initial camera state is passed via mapOptions, not moveCamera');
test.todo('updated camera state is passed to moveCamera');
test.todo("re-renders with unchanged camera state don't trigger moveCamera");
test.todo(
"re-renders with props received via events don't trigger moveCamera"
);
});

describe('map events and event-props', () => {
test.todo('events dispatched by the map are received via event-props');
});
25 changes: 16 additions & 9 deletions src/components/map/use-map-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,17 @@ export function useMapInstance(
...mapOptions
} = props;

if (props.zoom === undefined && props.defaultZoom === undefined) {
throw new Error(
'Map zoom not set. Provide either a `zoom` or a `defaultZoom` prop.'
);
}

if (props.center === undefined && props.defaultCenter === undefined) {
throw new Error(
'Map center not set. Provide either a `center` or a `defaultCenter` prop.'
const hasZoom = props.zoom !== undefined || props.defaultZoom !== undefined;
const hasCenter =
props.center !== undefined || props.defaultCenter !== undefined;

if (!defaultBounds && (!hasZoom || !hasCenter)) {
console.warn(
'<Map> component is missing configuration. ' +
'You have to provide zoom and center (via the `zoom`/`defaultZoom` and ' +
'`center`/`defaultCenter` props) or specify the region to show using ' +
'`defaultBounds`. See ' +
'https://visgl.github.io/react-google-maps/docs/api-reference/components/map#required'
);
}

Expand Down Expand Up @@ -88,6 +90,11 @@ export function useMapInstance(
newMap.fitBounds(defaultBounds);
}

// prevent map not rendering due to missing configuration
else if (!hasZoom || !hasCenter) {
newMap.fitBounds({east: 180, west: -180, south: -90, north: 90});
}

// the savedMapState is used to restore the camera parameters when the mapId is changed
if (savedMapStateRef.current) {
const {mapId: savedMapId, cameraState: savedCameraState} =
Expand Down

0 comments on commit 02a9cdc

Please sign in to comment.