From f48f5587fcb811fe77892636842522f6f2afa0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Or=C5=82=C3=B3w?= <adrian.orlow@fishbrain.com> Date: Mon, 11 Mar 2024 15:52:41 +0100 Subject: [PATCH] fix: rfc changes --- docs/plugins/errors.md | 6 +++ .../MapNavigation.component.test.tsx | 2 +- .../MapViewer/utils/config/useOlMapView.ts | 7 ++- .../listeners/onMapPositionChange.test.ts | 4 +- .../utils/listeners/onMapPositionChange.ts | 16 +----- .../utils/listeners/useOlMapListeners.ts | 5 +- src/constants/errors.ts | 7 +++ src/redux/map/map.reducers.ts | 3 +- .../pluginsManager/map/zoom/setZoom.test.ts | 52 ++++++++++++++++++- .../pluginsManager/map/zoom/setZoom.ts | 13 ++++- 10 files changed, 87 insertions(+), 28 deletions(-) diff --git a/docs/plugins/errors.md b/docs/plugins/errors.md index 2795ec51..08d08e23 100644 --- a/docs/plugins/errors.md +++ b/docs/plugins/errors.md @@ -15,3 +15,9 @@ ## Project Errors - **Project does not exist**: This error occurs when the project data is not available. + +## Zoom errors + +- **Provided zoom value exeeds max zoom of ...**: This error occurs when `zoom` param of `setZoom` exeeds max zoom value of the selected map + +- **Provided zoom value exceeds min zoom of ...**: This error occurs when `zoom` param of `setZoom` exceeds min zoom value of the selected map diff --git a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx index cefbc59f..cf1aa42f 100644 --- a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx +++ b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx @@ -170,7 +170,7 @@ describe('MapNavigation - component', () => { histamineMapCloseButton.click(); }); - expect(dispatchEventMock).toHaveBeenCalledTimes(4); + expect(dispatchEventMock).toHaveBeenCalledTimes(3); expect(dispatchEventMock).toHaveBeenCalledWith('onSubmapClose', 5052); expect(dispatchEventMock).toHaveBeenCalledWith('onSubmapOpen', 52); }); diff --git a/src/components/Map/MapViewer/utils/config/useOlMapView.ts b/src/components/Map/MapViewer/utils/config/useOlMapView.ts index 4a4d9dc1..cd4f2ac0 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapView.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapView.ts @@ -1,6 +1,6 @@ /* eslint-disable no-magic-numbers */ import { OPTIONS } from '@/constants/map'; -import { mapDataInitialPositionSelector } from '@/redux/map/map.selectors'; +import { mapDataInitialPositionSelector, mapDataSizeSelector } from '@/redux/map/map.selectors'; import { MapInstance, Point } from '@/types/map'; import { usePointToProjection } from '@/utils/map/usePointToProjection'; import { View } from 'ol'; @@ -14,6 +14,7 @@ interface UseOlMapViewInput { export const useOlMapView = ({ mapInstance }: UseOlMapViewInput): MapConfig['view'] => { const mapInitialPosition = useSelector(mapDataInitialPositionSelector); + const mapSize = useSelector(mapDataSizeSelector); const pointToProjection = usePointToProjection(); const center = useMemo((): Point => { @@ -35,8 +36,10 @@ export const useOlMapView = ({ mapInstance }: UseOlMapViewInput): MapConfig['vie center: [center.x, center.y], zoom: mapInitialPosition.z, showFullExtent: OPTIONS.showFullExtent, + maxZoom: mapSize.maxZoom, + minZoom: mapSize.minZoom, }), - [center.x, center.y, mapInitialPosition.z], + [center.x, center.y, mapInitialPosition.z, mapSize.maxZoom, mapSize.minZoom], ); const view = useMemo(() => new View(viewConfig), [viewConfig]); diff --git a/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.test.ts b/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.test.ts index bc9228f0..295b91d1 100644 --- a/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.test.ts +++ b/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.test.ts @@ -16,8 +16,6 @@ const getEvent = (targetValues: ObjectEvent['target']['values_']): ObjectEvent = /* eslint-disable no-magic-numbers */ describe('onMapPositionChange - util', () => { - const MAP_ID = 52; - const LAST_ZOOM = 4; const cases: [MapSize, ObjectEvent['target']['values_'], Point][] = [ [ { @@ -65,7 +63,7 @@ describe('onMapPositionChange - util', () => { const dispatch = result.current; const event = getEvent(targetValues); - onMapPositionChange(mapSize, dispatch, MAP_ID, LAST_ZOOM)(event); + onMapPositionChange(mapSize, dispatch)(event); const { position } = mapDataSelector(store.getState()); expect(position.last).toMatchObject(lastPosition); diff --git a/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.ts b/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.ts index d49f6f3e..7102fec7 100644 --- a/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.ts +++ b/src/components/Map/MapViewer/utils/listeners/onMapPositionChange.ts @@ -1,33 +1,19 @@ import { setMapPosition } from '@/redux/map/map.slice'; import { MapSize } from '@/redux/map/map.types'; import { AppDispatch } from '@/redux/store'; -import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus'; import { latLngToPoint } from '@/utils/map/latLngToPoint'; import { toLonLat } from 'ol/proj'; import { ObjectEvent } from 'openlayers'; /* prettier-ignore */ export const onMapPositionChange = - (mapSize: MapSize, dispatch: AppDispatch, modelId: number, mapLastZoomValue: number | undefined) => + (mapSize: MapSize, dispatch: AppDispatch) => (e: ObjectEvent): void => { // eslint-disable-next-line no-underscore-dangle const { center, zoom } = e.target.values_; const [lng, lat] = toLonLat(center); const { x, y } = latLngToPoint([lat, lng], mapSize, { rounded: true }); - if (mapLastZoomValue !== zoom) { - PluginsEventBus.dispatchEvent('onZoomChanged', { - modelId, - zoom, - }); - } - - PluginsEventBus.dispatchEvent('onCenterChanged', { - modelId, - x, - y - }); - dispatch( setMapPosition({ x, diff --git a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts index 1742d6fa..5d7631ff 100644 --- a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts +++ b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts @@ -1,6 +1,6 @@ import { OPTIONS } from '@/constants/map'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; -import { mapDataLastZoomValue, mapDataSizeSelector } from '@/redux/map/map.selectors'; +import { mapDataSizeSelector } from '@/redux/map/map.selectors'; import { currentModelIdSelector } from '@/redux/models/models.selectors'; import { MapInstance } from '@/types/map'; import { View } from 'ol'; @@ -22,7 +22,6 @@ interface UseOlMapListenersInput { export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput): void => { const mapSize = useSelector(mapDataSizeSelector); const modelId = useSelector(currentModelIdSelector); - const mapLastZoomValue = useSelector(mapDataLastZoomValue); const coordinate = useRef<Coordinate>([]); const pixel = useRef<Pixel>([]); const dispatch = useAppDispatch(); @@ -36,7 +35,7 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput) ); const handleChangeCenter = useDebouncedCallback( - onMapPositionChange(mapSize, dispatch, modelId, mapLastZoomValue), + onMapPositionChange(mapSize, dispatch), OPTIONS.queryPersistTime, { leading: false }, ); diff --git a/src/constants/errors.ts b/src/constants/errors.ts index b8639a2f..cba5c0d0 100644 --- a/src/constants/errors.ts +++ b/src/constants/errors.ts @@ -3,3 +3,10 @@ export const DEFAULT_ERROR: Error = { message: '', name: '' }; export const OVERVIEW_IMAGE_ERRORS = { IMAGE_ID_IS_INVALID: "Image id is invalid. There's no such image in overview images list", }; + +export const ZOOM_ERRORS = { + ZOOM_VALUE_TOO_HIGH: (maxZoom: number): string => + `Provided zoom value exeeds max zoom of ${maxZoom}`, + ZOOM_VALUE_TOO_LOW: (minZoom: number): string => + `Provided zoom value exceeds min zoom of ${minZoom}`, +}; diff --git a/src/redux/map/map.reducers.ts b/src/redux/map/map.reducers.ts index e21b6566..6ef98049 100644 --- a/src/redux/map/map.reducers.ts +++ b/src/redux/map/map.reducers.ts @@ -33,6 +33,7 @@ export const setMapDataReducer = (state: MapState, action: SetMapDataAction): vo export const setMapPositionReducer = (state: MapState, action: SetMapPositionDataAction): void => { const position = action.payload || {}; const statePosition = state.data.position; + const lastZoom = statePosition.last.z; const finalPosition = getPointMerged(position || {}, statePosition.last); const { modelId } = state.data; @@ -42,7 +43,7 @@ export const setMapPositionReducer = (state: MapState, action: SetMapPositionDat y: finalPosition.y, }); - if (position?.z) { + if (position?.z && lastZoom && lastZoom !== position?.z) { PluginsEventBus.dispatchEvent('onZoomChanged', { modelId, zoom: position?.z, diff --git a/src/services/pluginsManager/map/zoom/setZoom.test.ts b/src/services/pluginsManager/map/zoom/setZoom.test.ts index dc92068a..55502f30 100644 --- a/src/services/pluginsManager/map/zoom/setZoom.test.ts +++ b/src/services/pluginsManager/map/zoom/setZoom.test.ts @@ -1,6 +1,8 @@ /* eslint-disable no-magic-numbers */ +import { ZOOM_ERRORS } from '@/constants/errors'; +import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/map/map.fixtures'; import { setLastPositionZoom } from '@/redux/map/map.slice'; -import { store } from '@/redux/store'; +import { RootState, store } from '@/redux/store'; import { ZodError } from 'zod'; import { setZoom } from './setZoom'; @@ -8,8 +10,38 @@ jest.mock('../../../../redux/store'); describe('setZoom - plugin method', () => { const dispatchSpy = jest.spyOn(store, 'dispatch'); + const getStateSpy = jest.spyOn(store, 'getState'); - describe('when zoom is invalid', () => { + beforeEach(() => { + getStateSpy.mockImplementation( + () => + ({ + map: { + data: { + ...initialMapDataFixture, + position: { + ...initialMapDataFixture.position, + last: { + x: 2137, + y: 420, + z: 1.488, + }, + }, + size: { + ...initialMapDataFixture.size, + minZoom: 2, + maxZoom: 8, + }, + }, + loading: 'succeeded', + error: { message: '', name: '' }, + openedMaps: openedMapsThreeSubmapsFixture, + }, + }) as RootState, + ); + }); + + describe('when zoom value type is invalid', () => { const invalidZoom = [-1, -123, '-123'] as number[]; it.each(invalidZoom)('should throw error', zoom => { @@ -17,6 +49,22 @@ describe('setZoom - plugin method', () => { }); }); + describe('when zoom value value exeeds max zoom', () => { + const invalidZoom = [444, 21, 9] as number[]; + + it.each(invalidZoom)('should throw error', zoom => { + expect(() => setZoom(zoom)).toThrow(ZOOM_ERRORS.ZOOM_VALUE_TOO_HIGH(8)); + }); + }); + + describe('when zoom value value exeeds min zoom', () => { + const invalidZoom = [1, 0] as number[]; + + it.each(invalidZoom)('should throw error', zoom => { + expect(() => setZoom(zoom)).toThrow(ZOOM_ERRORS.ZOOM_VALUE_TOO_LOW(2)); + }); + }); + describe('when zoom is valid', () => { const zoom = 2; diff --git a/src/services/pluginsManager/map/zoom/setZoom.ts b/src/services/pluginsManager/map/zoom/setZoom.ts index 8d599604..f161d1c0 100644 --- a/src/services/pluginsManager/map/zoom/setZoom.ts +++ b/src/services/pluginsManager/map/zoom/setZoom.ts @@ -1,10 +1,21 @@ +import { ZOOM_ERRORS } from '@/constants/errors'; import { zPointSchema } from '@/models/pointSchema'; +import { mapDataSizeSelector } from '@/redux/map/map.selectors'; import { setLastPositionZoom } from '@/redux/map/map.slice'; import { store } from '@/redux/store'; export const setZoom = (zoom: number): void => { - const { dispatch } = store; + const { dispatch, getState } = store; + const { minZoom, maxZoom } = mapDataSizeSelector(getState()); zPointSchema.parse(zoom); + if (zoom < minZoom) { + throw Error(ZOOM_ERRORS.ZOOM_VALUE_TOO_LOW(minZoom)); + } + + if (zoom > maxZoom) { + throw Error(ZOOM_ERRORS.ZOOM_VALUE_TOO_HIGH(maxZoom)); + } + dispatch(setLastPositionZoom({ zoom })); }; -- GitLab