@defra/interactive-map 0.0.14-alpha → 0.0.16-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/css/docusaurus.css +104 -0
- package/assets/images/favicon.svg +1 -0
- package/assets/images/hero.png +0 -0
- package/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api/slot-map.svg +1 -0
- package/docs/api/slots.md +89 -6
- package/docs/api.md +1 -1
- package/docs/architecture.md +3 -1
- package/docs/{demo.mdx → examples.mdx} +1 -1
- package/docs/getting-started.md +1 -3
- package/docs/index.mdx +42 -0
- package/docs/plugins/interact.md +176 -55
- package/docs/plugins/map-styles.md +64 -7
- package/docs/plugins/search.md +207 -63
- package/docs/plugins.md +7 -15
- package/docusaurus.config.cjs +34 -34
- package/jest.setup.js +1 -1
- package/package.json +5 -4
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/src/DatasetsInit.jsx +1 -1
- package/plugins/beta/datasets/src/api/addDataset.js +1 -1
- package/plugins/beta/datasets/src/api/hideDataset.js +1 -1
- package/plugins/beta/datasets/src/api/hideFeatures.js +1 -1
- package/plugins/beta/datasets/src/api/removeDataset.js +1 -1
- package/plugins/beta/datasets/src/api/showDataset.js +1 -1
- package/plugins/beta/datasets/src/api/showFeatures.js +1 -1
- package/plugins/beta/datasets/src/datasets.js +4 -4
- package/plugins/beta/datasets/src/defaults.js +1 -1
- package/plugins/beta/datasets/src/fetch/createDynamicSource.js +5 -5
- package/plugins/beta/datasets/src/handleSetMapStyle.js +1 -1
- package/plugins/beta/datasets/src/manifest.js +7 -7
- package/plugins/beta/datasets/src/mapLayers.js +2 -3
- package/plugins/beta/datasets/src/panels/Key.jsx +31 -29
- package/plugins/beta/datasets/src/panels/Layers.jsx +8 -9
- package/plugins/beta/datasets/src/utils/bbox.js +4 -4
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
- package/plugins/beta/draw-es/src/DrawInit.jsx +16 -16
- package/plugins/beta/draw-es/src/api/addFeature.js +3 -3
- package/plugins/beta/draw-es/src/api/deleteFeature.js +3 -3
- package/plugins/beta/draw-es/src/api/editFeature.js +3 -3
- package/plugins/beta/draw-es/src/api/newPolygon.js +3 -3
- package/plugins/beta/draw-es/src/events.js +52 -20
- package/plugins/beta/draw-es/src/events.test.js +301 -0
- package/plugins/beta/draw-es/src/graphic.js +1 -1
- package/plugins/beta/draw-es/src/manifest.js +4 -4
- package/plugins/beta/draw-es/src/reducer.js +1 -1
- package/plugins/beta/draw-es/src/sketchViewModel.js +1 -1
- package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/src/DrawInit.jsx +49 -52
- package/plugins/beta/draw-ml/src/api/deleteFeature.js +1 -1
- package/plugins/beta/draw-ml/src/api/editFeature.js +8 -5
- package/plugins/beta/draw-ml/src/api/newLine.js +0 -1
- package/plugins/beta/draw-ml/src/api/newPolygon.js +0 -1
- package/plugins/beta/draw-ml/src/api/split.js +4 -4
- package/plugins/beta/draw-ml/src/defaults.js +1 -1
- package/plugins/beta/draw-ml/src/events.js +8 -6
- package/plugins/beta/draw-ml/src/manifest.js +15 -15
- package/plugins/beta/draw-ml/src/mapboxDraw.js +1 -1
- package/plugins/beta/draw-ml/src/mapboxSnap.js +17 -18
- package/plugins/beta/draw-ml/src/modes/createDrawMode.js +31 -31
- package/plugins/beta/draw-ml/src/modes/disabledMode.js +1 -1
- package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +11 -11
- package/plugins/beta/draw-ml/src/modes/editVertex/undoHandlers.js +7 -7
- package/plugins/beta/draw-ml/src/modes/editVertex/vertexOperations.js +8 -8
- package/plugins/beta/draw-ml/src/modes/editVertex/vertexQueries.js +7 -7
- package/plugins/beta/draw-ml/src/modes/editVertexMode.js +32 -24
- package/plugins/beta/draw-ml/src/reducer.js +1 -1
- package/plugins/beta/draw-ml/src/undoStack.js +4 -4
- package/plugins/beta/draw-ml/src/utils/snapHelpers.js +12 -12
- package/plugins/beta/draw-ml/src/utils/spatial.js +11 -11
- package/plugins/beta/frame/src/Frame.jsx +4 -4
- package/plugins/beta/frame/src/FrameInit.jsx +4 -4
- package/plugins/beta/frame/src/api/addFrame.js +1 -1
- package/plugins/beta/frame/src/api/editFeature.js +1 -1
- package/plugins/beta/frame/src/config.js +1 -1
- package/plugins/beta/frame/src/manifest.js +3 -3
- package/plugins/beta/frame/src/reducer.js +1 -1
- package/plugins/beta/frame/src/utils.js +1 -1
- package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/src/MapStyles.jsx +18 -18
- package/plugins/beta/map-styles/src/manifest.js +2 -2
- package/plugins/beta/scale-bar/src/ScaleBar.jsx +5 -5
- package/plugins/beta/use-location/src/UseLocation.jsx +1 -1
- package/plugins/beta/use-location/src/defaults.js +1 -1
- package/plugins/beta/use-location/src/events.js +3 -3
- package/plugins/interact/src/InteractInit.jsx +1 -2
- package/plugins/interact/src/api/enable.js +8 -5
- package/plugins/interact/src/api/enable.test.js +2 -2
- package/plugins/interact/src/api/selectFeature.js +4 -4
- package/plugins/interact/src/api/unselectFeature.js +5 -5
- package/plugins/interact/src/defaults.js +0 -1
- package/plugins/interact/src/events.test.js +15 -15
- package/plugins/interact/src/hooks/useHighlightSync.js +1 -1
- package/plugins/interact/src/hooks/useInteractionHandlers.js +2 -2
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +5 -5
- package/plugins/interact/src/manifest.js +2 -2
- package/plugins/interact/src/manifest.test.js +3 -4
- package/plugins/interact/src/reducer.js +3 -3
- package/plugins/interact/src/reducer.test.js +0 -1
- package/plugins/interact/src/utils/spatial.js +10 -10
- package/plugins/interact/src/utils/spatial.test.js +14 -14
- package/plugins/search/dist/css/index.css +1 -1
- package/plugins/search/dist/esm/im-search-plugin.js +1 -1
- package/plugins/search/dist/esm/index.js +1 -1
- package/plugins/search/dist/umd/im-search-plugin.js +1 -1
- package/plugins/search/dist/umd/index.js +1 -1
- package/plugins/search/src/Search.jsx +7 -6
- package/plugins/search/src/Search.test.jsx +23 -23
- package/plugins/search/src/components/CloseButton/CloseButton.jsx +15 -15
- package/plugins/search/src/components/CloseButton/CloseButton.test.jsx +2 -2
- package/plugins/search/src/components/Form/Form.jsx +14 -14
- package/plugins/search/src/components/Form/Form.test.jsx +11 -11
- package/plugins/search/src/components/OpenButton/OpenButton.jsx +16 -15
- package/plugins/search/src/components/OpenButton/OpenButton.test.jsx +6 -2
- package/plugins/search/src/components/SubmitButton/SubmitButton.jsx +15 -15
- package/plugins/search/src/components/Suggestions/Suggestions.jsx +6 -6
- package/plugins/search/src/components/Suggestions/Suggestions.test.jsx +4 -4
- package/plugins/search/src/datasets.js +12 -13
- package/plugins/search/src/datasets.test.js +1 -1
- package/plugins/search/src/defaults.js +1 -1
- package/plugins/search/src/events/fetchSuggestions.js +4 -4
- package/plugins/search/src/events/fetchSuggestions.test.js +5 -5
- package/plugins/search/src/events/formHandlers.js +3 -3
- package/plugins/search/src/events/formHandlers.test.js +1 -1
- package/plugins/search/src/events/index.js +2 -2
- package/plugins/search/src/events/index.test.js +2 -2
- package/plugins/search/src/events/inputHandlers.js +4 -4
- package/plugins/search/src/events/inputHandlers.test.js +1 -1
- package/plugins/search/src/events/suggestionHandlers.js +2 -2
- package/plugins/search/src/events/suggestionHandlers.test.js +1 -1
- package/plugins/search/src/index.js +2 -1
- package/plugins/search/src/index.test.js +3 -3
- package/plugins/search/src/manifest.js +6 -4
- package/plugins/search/src/reducer.js +1 -2
- package/plugins/search/src/reducer.test.js +2 -2
- package/plugins/search/src/search.scss +18 -6
- package/plugins/search/src/utils/parseOsNamesResults.js +1 -2
- package/plugins/search/src/utils/parseOsNamesResults.test.js +2 -2
- package/plugins/search/src/utils/updateMap.js +1 -1
- package/plugins/search/src/utils/updateMap.test.js +5 -5
- package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
- package/providers/beta/esri/src/esriProvider.js +5 -5
- package/providers/beta/esri/src/utils/coords.js +1 -1
- package/providers/beta/esri/src/utils/esriFixes.js +1 -1
- package/providers/beta/esri/src/utils/query.js +4 -4
- package/providers/beta/esri/src/utils/spatial.js +1 -2
- package/providers/beta/esri/src/utils/spatial.test.js +4 -1
- package/providers/beta/open-names/src/utils/mapToLocationModel.test.js +1 -1
- package/providers/maplibre/src/appEvents.test.js +1 -1
- package/providers/maplibre/src/index.js +1 -1
- package/providers/maplibre/src/index.test.js +3 -5
- package/providers/maplibre/src/mapEvents.test.js +15 -5
- package/providers/maplibre/src/maplibreProvider.test.js +6 -2
- package/providers/maplibre/src/utils/calculateLinearTextSize.js +4 -4
- package/providers/maplibre/src/utils/calculateLinearTextSize.test.js +3 -3
- package/providers/maplibre/src/utils/detectWebgl.test.js +1 -1
- package/providers/maplibre/src/utils/highlightFeatures.js +2 -2
- package/providers/maplibre/src/utils/highlightFeatures.test.js +12 -6
- package/providers/maplibre/src/utils/labels.js +19 -20
- package/providers/maplibre/src/utils/labels.test.js +15 -13
- package/providers/maplibre/src/utils/maplibreFixes.test.js +1 -1
- package/providers/maplibre/src/utils/queryFeatures.js +6 -6
- package/providers/maplibre/src/utils/queryFeatures.test.js +13 -13
- package/providers/maplibre/src/utils/spatial.js +0 -1
- package/providers/maplibre/src/utils/spatial.test.js +26 -27
- package/src/App/components/Panel/Panel.module.scss +1 -0
- package/src/App/hooks/useLayoutMeasurements.js +1 -10
- package/src/App/hooks/useLayoutMeasurements.test.js +2 -5
- package/src/App/hooks/useVisibleGeometry.js +7 -13
- package/src/App/hooks/useVisibleGeometry.test.js +72 -47
- package/src/App/layout/Layout.jsx +0 -3
- package/src/App/layout/Layout.test.jsx +0 -1
- package/src/App/layout/layout.module.scss +11 -77
- package/src/App/registry/pluginRegistry.js +17 -0
- package/src/App/registry/pluginRegistry.test.js +33 -0
- package/src/App/renderer/HtmlElementHost.jsx +0 -1
- package/src/App/renderer/HtmlElementHost.test.jsx +20 -11
- package/src/App/renderer/mapButtons.js +3 -2
- package/src/App/renderer/mapPanels.test.js +3 -3
- package/src/App/renderer/slotHelpers.js +2 -2
- package/src/App/renderer/slotHelpers.test.js +3 -3
- package/src/App/renderer/slots.js +0 -3
- package/src/App/store/AppProvider.jsx +0 -1
- package/src/App/store/appDispatchMiddleware.js +33 -1
- package/src/App/store/appDispatchMiddleware.test.js +250 -222
- package/src/config/appConfig.js +4 -4
- package/src/utils/getSafeZoneInset.js +139 -42
- package/src/utils/getSafeZoneInset.test.js +298 -122
- package/src/utils/logger.js +6 -0
- package/src/utils/logger.test.js +32 -0
- package/webpack.dev.mjs +22 -18
- package/docs/govuk-prototype.md +0 -23
- package/docs/index.md +0 -19
|
@@ -30,7 +30,7 @@ const isPointInPolygon = (point, ring) => {
|
|
|
30
30
|
const intersectX = ((xj - xi) * (py - yi)) / (yj - yi) + xi
|
|
31
31
|
|
|
32
32
|
if (px < intersectX) {
|
|
33
|
-
inside = !inside
|
|
33
|
+
inside = !inside
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
return inside
|
|
@@ -43,7 +43,7 @@ const getMinDistToGeometry = (map, point, geometry) => {
|
|
|
43
43
|
const { coordinates: coords, type } = geometry
|
|
44
44
|
let minSqDist = Infinity
|
|
45
45
|
const getScreenPt = (lngLat) => map.project(lngLat)
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
const processLine = (lineCoords) => {
|
|
48
48
|
for (let i = 0; i < lineCoords.length - 1; i++) {
|
|
49
49
|
const d2 = distToSegmentSquared(point, getScreenPt(lineCoords[i]), getScreenPt(lineCoords[i + 1]))
|
|
@@ -52,7 +52,7 @@ const getMinDistToGeometry = (map, point, geometry) => {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
if (type === 'Point') {
|
|
57
57
|
const p = getScreenPt(coords)
|
|
58
58
|
minSqDist = (point.x - p.x) ** 2 + (point.y - p.y) ** 2
|
|
@@ -117,7 +117,7 @@ export const queryFeatures = (map, point, options = {}) => {
|
|
|
117
117
|
let score = 0
|
|
118
118
|
const type = f.geometry.type
|
|
119
119
|
const pixelDistSq = getMinDistToGeometry(map, point, f.geometry)
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
// PRIORITY 1: LAYER ORDER
|
|
122
122
|
const layerRank = layerStack.indexOf(f.layer.id)
|
|
123
123
|
score += (layerRank * 1000000)
|
|
@@ -126,7 +126,7 @@ export const queryFeatures = (map, point, options = {}) => {
|
|
|
126
126
|
if (type.includes('Polygon')) {
|
|
127
127
|
const polys = type === 'Polygon' ? [f.geometry.coordinates] : f.geometry.coordinates
|
|
128
128
|
const isInside = polys.some((ring) => isPointInPolygon(clickPt, ring[0]))
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
if (isInside === true) {
|
|
131
131
|
// Massive boost for polygons if we are actually inside them
|
|
132
132
|
score -= 500000 // NOSONAR - tolerance used only here
|
|
@@ -143,4 +143,4 @@ export const queryFeatures = (map, point, options = {}) => {
|
|
|
143
143
|
})
|
|
144
144
|
.sort((a, b) => a.score - b.score)
|
|
145
145
|
.map(({ f }) => f)
|
|
146
|
-
}
|
|
146
|
+
}
|
|
@@ -15,12 +15,12 @@ describe('queryFeatures coverage', () => {
|
|
|
15
15
|
const cases = [
|
|
16
16
|
{ type: 'Point', coords: [0, 0], p: { x: 3, y: 4 } },
|
|
17
17
|
{ type: 'LineString', coords: [[0, 0], [10, 0]], p: { x: 5, y: 5 } }, // t=0.5
|
|
18
|
-
{ type: 'LineString', coords: [[0, 0], [0, 0]], p: { x: 1, y: 1 } },
|
|
18
|
+
{ type: 'LineString', coords: [[0, 0], [0, 0]], p: { x: 1, y: 1 } }, // l2=0
|
|
19
19
|
{ type: 'LineString', coords: [[0, 0], [10, 0]], p: { x: -5, y: 0 } }, // t<0
|
|
20
20
|
{ type: 'MultiPoint', coords: [[0, 0], [10, 10]], p: { x: 1, y: 0 } },
|
|
21
21
|
{ type: 'MultiLineString', coords: [[[0, 0], [10, 0]]], p: { x: 5, y: 1 } },
|
|
22
22
|
{ type: 'Polygon', coords: [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]], p: { x: 5, y: 5 } }, // Inside
|
|
23
|
-
{ type: 'MultiPolygon', coords: [[[[0, 0], [10, 0], [10, 10], [0, 0]]]], p: { x: 20, y: 20 } },
|
|
23
|
+
{ type: 'MultiPolygon', coords: [[[[0, 0], [10, 0], [10, 10], [0, 0]]]], p: { x: 20, y: 20 } }, // Outside
|
|
24
24
|
{ type: 'Unknown', coords: [], p: { x: 0, y: 0 } }
|
|
25
25
|
]
|
|
26
26
|
|
|
@@ -31,21 +31,21 @@ describe('queryFeatures coverage', () => {
|
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
// 3. Hits Line 144 (.sort) and property-based ID fallback
|
|
34
|
-
const f1 = {
|
|
35
|
-
properties: { key: 'a' },
|
|
36
|
-
layer: { id: 'layer-A' },
|
|
37
|
-
geometry: { type: 'Point', coordinates: [10, 10] }
|
|
34
|
+
const f1 = {
|
|
35
|
+
properties: { key: 'a' },
|
|
36
|
+
layer: { id: 'layer-A' },
|
|
37
|
+
geometry: { type: 'Point', coordinates: [10, 10] }
|
|
38
38
|
}
|
|
39
|
-
const f2 = {
|
|
40
|
-
id: 'b',
|
|
41
|
-
layer: { id: 'layer-B' },
|
|
42
|
-
geometry: { type: 'Point', coordinates: [0, 0] }
|
|
39
|
+
const f2 = {
|
|
40
|
+
id: 'b',
|
|
41
|
+
layer: { id: 'layer-B' },
|
|
42
|
+
geometry: { type: 'Point', coordinates: [0, 0] }
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
// map.queryRenderedFeatures returns multiple items to trigger .sort()
|
|
46
46
|
const sortMap = { ...mockMap, queryRenderedFeatures: () => [f1, f2] }
|
|
47
47
|
const result = queryFeatures(sortMap, { x: 0, y: 0 })
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
expect(result.length).toBe(2)
|
|
50
50
|
expect(result[0].layer.id).toBe('layer-A') // Sorted by layerStack index
|
|
51
51
|
|
|
@@ -57,4 +57,4 @@ describe('queryFeatures coverage', () => {
|
|
|
57
57
|
const rayMap = { ...mockMap, queryRenderedFeatures: () => [polyFeat] }
|
|
58
58
|
expect(queryFeatures(rayMap, { x: -1, y: 5 }).length).toBe(1)
|
|
59
59
|
})
|
|
60
|
-
})
|
|
60
|
+
})
|
|
@@ -10,7 +10,6 @@ jest.mock('geodesy/latlon-spherical.js', () =>
|
|
|
10
10
|
jest.mock('@turf/bbox', () => jest.fn(() => [-1, 50, 1, 52]))
|
|
11
11
|
|
|
12
12
|
describe('spatial utils', () => {
|
|
13
|
-
|
|
14
13
|
test('formatDimension hits all branches', () => {
|
|
15
14
|
// < 0.5 miles
|
|
16
15
|
expect(spatial.formatDimension(500)).toMatch(/m$/)
|
|
@@ -43,55 +42,55 @@ describe('spatial utils', () => {
|
|
|
43
42
|
})
|
|
44
43
|
|
|
45
44
|
test('north/south/east/west moves', () => {
|
|
46
|
-
expect(spatial.getCardinalMove([0,0],[0,0.5])).toMatch(/north/)
|
|
47
|
-
expect(spatial.getCardinalMove([0,0],[0
|
|
48
|
-
expect(spatial.getCardinalMove([0,0],[0.5,0])).toMatch(/east/)
|
|
49
|
-
expect(spatial.getCardinalMove([0,0],[-0.5,0])).toMatch(/west/)
|
|
50
|
-
expect(spatial.getCardinalMove([0,0],[0.5,0.5])).toMatch(/north.*east|east.*north/)
|
|
51
|
-
expect(spatial.getCardinalMove([0,0],[0.00001,0.00001])).toBe('')
|
|
45
|
+
expect(spatial.getCardinalMove([0, 0], [0, 0.5])).toMatch(/north/)
|
|
46
|
+
expect(spatial.getCardinalMove([0, 0], [0, -0.5])).toMatch(/south/)
|
|
47
|
+
expect(spatial.getCardinalMove([0, 0], [0.5, 0])).toMatch(/east/)
|
|
48
|
+
expect(spatial.getCardinalMove([0, 0], [-0.5, 0])).toMatch(/west/)
|
|
49
|
+
expect(spatial.getCardinalMove([0, 0], [0.5, 0.5])).toMatch(/north.*east|east.*north/)
|
|
50
|
+
expect(spatial.getCardinalMove([0, 0], [0.00001, 0.00001])).toBe('')
|
|
52
51
|
})
|
|
53
52
|
|
|
54
53
|
test('spatialNavigate all directions and fallback', () => {
|
|
55
|
-
const pixels = [[0,0],[0
|
|
56
|
-
expect(spatial.spatialNavigate('ArrowUp',[0,0],pixels)).toBe(1)
|
|
57
|
-
expect(spatial.spatialNavigate('ArrowDown',[0,0],pixels)).toBe(3)
|
|
58
|
-
expect(spatial.spatialNavigate('ArrowLeft',[0,0],pixels)).toBe(4)
|
|
59
|
-
expect(spatial.spatialNavigate('ArrowRight',[0,0],pixels)).toBe(2)
|
|
60
|
-
expect(spatial.spatialNavigate('InvalidDir',[0,0],pixels)).toBe(0)
|
|
54
|
+
const pixels = [[0, 0], [0, -1], [1, 0], [0, 1], [-1, 0]]
|
|
55
|
+
expect(spatial.spatialNavigate('ArrowUp', [0, 0], pixels)).toBe(1)
|
|
56
|
+
expect(spatial.spatialNavigate('ArrowDown', [0, 0], pixels)).toBe(3)
|
|
57
|
+
expect(spatial.spatialNavigate('ArrowLeft', [0, 0], pixels)).toBe(4)
|
|
58
|
+
expect(spatial.spatialNavigate('ArrowRight', [0, 0], pixels)).toBe(2)
|
|
59
|
+
expect(spatial.spatialNavigate('InvalidDir', [0, 0], pixels)).toBe(0)
|
|
61
60
|
})
|
|
62
61
|
|
|
63
62
|
test('spatialNavigate finds closer candidates (hits dist < minDist)', () => {
|
|
64
|
-
const start = [0,0]
|
|
65
|
-
const pixels = [[0,0],[10,0],[2,0]]
|
|
63
|
+
const start = [0, 0]
|
|
64
|
+
const pixels = [[0, 0], [10, 0], [2, 0]]
|
|
66
65
|
expect(spatial.spatialNavigate('ArrowRight', start, pixels)).toBe(2)
|
|
67
66
|
})
|
|
68
67
|
|
|
69
68
|
test('spatialNavigate skips farther candidate (dist >= minDist false branch)', () => {
|
|
70
69
|
// Closer candidate first → second candidate fails dist < minDist
|
|
71
|
-
const pixels = [[0,0],[2,0],[10,0]]
|
|
72
|
-
expect(spatial.spatialNavigate('ArrowRight', [0,0], pixels)).toBe(1)
|
|
70
|
+
const pixels = [[0, 0], [2, 0], [10, 0]]
|
|
71
|
+
expect(spatial.spatialNavigate('ArrowRight', [0, 0], pixels)).toBe(1)
|
|
73
72
|
})
|
|
74
73
|
|
|
75
74
|
test('spatialNavigate diagonal with dx>dy', () => {
|
|
76
|
-
const start = [0,0]
|
|
77
|
-
const pixels = [[0,0],[3,1],[1,0]] // dx>dy
|
|
75
|
+
const start = [0, 0]
|
|
76
|
+
const pixels = [[0, 0], [3, 1], [1, 0]] // dx>dy
|
|
78
77
|
expect(spatial.spatialNavigate('ArrowRight', start, pixels)).toBe(2)
|
|
79
78
|
})
|
|
80
79
|
|
|
81
80
|
test('getResolution returns positive value', () => {
|
|
82
|
-
expect(spatial.getResolution({lat:0},1)).toBeGreaterThan(0)
|
|
81
|
+
expect(spatial.getResolution({ lat: 0 }, 1)).toBeGreaterThan(0)
|
|
83
82
|
})
|
|
84
83
|
|
|
85
84
|
test('getPaddedBounds returns bounds', () => {
|
|
86
85
|
const map = {
|
|
87
|
-
getContainer: () => ({ getBoundingClientRect: () => ({ width:100,height:200 }) }),
|
|
88
|
-
getPadding: () => ({ top:1,right:2,bottom:3,left:4 }),
|
|
89
|
-
unproject: p => ({ x:p[0], y:p[1] })
|
|
86
|
+
getContainer: () => ({ getBoundingClientRect: () => ({ width: 100, height: 200 }) }),
|
|
87
|
+
getPadding: () => ({ top: 1, right: 2, bottom: 3, left: 4 }),
|
|
88
|
+
unproject: p => ({ x: p[0], y: p[1] })
|
|
90
89
|
}
|
|
91
|
-
const LngLatBounds = function(sw,ne){
|
|
92
|
-
return {sw,ne}
|
|
90
|
+
const LngLatBounds = function (sw, ne) {
|
|
91
|
+
return { sw, ne }
|
|
93
92
|
}
|
|
94
|
-
const bounds = spatial.getPaddedBounds(LngLatBounds,map)
|
|
93
|
+
const bounds = spatial.getPaddedBounds(LngLatBounds, map)
|
|
95
94
|
expect(bounds.sw).toBeDefined()
|
|
96
95
|
expect(bounds.ne).toBeDefined()
|
|
97
96
|
})
|
|
@@ -140,4 +139,4 @@ describe('spatial utils', () => {
|
|
|
140
139
|
expect(map.project).toHaveBeenCalledTimes(4)
|
|
141
140
|
})
|
|
142
141
|
})
|
|
143
|
-
})
|
|
142
|
+
})
|
|
@@ -12,9 +12,6 @@ const topColWidth = (left, right) =>
|
|
|
12
12
|
const subSlotMaxHeight = (columnHeight, siblingButtons, gap) =>
|
|
13
13
|
columnHeight - (siblingButtons ? siblingButtons + gap : 0)
|
|
14
14
|
|
|
15
|
-
const calcOffsetLeft = (bottomOffsetTop, gap, insetBottom, inset) =>
|
|
16
|
-
bottomOffsetTop - gap > insetBottom ? 0 : inset.offsetLeft + inset.offsetWidth
|
|
17
|
-
|
|
18
15
|
export function useLayoutMeasurements () {
|
|
19
16
|
const { dispatch, breakpoint, layoutRefs } = useApp()
|
|
20
17
|
const { mapSize, isMapReady } = useMap()
|
|
@@ -26,7 +23,6 @@ export function useLayoutMeasurements () {
|
|
|
26
23
|
topRef,
|
|
27
24
|
topLeftColRef,
|
|
28
25
|
topRightColRef,
|
|
29
|
-
insetRef,
|
|
30
26
|
footerRef,
|
|
31
27
|
actionsRef,
|
|
32
28
|
leftTopRef,
|
|
@@ -44,11 +40,9 @@ export function useLayoutMeasurements () {
|
|
|
44
40
|
const top = topRef.current
|
|
45
41
|
const topLeftCol = topLeftColRef.current
|
|
46
42
|
const topRightCol = topRightColRef.current
|
|
47
|
-
const inset = insetRef.current
|
|
48
43
|
const bottom = footerRef.current
|
|
49
|
-
const actions = actionsRef.current
|
|
50
44
|
|
|
51
|
-
if ([main, top,
|
|
45
|
+
if ([main, top, bottom].some(r => !r)) {
|
|
52
46
|
return
|
|
53
47
|
}
|
|
54
48
|
|
|
@@ -77,9 +71,6 @@ export function useLayoutMeasurements () {
|
|
|
77
71
|
appContainer.style.setProperty('--left-bottom-panel-max-height', `${subSlotMaxHeight(leftColumnHeight, buttonHeight(leftTopRef), dividerGap)}px`)
|
|
78
72
|
appContainer.style.setProperty('--right-top-panel-max-height', `${subSlotMaxHeight(rightColumnHeight, buttonHeight(rightBottomRef), dividerGap)}px`)
|
|
79
73
|
appContainer.style.setProperty('--right-bottom-panel-max-height', `${subSlotMaxHeight(rightColumnHeight, buttonHeight(rightTopRef), dividerGap)}px`)
|
|
80
|
-
|
|
81
|
-
// === Bottom left offset ===
|
|
82
|
-
appContainer.style.setProperty('--offset-left', `${calcOffsetLeft(Math.min(bottom.offsetTop, actions.offsetTop), dividerGap, inset.offsetHeight + leftOffsetTop, inset)}px`)
|
|
83
74
|
}
|
|
84
75
|
|
|
85
76
|
// --------------------------------
|
|
@@ -24,7 +24,6 @@ const refs = (o = {}) => ({
|
|
|
24
24
|
topRef: { current: o.top === null ? null : el({ offsetTop: 10, ...o.top }) },
|
|
25
25
|
topLeftColRef: { current: el({ offsetHeight: 50, offsetWidth: 200, ...o.topLeftCol }) },
|
|
26
26
|
topRightColRef: { current: el({ offsetHeight: 40, offsetWidth: 180, ...o.topRightCol }) },
|
|
27
|
-
insetRef: { current: o.inset === null ? null : el({ offsetHeight: 100, offsetLeft: 20, offsetWidth: 300, ...o.inset }) },
|
|
28
27
|
footerRef: { current: o.footer === null ? null : el({ offsetTop: 400, ...o.footer }) },
|
|
29
28
|
actionsRef: { current: el({ offsetTop: 450, ...o.actions }) },
|
|
30
29
|
leftTopRef: { current: el({ offsetHeight: 0, ...o.leftTop }) },
|
|
@@ -57,7 +56,7 @@ describe('useLayoutMeasurements', () => {
|
|
|
57
56
|
})
|
|
58
57
|
|
|
59
58
|
test('early return when required refs are null', () => {
|
|
60
|
-
const { layoutRefs } = setup({ refs: { main: null, top: null,
|
|
59
|
+
const { layoutRefs } = setup({ refs: { main: null, top: null, footer: null } })
|
|
61
60
|
renderHook(() => useLayoutMeasurements())
|
|
62
61
|
expect(layoutRefs.appContainerRef.current.style.setProperty).not.toHaveBeenCalled()
|
|
63
62
|
})
|
|
@@ -66,13 +65,11 @@ describe('useLayoutMeasurements', () => {
|
|
|
66
65
|
const { layoutRefs } = setup()
|
|
67
66
|
renderHook(() => useLayoutMeasurements())
|
|
68
67
|
const spy = layoutRefs.appContainerRef.current.style.setProperty
|
|
69
|
-
;['--
|
|
68
|
+
;['--right-offset-top', '--right-offset-bottom', '--top-col-width']
|
|
70
69
|
.forEach(prop => expect(spy).toHaveBeenCalledWith(prop, expect.any(String)))
|
|
71
70
|
})
|
|
72
71
|
|
|
73
72
|
test.each([
|
|
74
|
-
['offset-left with overlap', { inset: { offsetHeight: 200, offsetLeft: 30, offsetWidth: 150 }, footer: { offsetTop: 100 }, actions: { offsetTop: 120 }, topLeftCol: { offsetHeight: 50 }, top: { offsetTop: 10 } }, '180px'],
|
|
75
|
-
['offset-left without overlap', { inset: { offsetHeight: 50, offsetLeft: 30, offsetWidth: 150 }, footer: { offsetTop: 200 }, actions: { offsetTop: 220 }, topLeftCol: { offsetHeight: 50 }, top: { offsetTop: 10 } }, '0px'],
|
|
76
73
|
['right-offset-top', { topRightCol: { offsetHeight: 80 }, top: { offsetTop: 15 } }, '95px'],
|
|
77
74
|
['right-offset-bottom', { main: { offsetHeight: 600 }, footer: { offsetTop: 500 } }, '108px'],
|
|
78
75
|
// leftColumnHeight = 400 - (50+10) - 8 = 332; rightColumnHeight = 400 - (40+10) - 8 = 342
|
|
@@ -31,14 +31,8 @@ export const getPointCoordinates = (geojson) => {
|
|
|
31
31
|
return null
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const SLOT_REFS = {
|
|
35
|
-
inset: 'insetRef',
|
|
36
|
-
bottom: 'bottomRef',
|
|
37
|
-
side: 'sideRef'
|
|
38
|
-
}
|
|
39
|
-
|
|
40
34
|
export const useVisibleGeometry = () => {
|
|
41
|
-
const { mapProvider, eventBus } = useConfig()
|
|
35
|
+
const { id, mapProvider, eventBus } = useConfig()
|
|
42
36
|
const { layoutRefs, panelConfig, panelRegistry, breakpoint } = useApp()
|
|
43
37
|
|
|
44
38
|
const latestRef = useRef({ layoutRefs, panelConfig, panelRegistry, breakpoint })
|
|
@@ -49,14 +43,13 @@ export const useVisibleGeometry = () => {
|
|
|
49
43
|
return undefined
|
|
50
44
|
}
|
|
51
45
|
|
|
52
|
-
const handlePanelOpened = ({ panelId,
|
|
53
|
-
const { panelConfig: config, panelRegistry: registry
|
|
46
|
+
const handlePanelOpened = ({ panelId, visibleGeometry: eventVisibleGeometry }) => {
|
|
47
|
+
const { panelConfig: config, panelRegistry: registry } = latestRef.current
|
|
54
48
|
const resolvedConfig = config?.[panelId] ? config : (registry?.getPanelConfig() ?? config)
|
|
55
49
|
const visibleGeometry = eventVisibleGeometry ?? resolvedConfig?.[panelId]?.visibleGeometry
|
|
56
|
-
const
|
|
57
|
-
const slotRef = refs[SLOT_REFS[slot]]
|
|
50
|
+
const panel = layoutRefs.appContainerRef.current?.querySelector(`#${id}-panel-${panelId}`)
|
|
58
51
|
|
|
59
|
-
if (!visibleGeometry
|
|
52
|
+
if (!visibleGeometry) {
|
|
60
53
|
return
|
|
61
54
|
}
|
|
62
55
|
if (typeof mapProvider.isGeometryObscured !== 'function') {
|
|
@@ -64,7 +57,8 @@ export const useVisibleGeometry = () => {
|
|
|
64
57
|
}
|
|
65
58
|
|
|
66
59
|
const waitForPanel = () => {
|
|
67
|
-
|
|
60
|
+
if (!panel) { return }
|
|
61
|
+
const panelRect = panel.getBoundingClientRect()
|
|
68
62
|
|
|
69
63
|
if (!panelRect || panelRect.width === 0 || panelRect.height === 0) {
|
|
70
64
|
// Not ready yet, check on the next animation frame
|
|
@@ -10,8 +10,16 @@ const pointFeature = { type: 'Feature', geometry: { type: 'Point', coordinates:
|
|
|
10
10
|
const multiPointFeature = { type: 'Feature', geometry: { type: 'MultiPoint', coordinates: [[1, 51], [2, 52]] }, properties: {} }
|
|
11
11
|
const polygonFeature = { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] }, properties: {} }
|
|
12
12
|
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const APP_ID = 'test'
|
|
14
|
+
const panelRect = { left: 600, top: 0, right: 1000, bottom: 800, width: 400, height: 800 }
|
|
15
|
+
|
|
16
|
+
// Creates a panel DOM element with id matching what useVisibleGeometry queries.
|
|
17
|
+
const makePanelEl = (panelId, rect = panelRect) => {
|
|
18
|
+
const el = document.createElement('div')
|
|
19
|
+
el.id = `${APP_ID}-panel-${panelId}`
|
|
20
|
+
el.getBoundingClientRect = jest.fn(() => rect)
|
|
21
|
+
return el
|
|
22
|
+
}
|
|
15
23
|
|
|
16
24
|
const setup = (overrides = {}) => {
|
|
17
25
|
const capturedHandlers = {}
|
|
@@ -27,19 +35,18 @@ const setup = (overrides = {}) => {
|
|
|
27
35
|
...overrides.eventBus
|
|
28
36
|
}
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
38
|
+
// appContainerRef holds panel elements that the hook queries by id
|
|
39
|
+
const appContainer = document.createElement('div')
|
|
40
|
+
const myPanelEl = makePanelEl('myPanel')
|
|
41
|
+
appContainer.appendChild(myPanelEl)
|
|
34
42
|
|
|
35
43
|
const layoutRefs = {
|
|
36
44
|
mainRef: { current: document.createElement('div') },
|
|
37
|
-
|
|
38
|
-
bottomRef: { current: bottomEl },
|
|
45
|
+
appContainerRef: { current: appContainer },
|
|
39
46
|
...overrides.layoutRefs
|
|
40
47
|
}
|
|
41
48
|
const panelConfig = {
|
|
42
|
-
myPanel: { visibleGeometry: polygonFeature, desktop: { slot: '
|
|
49
|
+
myPanel: { visibleGeometry: polygonFeature, desktop: { slot: 'left-top' } },
|
|
43
50
|
emptyPanel: {},
|
|
44
51
|
...overrides.panelConfig
|
|
45
52
|
}
|
|
@@ -48,10 +55,10 @@ const setup = (overrides = {}) => {
|
|
|
48
55
|
...overrides.panelRegistry
|
|
49
56
|
}
|
|
50
57
|
|
|
51
|
-
useConfig.mockReturnValue({ mapProvider, eventBus, ...overrides.config })
|
|
58
|
+
useConfig.mockReturnValue({ id: APP_ID, mapProvider, eventBus, ...overrides.config })
|
|
52
59
|
useApp.mockReturnValue({ layoutRefs, panelConfig, panelRegistry, breakpoint: 'desktop', ...overrides.app })
|
|
53
60
|
|
|
54
|
-
return { mapProvider, eventBus, capturedHandlers, layoutRefs, panelConfig,
|
|
61
|
+
return { mapProvider, eventBus, capturedHandlers, layoutRefs, panelConfig, myPanelEl, appContainer }
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
describe('useVisibleGeometry', () => {
|
|
@@ -102,12 +109,14 @@ describe('useVisibleGeometry', () => {
|
|
|
102
109
|
expect(mapProvider.isGeometryObscured).not.toHaveBeenCalled()
|
|
103
110
|
})
|
|
104
111
|
|
|
105
|
-
test('does nothing when panel
|
|
112
|
+
test('does nothing when panel element is not in the DOM', () => {
|
|
113
|
+
// Panel has visibleGeometry and slot config but its DOM element is not mounted yet
|
|
106
114
|
const { mapProvider, capturedHandlers } = setup({
|
|
107
|
-
panelConfig: {
|
|
115
|
+
panelConfig: { noElPanel: { visibleGeometry: polygonFeature, desktop: { slot: 'left-top' } } }
|
|
108
116
|
})
|
|
109
117
|
renderHook(() => useVisibleGeometry())
|
|
110
|
-
capturedHandlers['app:panelopened']({ panelId: '
|
|
118
|
+
capturedHandlers['app:panelopened']({ panelId: 'noElPanel' })
|
|
119
|
+
jest.runAllTimers()
|
|
111
120
|
expect(mapProvider.isGeometryObscured).not.toHaveBeenCalled()
|
|
112
121
|
})
|
|
113
122
|
|
|
@@ -118,19 +127,20 @@ describe('useVisibleGeometry', () => {
|
|
|
118
127
|
expect(mapProvider.fitToBounds).not.toHaveBeenCalled()
|
|
119
128
|
})
|
|
120
129
|
|
|
121
|
-
test('does nothing when
|
|
122
|
-
const
|
|
123
|
-
|
|
130
|
+
test('does nothing when panel element has zero dimensions (panel not yet visible)', () => {
|
|
131
|
+
const zeroRect = { left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0 }
|
|
132
|
+
const appContainer = document.createElement('div')
|
|
133
|
+
appContainer.appendChild(makePanelEl('myPanel', zeroRect))
|
|
124
134
|
const { mapProvider, capturedHandlers } = setup({
|
|
125
135
|
layoutRefs: {
|
|
126
136
|
mainRef: { current: document.createElement('div') },
|
|
127
|
-
|
|
137
|
+
appContainerRef: { current: appContainer }
|
|
128
138
|
}
|
|
129
139
|
})
|
|
130
140
|
renderHook(() => useVisibleGeometry())
|
|
131
141
|
capturedHandlers['app:panelopened']({ panelId: 'myPanel' })
|
|
132
142
|
|
|
133
|
-
// Run the
|
|
143
|
+
// Run only the first pending animation frame — panel has zero size so it reschedules
|
|
134
144
|
jest.runOnlyPendingTimers()
|
|
135
145
|
|
|
136
146
|
expect(mapProvider.isGeometryObscured).not.toHaveBeenCalled()
|
|
@@ -151,14 +161,17 @@ describe('useVisibleGeometry', () => {
|
|
|
151
161
|
capturedHandlers['app:panelopened']({ panelId: 'myPanel' })
|
|
152
162
|
jest.runAllTimers()
|
|
153
163
|
|
|
154
|
-
expect(mapProvider.isGeometryObscured).toHaveBeenCalledWith(polygonFeature,
|
|
164
|
+
expect(mapProvider.isGeometryObscured).toHaveBeenCalledWith(polygonFeature, panelRect)
|
|
155
165
|
expect(mapProvider.fitToBounds).toHaveBeenCalledWith(polygonFeature)
|
|
156
166
|
expect(mapProvider.setView).not.toHaveBeenCalled()
|
|
157
167
|
})
|
|
158
168
|
|
|
159
169
|
test('calls setView with center for Point geometry when obscured', () => {
|
|
170
|
+
const appContainer = document.createElement('div')
|
|
171
|
+
appContainer.appendChild(makePanelEl('pointPanel'))
|
|
160
172
|
const { mapProvider, capturedHandlers } = setup({
|
|
161
|
-
panelConfig: { pointPanel: { visibleGeometry: pointFeature, desktop: { slot: '
|
|
173
|
+
panelConfig: { pointPanel: { visibleGeometry: pointFeature, desktop: { slot: 'left-top' } } },
|
|
174
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } }
|
|
162
175
|
})
|
|
163
176
|
renderHook(() => useVisibleGeometry())
|
|
164
177
|
capturedHandlers['app:panelopened']({ panelId: 'pointPanel' })
|
|
@@ -169,8 +182,11 @@ describe('useVisibleGeometry', () => {
|
|
|
169
182
|
})
|
|
170
183
|
|
|
171
184
|
test('calls setView with first coordinate for MultiPoint geometry when obscured', () => {
|
|
185
|
+
const appContainer = document.createElement('div')
|
|
186
|
+
appContainer.appendChild(makePanelEl('mpPanel'))
|
|
172
187
|
const { mapProvider, capturedHandlers } = setup({
|
|
173
|
-
panelConfig: { mpPanel: { visibleGeometry: multiPointFeature, desktop: { slot: '
|
|
188
|
+
panelConfig: { mpPanel: { visibleGeometry: multiPointFeature, desktop: { slot: 'left-top' } } },
|
|
189
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } }
|
|
174
190
|
})
|
|
175
191
|
renderHook(() => useVisibleGeometry())
|
|
176
192
|
capturedHandlers['app:panelopened']({ panelId: 'mpPanel' })
|
|
@@ -182,8 +198,11 @@ describe('useVisibleGeometry', () => {
|
|
|
182
198
|
|
|
183
199
|
test('calls fitToBounds for a raw non-Feature geometry (e.g. Polygon) when obscured', () => {
|
|
184
200
|
const rawPolygon = { type: 'Polygon', coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] }
|
|
201
|
+
const appContainer = document.createElement('div')
|
|
202
|
+
appContainer.appendChild(makePanelEl('geoPanel'))
|
|
185
203
|
const { mapProvider, capturedHandlers } = setup({
|
|
186
|
-
panelConfig: { geoPanel: { visibleGeometry: rawPolygon, desktop: { slot: '
|
|
204
|
+
panelConfig: { geoPanel: { visibleGeometry: rawPolygon, desktop: { slot: 'left-top' } } },
|
|
205
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } }
|
|
187
206
|
})
|
|
188
207
|
renderHook(() => useVisibleGeometry())
|
|
189
208
|
capturedHandlers['app:panelopened']({ panelId: 'geoPanel' })
|
|
@@ -194,8 +213,11 @@ describe('useVisibleGeometry', () => {
|
|
|
194
213
|
|
|
195
214
|
test('calls setView for a raw Point geometry (not Feature-wrapped) when obscured', () => {
|
|
196
215
|
const rawPoint = { type: 'Point', coordinates: [1, 51] }
|
|
216
|
+
const appContainer = document.createElement('div')
|
|
217
|
+
appContainer.appendChild(makePanelEl('rawPointPanel'))
|
|
197
218
|
const { mapProvider, capturedHandlers } = setup({
|
|
198
|
-
panelConfig: { rawPointPanel: { visibleGeometry: rawPoint, desktop: { slot: '
|
|
219
|
+
panelConfig: { rawPointPanel: { visibleGeometry: rawPoint, desktop: { slot: 'left-top' } } },
|
|
220
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } }
|
|
199
221
|
})
|
|
200
222
|
renderHook(() => useVisibleGeometry())
|
|
201
223
|
capturedHandlers['app:panelopened']({ panelId: 'rawPointPanel' })
|
|
@@ -206,8 +228,11 @@ describe('useVisibleGeometry', () => {
|
|
|
206
228
|
|
|
207
229
|
test('does not call setView when Point feature has null coordinates', () => {
|
|
208
230
|
const nullCoordsFeature = { type: 'Feature', geometry: { type: 'Point', coordinates: null }, properties: {} }
|
|
231
|
+
const appContainer = document.createElement('div')
|
|
232
|
+
appContainer.appendChild(makePanelEl('nullPanel'))
|
|
209
233
|
const { mapProvider, capturedHandlers } = setup({
|
|
210
|
-
panelConfig: { nullPanel: { visibleGeometry: nullCoordsFeature, desktop: { slot: '
|
|
234
|
+
panelConfig: { nullPanel: { visibleGeometry: nullCoordsFeature, desktop: { slot: 'left-top' } } },
|
|
235
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } }
|
|
211
236
|
})
|
|
212
237
|
renderHook(() => useVisibleGeometry())
|
|
213
238
|
capturedHandlers['app:panelopened']({ panelId: 'nullPanel' })
|
|
@@ -216,26 +241,14 @@ describe('useVisibleGeometry', () => {
|
|
|
216
241
|
expect(mapProvider.fitToBounds).not.toHaveBeenCalled()
|
|
217
242
|
})
|
|
218
243
|
|
|
219
|
-
test('uses bottom slot ref when panel is in bottom slot', () => {
|
|
220
|
-
const { mapProvider, capturedHandlers } = setup({
|
|
221
|
-
panelConfig: { bottomPanel: { visibleGeometry: polygonFeature, desktop: { slot: 'bottom' } } }
|
|
222
|
-
})
|
|
223
|
-
renderHook(() => useVisibleGeometry())
|
|
224
|
-
capturedHandlers['app:panelopened']({ panelId: 'bottomPanel' })
|
|
225
|
-
jest.runAllTimers()
|
|
226
|
-
|
|
227
|
-
expect(mapProvider.isGeometryObscured).toHaveBeenCalledWith(polygonFeature, bottomPanelRect)
|
|
228
|
-
expect(mapProvider.fitToBounds).toHaveBeenCalledWith(polygonFeature)
|
|
229
|
-
})
|
|
230
|
-
|
|
231
244
|
test('uses latest panelConfig via ref when it changes between renders', () => {
|
|
232
|
-
const { mapProvider, capturedHandlers,
|
|
245
|
+
const { mapProvider, capturedHandlers, appContainer } = setup()
|
|
233
246
|
const { rerender } = renderHook(() => useVisibleGeometry())
|
|
234
247
|
|
|
235
248
|
const updatedGeometry = { type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} }
|
|
236
|
-
const updatedPanelConfig = { myPanel: { visibleGeometry: updatedGeometry, desktop: { slot: '
|
|
249
|
+
const updatedPanelConfig = { myPanel: { visibleGeometry: updatedGeometry, desktop: { slot: 'left-top' } } }
|
|
237
250
|
useApp.mockReturnValue({
|
|
238
|
-
layoutRefs: { mainRef: { current: document.createElement('div') },
|
|
251
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } },
|
|
239
252
|
panelConfig: updatedPanelConfig,
|
|
240
253
|
panelRegistry: { getPanelConfig: jest.fn(() => updatedPanelConfig) },
|
|
241
254
|
breakpoint: 'desktop'
|
|
@@ -249,20 +262,26 @@ describe('useVisibleGeometry', () => {
|
|
|
249
262
|
|
|
250
263
|
test('uses slot from event payload when registry config lacks slot info', () => {
|
|
251
264
|
const freshGeometry = { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] }, properties: {} }
|
|
265
|
+
const appContainer = document.createElement('div')
|
|
266
|
+
appContainer.appendChild(makePanelEl('freshPanel'))
|
|
252
267
|
const { mapProvider, capturedHandlers } = setup({
|
|
253
|
-
panelRegistry: { getPanelConfig: jest.fn(() => ({ freshPanel: { visibleGeometry: freshGeometry } })) }
|
|
268
|
+
panelRegistry: { getPanelConfig: jest.fn(() => ({ freshPanel: { visibleGeometry: freshGeometry } })) },
|
|
269
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } }
|
|
254
270
|
})
|
|
255
271
|
renderHook(() => useVisibleGeometry())
|
|
256
272
|
// Event includes slot (as middleware provides for ADD_PANEL); registry config has no slot info
|
|
257
|
-
capturedHandlers['app:panelopened']({ panelId: 'freshPanel', slot: '
|
|
273
|
+
capturedHandlers['app:panelopened']({ panelId: 'freshPanel', slot: 'left-top' })
|
|
258
274
|
jest.runAllTimers()
|
|
259
275
|
expect(mapProvider.fitToBounds).toHaveBeenCalledWith(freshGeometry)
|
|
260
276
|
})
|
|
261
277
|
|
|
262
278
|
test('falls back to panelRegistry for panels not yet in stale panelConfig', () => {
|
|
263
279
|
const freshGeometry = { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[[0, 0], [1, 0], [1, 1], [0, 0]]] }, properties: {} }
|
|
280
|
+
const appContainer = document.createElement('div')
|
|
281
|
+
appContainer.appendChild(makePanelEl('freshPanel'))
|
|
264
282
|
const { mapProvider, capturedHandlers } = setup({
|
|
265
|
-
panelRegistry: { getPanelConfig: jest.fn(() => ({ freshPanel: { visibleGeometry: freshGeometry, desktop: { slot: '
|
|
283
|
+
panelRegistry: { getPanelConfig: jest.fn(() => ({ freshPanel: { visibleGeometry: freshGeometry, desktop: { slot: 'left-top' } } })) },
|
|
284
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } }
|
|
266
285
|
})
|
|
267
286
|
renderHook(() => useVisibleGeometry())
|
|
268
287
|
capturedHandlers['app:panelopened']({ panelId: 'freshPanel' })
|
|
@@ -271,13 +290,16 @@ describe('useVisibleGeometry', () => {
|
|
|
271
290
|
})
|
|
272
291
|
|
|
273
292
|
test('falls back to config when panel not in panelConfig and registry returns null', () => {
|
|
293
|
+
const appContainer = document.createElement('div')
|
|
294
|
+
appContainer.appendChild(makePanelEl('missingPanel'))
|
|
274
295
|
const { mapProvider, capturedHandlers } = setup({
|
|
275
296
|
panelConfig: {}, // panel not present
|
|
276
|
-
panelRegistry: { getPanelConfig: jest.fn(() => null) } // registry returns null
|
|
297
|
+
panelRegistry: { getPanelConfig: jest.fn(() => null) }, // registry returns null
|
|
298
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } }
|
|
277
299
|
})
|
|
278
300
|
|
|
279
301
|
renderHook(() => useVisibleGeometry())
|
|
280
|
-
capturedHandlers['app:panelopened']({ panelId: 'missingPanel', visibleGeometry: polygonFeature, slot: '
|
|
302
|
+
capturedHandlers['app:panelopened']({ panelId: 'missingPanel', visibleGeometry: polygonFeature, slot: 'left-top' })
|
|
281
303
|
jest.runAllTimers()
|
|
282
304
|
// Should still call fitToBounds using visibleGeometry from event payload
|
|
283
305
|
expect(mapProvider.fitToBounds).toHaveBeenCalledWith(polygonFeature)
|
|
@@ -285,11 +307,14 @@ describe('useVisibleGeometry', () => {
|
|
|
285
307
|
|
|
286
308
|
test('uses visibleGeometry from event payload directly, bypassing registry (ADD_PANEL first-click case)', () => {
|
|
287
309
|
// Registry is empty — simulates first ADD_PANEL before React has processed the reducer
|
|
310
|
+
const appContainer = document.createElement('div')
|
|
311
|
+
appContainer.appendChild(makePanelEl('newPanel'))
|
|
288
312
|
const { mapProvider, capturedHandlers } = setup({
|
|
289
|
-
panelRegistry: { getPanelConfig: jest.fn(() => ({})) }
|
|
313
|
+
panelRegistry: { getPanelConfig: jest.fn(() => ({})) },
|
|
314
|
+
layoutRefs: { mainRef: { current: document.createElement('div') }, appContainerRef: { current: appContainer } }
|
|
290
315
|
})
|
|
291
316
|
renderHook(() => useVisibleGeometry())
|
|
292
|
-
capturedHandlers['app:panelopened']({ panelId: 'newPanel', slot: '
|
|
317
|
+
capturedHandlers['app:panelopened']({ panelId: 'newPanel', slot: 'left-top', visibleGeometry: polygonFeature })
|
|
293
318
|
jest.runAllTimers()
|
|
294
319
|
expect(mapProvider.fitToBounds).toHaveBeenCalledWith(polygonFeature)
|
|
295
320
|
})
|
|
@@ -63,9 +63,6 @@ export const Layout = () => {
|
|
|
63
63
|
<SlotRenderer slot={layoutSlots.TOP_RIGHT} />
|
|
64
64
|
</div>
|
|
65
65
|
</div>
|
|
66
|
-
<div className='im-o-app__inset' ref={layoutRefs.insetRef}>
|
|
67
|
-
<SlotRenderer slot={layoutSlots.INSET} />
|
|
68
|
-
</div>
|
|
69
66
|
<div className='im-o-app__left' ref={layoutRefs.leftRef}>
|
|
70
67
|
<div className='im-o-app__left-top' ref={layoutRefs.leftTopRef}>
|
|
71
68
|
<SlotRenderer slot={layoutSlots.LEFT_TOP} />
|