From ac14c550704d60db1326bfe7e428d1f7dd2c0a8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrian=20Or=C5=82=C3=B3w?= <adrian.orlow@fishbrain.com>
Date: Tue, 17 Oct 2023 23:19:43 +0200
Subject: [PATCH] feat(map): improve hook and tests of useOlMap

---
 .../Map/MapViewer/utils/useOlMap.test.ts      | 88 +++++++++++++++++++
 .../Map/MapViewer/utils/useOlMap.ts           | 17 ++--
 2 files changed, 98 insertions(+), 7 deletions(-)
 create mode 100644 src/components/Map/MapViewer/utils/useOlMap.test.ts

diff --git a/src/components/Map/MapViewer/utils/useOlMap.test.ts b/src/components/Map/MapViewer/utils/useOlMap.test.ts
new file mode 100644
index 00000000..c606b77a
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/useOlMap.test.ts
@@ -0,0 +1,88 @@
+import mapSlice, { setMapData } from '@/redux/map/map.slice';
+import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
+import { renderHook, waitFor } from '@testing-library/react';
+import { Map } from 'ol';
+import React from 'react';
+import { useOlMap } from './useOlMap';
+
+const useRefValue = {
+  current: null,
+};
+
+Object.defineProperty(useRefValue, 'current', {
+  get: jest.fn(() => ({
+    innerHTML: '',
+    appendChild: jest.fn(),
+    addEventListener: jest.fn(),
+    getRootNode: jest.fn(),
+  })),
+  set: jest.fn(() => ({
+    innerHTML: '',
+    appendChild: jest.fn(),
+    addEventListener: jest.fn(),
+    getRootNode: jest.fn(),
+  })),
+});
+
+jest.spyOn(React, 'useRef').mockReturnValue(useRefValue);
+
+describe('useOlMap - util', () => {
+  const { Wrapper, store } = getReduxWrapperUsingSliceReducer('map', mapSlice);
+
+  describe('when initializing', () => {
+    it('should set map instance', async () => {
+      const dummyElement = document.createElement('div');
+      const { result } = renderHook(() => useOlMap({ target: dummyElement }), { wrapper: Wrapper });
+      await waitFor(() => expect(result.current.mapInstance).toBeInstanceOf(Map));
+    });
+
+    it('should render content inside the target element', async () => {
+      const FIRST_NODE = 0;
+      const dummyElement = document.createElement('div');
+      renderHook(() => useOlMap({ target: dummyElement }), { wrapper: Wrapper });
+
+      expect(dummyElement.childNodes[FIRST_NODE]).toHaveClass('ol-viewport');
+    });
+  });
+
+  describe('when initialized', () => {
+    it('should modify view of the map instance on position config change', async () => {
+      const dummyElement = document.createElement('div');
+      const { result } = renderHook(() => useOlMap({ target: dummyElement }), { wrapper: Wrapper });
+      const setViewSpy = jest.spyOn(result.current.mapInstance as Map, 'setView');
+      const CALLED_ONCE = 1;
+
+      store.dispatch(
+        setMapData({
+          position: {
+            x: 0,
+            y: 0,
+          },
+        }),
+      );
+
+      await waitFor(() => expect(setViewSpy).toBeCalledTimes(CALLED_ONCE));
+    });
+
+    it('should modify layers of the map instance on size config change', async () => {
+      const dummyElement = document.createElement('div');
+      const { result } = renderHook(() => useOlMap({ target: dummyElement }), { wrapper: Wrapper });
+      const setLayersSpy = jest.spyOn(result.current.mapInstance as Map, 'setLayers');
+      const CALLED_ONCE = 1;
+
+      store.dispatch(
+        setMapData({
+          size: {
+            maxZoom: 10,
+            minZoom: 2,
+            tileSize: 256,
+            width: 1000,
+            height: 1000,
+          },
+        }),
+      );
+
+      await waitFor(() => expect(setLayersSpy).toBeCalledTimes(CALLED_ONCE));
+    });
+  });
+});
diff --git a/src/components/Map/MapViewer/utils/useOlMap.ts b/src/components/Map/MapViewer/utils/useOlMap.ts
index f9247726..b7525d19 100644
--- a/src/components/Map/MapViewer/utils/useOlMap.ts
+++ b/src/components/Map/MapViewer/utils/useOlMap.ts
@@ -1,18 +1,21 @@
 import Map from 'ol/Map';
-import { MutableRefObject, useEffect, useRef, useState } from 'react';
+import React, { MutableRefObject, useEffect, useState } from 'react';
 import { MapInstance } from '../MapViewer.types';
 import { useOlMapConfig } from './useOlMapConfig';
 import { useOlMapInit } from './useOlMapInit';
 
+interface UseOlMapInput {
+  target?: HTMLElement;
+}
 interface UseOlMapOutput {
   mapRef: MutableRefObject<null | HTMLDivElement>;
   mapInstance: MapInstance;
 }
 
-type UseOlMap = () => UseOlMapOutput;
+type UseOlMap = (input?: UseOlMapInput) => UseOlMapOutput;
 
-export const useOlMap: UseOlMap = () => {
-  const mapRef = useRef<null | HTMLDivElement>(null);
+export const useOlMap: UseOlMap = ({ target } = {}) => {
+  const mapRef = React.useRef<null | HTMLDivElement>(null);
   const [mapInstance, setMapInstance] = useState<MapInstance>(undefined);
   const mapConfig = useOlMapConfig();
   useOlMapInit();
@@ -24,11 +27,11 @@ export const useOlMap: UseOlMap = () => {
     }
 
     const map = new Map({
-      target: mapRef.current,
+      target: target || mapRef.current,
     });
 
-    setMapInstance(map);
-  }, []);
+    setMapInstance(currentMap => currentMap || map);
+  }, [target]);
 
   useEffect(() => {
     if (!mapInstance) {
-- 
GitLab