@defra/interactive-map 0.0.31-alpha → 0.0.33-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/README.md +2 -2
- package/assets/images/bright-ofm-thumb.jpg +0 -0
- package/assets/images/dark-ofm-thumb.jpg +0 -0
- package/assets/images/road-ofm-thumb.jpg +0 -0
- package/assets/templates/add-polygons.njk +143 -0
- package/assets/templates/add-symbols.njk +119 -0
- package/assets/templates/{map.njk → basic-map.njk} +9 -17
- package/assets/templates/button-map.njk +56 -0
- package/assets/templates/draw-tools.njk +161 -0
- package/assets/templates/marker-panel.njk +91 -0
- package/assets/templates/place-marker.njk +104 -0
- package/assets/templates/search-control.njk +98 -0
- package/assets/templates/select-feature.njk +101 -0
- package/assets/templates/style-switcher.njk +87 -0
- package/assets/templates/toggle-marker-label.njk +74 -0
- package/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/im-shell.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api/map-style-config.md +27 -3
- package/docs/api.md +55 -0
- package/docs/assets/css/docusaurus.scss +22 -6
- package/docs/assets/images/add-polygons.jpg +0 -0
- package/docs/assets/images/add-symbols.jpg +0 -0
- package/docs/assets/images/hero.png +0 -0
- package/docs/assets/images/place-marker.jpg +0 -0
- package/docs/assets/images/screenshot.jpg +0 -0
- package/docs/examples/add-marker-with-panel.mdx +1 -1
- package/docs/examples/add-polygons.mdx +225 -0
- package/docs/examples/add-symbols.mdx +177 -0
- package/docs/examples/{draw.mdx → draw-tools.mdx} +2 -2
- package/docs/examples/index.mdx +38 -2
- package/docs/examples/place-marker.mdx +151 -0
- package/docs/plugins/datasets.md +144 -96
- package/docs/plugins/interact.md +1 -4
- package/docusaurus.config.cjs +5 -2
- package/govuk-prototype-kit.config.json +52 -2
- package/package.json +1 -1
- package/plugins/beta/datasets/dist/css/index.css +1 -2
- package/plugins/beta/datasets/dist/esm/im-datasets-ml-adapter.js +1 -1
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-ml-adapter.js +1 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/index.js +1 -1
- package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +27 -9
- package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.test.js +46 -4
- package/plugins/beta/datasets/src/api/setFeatureVisibility.js +10 -1
- package/plugins/beta/datasets/src/api/setFeatureVisibility.test.js +48 -6
- package/plugins/beta/datasets/src/components/LayersMenu/Layers.module.scss +12 -12
- package/plugins/beta/datasets/src/reducers/datasetsToMenu.js +11 -6
- package/plugins/beta/datasets/src/reducers/datasetsToMenu.test.js +49 -0
- package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.js +8 -1
- package/plugins/beta/datasets/src/reducers/pluginState.js +1 -1
- package/plugins/beta/datasets/src/registry/dataset.js +7 -3
- package/plugins/beta/datasets/src/registry/dataset.test.js +14 -0
- package/plugins/beta/datasets/src/registry/dynamicGeoJson.js +1 -1
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
- package/plugins/beta/draw-es/src/api/addFeature.js +12 -3
- package/plugins/beta/draw-es/src/events.js +8 -3
- package/plugins/beta/draw-es/src/events.test.js +2 -0
- package/plugins/beta/draw-es/src/graphic.js +26 -2
- 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 +19 -0
- package/plugins/beta/draw-ml/src/api/newLine.js +1 -0
- package/plugins/beta/draw-ml/src/api/newPolygon.js +1 -0
- package/plugins/beta/draw-ml/src/modes/createDrawMode.js +52 -37
- package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +1 -1
- package/plugins/beta/draw-ml/src/modes/editVertex/vertexOperations.js +5 -4
- package/plugins/beta/draw-ml/src/modes/editVertexMode.js +53 -21
- package/plugins/beta/draw-ol/dist/esm/DrawMode.js +1 -1
- package/plugins/beta/draw-ol/dist/esm/EditMode.js +1 -1
- package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
- package/plugins/beta/draw-ol/src/core/OLDrawManager.js +3 -0
- package/plugins/beta/draw-ol/src/draw/DrawMode.js +57 -49
- package/plugins/beta/draw-ol/src/draw/drawInput.js +76 -32
- package/plugins/beta/draw-ol/src/edit/EditMode.js +4 -4
- package/plugins/beta/draw-ol/src/edit/keyboardHandler.js +4 -2
- package/plugins/beta/draw-ol/src/manifest.js +5 -1
- package/plugins/beta/draw-ol/src/reducer.js +1 -1
- package/plugins/beta/draw-ol/src/snap/snapEngine.js +75 -31
- package/plugins/beta/draw-ol/src/snap/snapGeometry.js +23 -46
- package/plugins/beta/draw-ol/src/snap/snapInteraction.js +30 -29
- package/plugins/beta/draw-ol/src/snap/snapManager.js +10 -1
- package/plugins/beta/draw-ol/src/utils/geometryHelpers.js +9 -3
- package/plugins/beta/draw-ol/src/utils/olCoords.js +2 -2
- package/plugins/beta/draw-ol/src/utils/spatial.js +4 -8
- package/plugins/beta/map-styles/src/mapStyles.scss +41 -41
- package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/index.js +1 -1
- package/plugins/interact/src/InteractInit.jsx +1 -3
- package/plugins/interact/src/InteractInit.test.js +0 -2
- package/plugins/interact/src/events.test.js +1 -1
- package/plugins/interact/src/hooks/useCrossHairVisibility.js +4 -10
- package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +5 -10
- package/plugins/interact/src/hooks/useHighlightSync.js +1 -10
- package/plugins/interact/src/hooks/useHighlightSync.test.js +2 -21
- package/plugins/interact/src/hooks/useInteractionHandlers.js +5 -11
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +5 -25
- package/plugins/interact/src/reducer.js +4 -22
- package/plugins/interact/src/reducer.test.js +11 -39
- package/plugins/search/dist/css/index.css +5 -2
- package/plugins/search/src/components/Form/Form.module.scss +5 -2
- package/plugins/search/src/search.scss +4 -4
- package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/index.js +1 -1
- package/providers/beta/openlayers/src/utils/tileLayers.js +29 -2
- package/providers/beta/openlayers/src/utils/tileLayers.test.js +53 -1
- package/src/App/components/CrossHair/CrossHair.module.scss +8 -8
- package/src/App/components/MapButton/MapButton.jsx +14 -10
- package/src/App/components/MapButton/MapButton.module.scss +1 -1
- package/src/App/components/MapButton/MapButton.test.jsx +1 -1
- package/src/App/components/PopupMenu/PopupMenu.test.jsx +79 -0
- package/src/App/components/PopupMenu/usePopupMenu.js +61 -12
- package/src/App/hooks/useLayoutMeasurements.js +10 -2
- package/src/App/hooks/useResizeObserver.js +4 -2
- package/src/InteractiveMap/InteractiveMap.js +4 -4
- package/src/InteractiveMap/InteractiveMap.test.js +3 -3
- package/src/InteractiveMap/behaviourController.js +3 -1
- package/src/InteractiveMap/behaviourController.test.js +6 -3
- package/src/InteractiveMap/domStateManager.js +2 -0
- package/src/config/events.js +21 -3
- package/src/types.js +12 -3
- package/src/utils/findNextTabStop.js +3 -3
- package/src/utils/findNextTabStop.test.js +25 -1
- package/webpack.dev.mjs +1 -0
- package/docs/assets/images/screens-blue.jpg +0 -0
- package/docs/assets/images/screens-white.jpg +0 -0
|
@@ -9,16 +9,12 @@ import { buildStylesMap } from '../utils/buildStylesMap.js'
|
|
|
9
9
|
* (shown with the keyboard cursor ring) as separate arguments so the provider can style them
|
|
10
10
|
* differently. Also re-applies highlights after a map style reload, since highlight layers
|
|
11
11
|
* are removed when the base style changes.
|
|
12
|
-
*
|
|
13
|
-
* Dispatches UPDATE_SELECTED_BOUNDS with the bounding box returned by the provider so
|
|
14
|
-
* downstream consumers (e.g. interact:done) receive up-to-date bounds.
|
|
15
12
|
*/
|
|
16
13
|
export const useHighlightSync = ({
|
|
17
14
|
mapProvider,
|
|
18
15
|
mapStyle,
|
|
19
16
|
pluginState,
|
|
20
17
|
selectedFeatures,
|
|
21
|
-
dispatch,
|
|
22
18
|
events,
|
|
23
19
|
eventBus
|
|
24
20
|
}) => {
|
|
@@ -37,12 +33,7 @@ export const useHighlightSync = ({
|
|
|
37
33
|
const activeFeatures = listboxActiveItem
|
|
38
34
|
? [{ featureId: listboxActiveItem.featureId, layerId: listboxActiveItem.layerId, idProperty: listboxActiveItem.idProperty, geometry: listboxActiveItem.geometry }]
|
|
39
35
|
: []
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
dispatch({
|
|
43
|
-
type: 'UPDATE_SELECTED_BOUNDS',
|
|
44
|
-
payload: bounds
|
|
45
|
-
})
|
|
36
|
+
mapProvider.updateHighlightedFeatures?.(selectedFeatures, activeFeatures, stylesMap)
|
|
46
37
|
}
|
|
47
38
|
|
|
48
39
|
useEffect(() => {
|
|
@@ -30,7 +30,6 @@ beforeEach(() => {
|
|
|
30
30
|
layers: [{ layerId: 'layer1' }]
|
|
31
31
|
},
|
|
32
32
|
selectedFeatures: [],
|
|
33
|
-
dispatch: jest.fn(),
|
|
34
33
|
events: { MAP_STYLE_CHANGE: STYLE_CHANGE, MAP_DATA_CHANGE: DATA_CHANGE },
|
|
35
34
|
eventBus: {
|
|
36
35
|
on: jest.fn((event, handler) => {
|
|
@@ -51,7 +50,7 @@ beforeEach(() => {
|
|
|
51
50
|
// ─── useHighlightSync — highlighting ─────────────────────────────────────────
|
|
52
51
|
|
|
53
52
|
describe('useHighlightSync — highlighting', () => {
|
|
54
|
-
it('updates map highlights
|
|
53
|
+
it('updates map highlights', () => {
|
|
55
54
|
mockDeps.selectedFeatures = [{ featureId: 'F1', layerId: 'layer1' }]
|
|
56
55
|
|
|
57
56
|
render()
|
|
@@ -61,22 +60,6 @@ describe('useHighlightSync — highlighting', () => {
|
|
|
61
60
|
[],
|
|
62
61
|
expect.any(Object)
|
|
63
62
|
)
|
|
64
|
-
expect(mockDeps.dispatch).toHaveBeenCalledWith({
|
|
65
|
-
type: 'UPDATE_SELECTED_BOUNDS',
|
|
66
|
-
payload: { sw: [0, 0], ne: [1, 1] }
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('dispatches null bounds when provider returns null', () => {
|
|
71
|
-
mockDeps.selectedFeatures = [{ featureId: 'F1' }]
|
|
72
|
-
mockDeps.mapProvider.updateHighlightedFeatures.mockReturnValue(null)
|
|
73
|
-
|
|
74
|
-
render()
|
|
75
|
-
|
|
76
|
-
expect(mockDeps.dispatch).toHaveBeenCalledWith({
|
|
77
|
-
type: 'UPDATE_SELECTED_BOUNDS',
|
|
78
|
-
payload: null
|
|
79
|
-
})
|
|
80
63
|
})
|
|
81
64
|
})
|
|
82
65
|
|
|
@@ -144,9 +127,7 @@ describe('useHighlightSync — guards', () => {
|
|
|
144
127
|
mockDeps.mapProvider = null
|
|
145
128
|
mockDeps.selectedFeatures = [{ featureId: 'F1' }]
|
|
146
129
|
|
|
147
|
-
render()
|
|
148
|
-
|
|
149
|
-
expect(mockDeps.dispatch).not.toHaveBeenCalled()
|
|
130
|
+
expect(() => render()).not.toThrow()
|
|
150
131
|
})
|
|
151
132
|
|
|
152
133
|
it('does nothing when mapStyle is null', () => {
|
|
@@ -37,15 +37,10 @@ const findMarkerAtPoint = (markers, point, scale) => {
|
|
|
37
37
|
return null
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const useSelectionChangeEmitter = (eventBus, selectedFeatures, selectedMarkers
|
|
40
|
+
const useSelectionChangeEmitter = (eventBus, selectedFeatures, selectedMarkers) => {
|
|
41
41
|
const lastEmittedSelectionChange = useRef(null)
|
|
42
42
|
|
|
43
43
|
useEffect(() => {
|
|
44
|
-
const awaitingBounds = selectedFeatures.length > 0 && !selectionBounds
|
|
45
|
-
if (awaitingBounds) {
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
|
|
49
44
|
const prev = lastEmittedSelectionChange.current
|
|
50
45
|
const wasEmpty = prev === null || (prev.features.length === 0 && prev.markers.length === 0)
|
|
51
46
|
if (wasEmpty && selectedFeatures.length === 0 && selectedMarkers.length === 0) {
|
|
@@ -53,14 +48,13 @@ const useSelectionChangeEmitter = (eventBus, selectedFeatures, selectedMarkers,
|
|
|
53
48
|
}
|
|
54
49
|
|
|
55
50
|
eventBus.emit('interact:selectionchange', {
|
|
56
|
-
selectedFeatures,
|
|
51
|
+
selectedFeatures: selectedFeatures.map(({ featureId, layerId, idProperty, properties }) => ({ featureId, layerId, idProperty, properties })),
|
|
57
52
|
selectedMarkers,
|
|
58
|
-
selectionBounds,
|
|
59
53
|
contiguous: areAllContiguous(selectedFeatures)
|
|
60
54
|
})
|
|
61
55
|
|
|
62
56
|
lastEmittedSelectionChange.current = { features: selectedFeatures, markers: selectedMarkers }
|
|
63
|
-
}, [selectedFeatures, selectedMarkers
|
|
57
|
+
}, [selectedFeatures, selectedMarkers])
|
|
64
58
|
}
|
|
65
59
|
|
|
66
60
|
/**
|
|
@@ -220,7 +214,7 @@ export const useInteractionHandlers = ({ mapState, pluginState, services, mapPro
|
|
|
220
214
|
const {
|
|
221
215
|
dispatch, layers, interactionModes, multiSelect, contiguous,
|
|
222
216
|
marker: markerOptions, tolerance, selectedFeatures, selectedMarkers,
|
|
223
|
-
|
|
217
|
+
deselectOnClickOutside
|
|
224
218
|
} = pluginState
|
|
225
219
|
const { eventBus } = services
|
|
226
220
|
const layerConfigMap = buildLayerConfigMap(layers)
|
|
@@ -272,6 +266,6 @@ export const useInteractionHandlers = ({ mapState, pluginState, services, mapPro
|
|
|
272
266
|
scale
|
|
273
267
|
})
|
|
274
268
|
|
|
275
|
-
useSelectionChangeEmitter(eventBus, selectedFeatures, selectedMarkers
|
|
269
|
+
useSelectionChangeEmitter(eventBus, selectedFeatures, selectedMarkers)
|
|
276
270
|
return { handleInteraction }
|
|
277
271
|
}
|
|
@@ -65,7 +65,6 @@ const setup = (pluginOverrides = {}, markerItems = [], markerRefs = new Map()) =
|
|
|
65
65
|
marker: { symbol: 'pin', backgroundColor: 'red' },
|
|
66
66
|
selectedFeatures: [],
|
|
67
67
|
selectedMarkers: [],
|
|
68
|
-
selectionBounds: null,
|
|
69
68
|
...pluginOverrides
|
|
70
69
|
},
|
|
71
70
|
services: {
|
|
@@ -345,30 +344,12 @@ it('does not check markers when selectMarker is not in interactionModes', () =>
|
|
|
345
344
|
/* Selection change event */
|
|
346
345
|
/* ------------------------------------------------------------------ */
|
|
347
346
|
|
|
348
|
-
it('
|
|
347
|
+
it('emits selectionchange when features are selected', () => {
|
|
349
348
|
const deps = {
|
|
350
349
|
mapState: { markers: { add: jest.fn(), remove: jest.fn(), items: [], markerRefs: new Map() } },
|
|
351
350
|
pluginState: {
|
|
352
|
-
selectedFeatures: [{ featureId: 'F1' }],
|
|
353
|
-
selectedMarkers: []
|
|
354
|
-
selectionBounds: null
|
|
355
|
-
},
|
|
356
|
-
services: { eventBus: { emit: jest.fn() } },
|
|
357
|
-
mapProvider: { getFeatureGeometry: jest.fn(() => null) }
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
renderHook(() => useInteractionHandlers(deps))
|
|
361
|
-
|
|
362
|
-
expect(deps.services.eventBus.emit).not.toHaveBeenCalled()
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
it('emits selectionchange once when bounds exist', () => {
|
|
366
|
-
const deps = {
|
|
367
|
-
mapState: { markers: { add: jest.fn(), remove: jest.fn(), items: [], markerRefs: new Map() } },
|
|
368
|
-
pluginState: {
|
|
369
|
-
selectedFeatures: [{ featureId: 'F1' }],
|
|
370
|
-
selectedMarkers: [],
|
|
371
|
-
selectionBounds: { sw: [0, 0], ne: [1, 1] }
|
|
351
|
+
selectedFeatures: [{ featureId: 'F1', layerId: 'l1', idProperty: 'id', properties: { name: 'A' }, geometry: { type: 'Point', coordinates: [0, 0] } }],
|
|
352
|
+
selectedMarkers: []
|
|
372
353
|
},
|
|
373
354
|
services: { eventBus: { emit: jest.fn() } },
|
|
374
355
|
mapProvider: { getFeatureGeometry: jest.fn(() => null) }
|
|
@@ -379,9 +360,8 @@ it('emits selectionchange once when bounds exist', () => {
|
|
|
379
360
|
expect(deps.services.eventBus.emit).toHaveBeenCalledWith(
|
|
380
361
|
'interact:selectionchange',
|
|
381
362
|
expect.objectContaining({
|
|
382
|
-
selectedFeatures:
|
|
363
|
+
selectedFeatures: [{ featureId: 'F1', layerId: 'l1', idProperty: 'id', properties: { name: 'A' } }],
|
|
383
364
|
selectedMarkers: [],
|
|
384
|
-
selectionBounds: deps.pluginState.selectionBounds,
|
|
385
365
|
contiguous: false
|
|
386
366
|
})
|
|
387
367
|
)
|
|
@@ -394,7 +374,7 @@ it('skips emission when selection remains empty after being cleared', () => {
|
|
|
394
374
|
const { rerender } = renderHook(
|
|
395
375
|
({ features }) => useInteractionHandlers({
|
|
396
376
|
mapState: { markers: { items: [], markerRefs: new Map() } },
|
|
397
|
-
pluginState: { selectedFeatures: features, selectedMarkers: []
|
|
377
|
+
pluginState: { selectedFeatures: features, selectedMarkers: [] },
|
|
398
378
|
services: { eventBus },
|
|
399
379
|
mapProvider: { getFeatureGeometry: jest.fn(() => null) }
|
|
400
380
|
}),
|
|
@@ -7,7 +7,6 @@ const initialState = {
|
|
|
7
7
|
deselectOnClickOutside: false,
|
|
8
8
|
selectedFeatures: [],
|
|
9
9
|
selectedMarkers: [],
|
|
10
|
-
selectionBounds: null,
|
|
11
10
|
closeOnAction: true, // Done or Cancel
|
|
12
11
|
listboxActiveItem: null // { featureId, layerId, idProperty, geometry } | null — ring shown but no selectionchange
|
|
13
12
|
}
|
|
@@ -26,7 +25,6 @@ const disable = (state) => {
|
|
|
26
25
|
enabled: false,
|
|
27
26
|
selectedFeatures: [],
|
|
28
27
|
selectedMarkers: [],
|
|
29
|
-
selectionBounds: null,
|
|
30
28
|
listboxActiveItem: null
|
|
31
29
|
}
|
|
32
30
|
}
|
|
@@ -46,7 +44,7 @@ const toggleSelectedFeatures = (state, payload) => {
|
|
|
46
44
|
// 1. Handle explicit unselect
|
|
47
45
|
if (addToExisting === false) {
|
|
48
46
|
const filtered = currentSelected.filter((_, i) => i !== existingIndex)
|
|
49
|
-
return { ...state, selectedFeatures: filtered
|
|
47
|
+
return { ...state, selectedFeatures: filtered }
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
// Define the feature object once to avoid repetition
|
|
@@ -70,18 +68,7 @@ const toggleSelectedFeatures = (state, payload) => {
|
|
|
70
68
|
nextSelected = isSameSingle ? [] : [featureObj]
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
return { ...state, selectedFeatures: nextSelected, selectedMarkers: multiSelect && !replaceAll ? state.selectedMarkers : []
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Update bounds (called from useEffect after map provider calculates them)
|
|
77
|
-
const updateSelectedBounds = (state, payload) => {
|
|
78
|
-
if (JSON.stringify(payload) === JSON.stringify(state.selectionBounds)) {
|
|
79
|
-
return state
|
|
80
|
-
}
|
|
81
|
-
return {
|
|
82
|
-
...state,
|
|
83
|
-
selectionBounds: payload
|
|
84
|
-
}
|
|
71
|
+
return { ...state, selectedFeatures: nextSelected, selectedMarkers: multiSelect && !replaceAll ? state.selectedMarkers : [] }
|
|
85
72
|
}
|
|
86
73
|
|
|
87
74
|
const toggleSelectedMarkers = (state, { markerId, multiSelect }) => {
|
|
@@ -94,7 +81,6 @@ const toggleSelectedMarkers = (state, { markerId, multiSelect }) => {
|
|
|
94
81
|
return {
|
|
95
82
|
...state,
|
|
96
83
|
selectedFeatures: [],
|
|
97
|
-
selectionBounds: null,
|
|
98
84
|
selectedMarkers: exists && current.length === 1 ? [] : [markerId]
|
|
99
85
|
}
|
|
100
86
|
}
|
|
@@ -103,8 +89,7 @@ const clearSelectedFeatures = (state) => {
|
|
|
103
89
|
return {
|
|
104
90
|
...state,
|
|
105
91
|
selectedFeatures: [],
|
|
106
|
-
selectedMarkers: []
|
|
107
|
-
selectionBounds: null
|
|
92
|
+
selectedMarkers: []
|
|
108
93
|
}
|
|
109
94
|
}
|
|
110
95
|
|
|
@@ -124,7 +109,6 @@ const selectMarker = (state, { markerId, multiSelect }) => {
|
|
|
124
109
|
return {
|
|
125
110
|
...state,
|
|
126
111
|
selectedFeatures: multiSelect ? state.selectedFeatures : [],
|
|
127
|
-
selectionBounds: null,
|
|
128
112
|
selectedMarkers: nextMarkers
|
|
129
113
|
}
|
|
130
114
|
}
|
|
@@ -145,8 +129,7 @@ const unselectMarker = (state, { markerId }) => {
|
|
|
145
129
|
|
|
146
130
|
const setSelectedFeatures = (state, features) => ({
|
|
147
131
|
...state,
|
|
148
|
-
selectedFeatures: features
|
|
149
|
-
selectionBounds: null
|
|
132
|
+
selectedFeatures: features
|
|
150
133
|
})
|
|
151
134
|
|
|
152
135
|
const actions = {
|
|
@@ -154,7 +137,6 @@ const actions = {
|
|
|
154
137
|
DISABLE: disable,
|
|
155
138
|
TOGGLE_SELECTED_FEATURES: toggleSelectedFeatures,
|
|
156
139
|
TOGGLE_SELECTED_MARKERS: toggleSelectedMarkers,
|
|
157
|
-
UPDATE_SELECTED_BOUNDS: updateSelectedBounds,
|
|
158
140
|
CLEAR_SELECTED_FEATURES: clearSelectedFeatures,
|
|
159
141
|
SET_SELECTED_FEATURES: setSelectedFeatures,
|
|
160
142
|
SELECT_MARKER: selectMarker,
|
|
@@ -11,7 +11,6 @@ describe('initialState', () => {
|
|
|
11
11
|
deselectOnClickOutside: false,
|
|
12
12
|
selectedFeatures: [],
|
|
13
13
|
selectedMarkers: [],
|
|
14
|
-
selectionBounds: null,
|
|
15
14
|
closeOnAction: true,
|
|
16
15
|
listboxActiveItem: null
|
|
17
16
|
})
|
|
@@ -33,7 +32,7 @@ describe('ENABLE/DISABLE actions', () => {
|
|
|
33
32
|
|
|
34
33
|
it('DISABLE sets enabled to false, clears selection and markers, and preserves other state', () => {
|
|
35
34
|
const marker = { symbol: 'pin', backgroundColor: 'red' }
|
|
36
|
-
const state = { ...initialState, enabled: true, layers: [1], marker, selectedFeatures: [{ featureId: 'f1' }], selectedMarkers: ['m1']
|
|
35
|
+
const state = { ...initialState, enabled: true, layers: [1], marker, selectedFeatures: [{ featureId: 'f1' }], selectedMarkers: ['m1'] }
|
|
37
36
|
const result = actions.DISABLE(state)
|
|
38
37
|
|
|
39
38
|
expect(result.enabled).toBe(false)
|
|
@@ -41,7 +40,6 @@ describe('ENABLE/DISABLE actions', () => {
|
|
|
41
40
|
expect(result.marker).toEqual(marker)
|
|
42
41
|
expect(result.selectedFeatures).toEqual([])
|
|
43
42
|
expect(result.selectedMarkers).toEqual([])
|
|
44
|
-
expect(result.selectionBounds).toBeNull()
|
|
45
43
|
expect(result).not.toBe(state)
|
|
46
44
|
})
|
|
47
45
|
})
|
|
@@ -56,7 +54,7 @@ describe('TOGGLE_SELECTED_FEATURES action', () => {
|
|
|
56
54
|
})
|
|
57
55
|
|
|
58
56
|
it('handles single-select, multi-select, add/remove, and replaceAll', () => {
|
|
59
|
-
let state = { ...initialState
|
|
57
|
+
let state = { ...initialState }
|
|
60
58
|
|
|
61
59
|
// Single-select: add
|
|
62
60
|
state = actions.TOGGLE_SELECTED_FEATURES(state, createFeature('f1'))
|
|
@@ -66,33 +64,27 @@ describe('TOGGLE_SELECTED_FEATURES action', () => {
|
|
|
66
64
|
state = actions.TOGGLE_SELECTED_FEATURES(state, createFeature('f2'))
|
|
67
65
|
expect(state.selectedFeatures[0].featureId).toBe('f2')
|
|
68
66
|
|
|
69
|
-
// Toggle off same
|
|
67
|
+
// Toggle off same
|
|
70
68
|
state = actions.TOGGLE_SELECTED_FEATURES(state, createFeature('f2'))
|
|
71
69
|
expect(state.selectedFeatures).toHaveLength(0)
|
|
72
|
-
expect(state.selectionBounds).toBeNull()
|
|
73
70
|
|
|
74
71
|
// Multi-select: add multiple
|
|
75
|
-
state = { ...state, selectionBounds: { sw: [0, 0], ne: [1, 1] } }
|
|
76
72
|
state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f1'), multiSelect: true })
|
|
77
73
|
state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f2'), multiSelect: true })
|
|
78
74
|
expect(state.selectedFeatures.map(f => f.featureId)).toEqual(['f1', 'f2'])
|
|
79
75
|
|
|
80
|
-
// Multi-select: remove (not last)
|
|
76
|
+
// Multi-select: remove (not last)
|
|
81
77
|
state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f1'), multiSelect: true })
|
|
82
78
|
expect(state.selectedFeatures.map(f => f.featureId)).toEqual(['f2'])
|
|
83
|
-
expect(state.selectionBounds).toBeNull()
|
|
84
79
|
|
|
85
|
-
// Multi-select: remove last
|
|
80
|
+
// Multi-select: remove last
|
|
86
81
|
state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f2'), multiSelect: true })
|
|
87
82
|
expect(state.selectedFeatures).toHaveLength(0)
|
|
88
|
-
expect(state.selectionBounds).toBeNull()
|
|
89
83
|
|
|
90
|
-
// addToExisting false removes feature
|
|
91
|
-
state = { ...state, selectionBounds: { sw: [0, 0], ne: [1, 1] } }
|
|
84
|
+
// addToExisting false removes feature
|
|
92
85
|
state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f2'), multiSelect: true })
|
|
93
86
|
state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f2'), addToExisting: false })
|
|
94
87
|
expect(state.selectedFeatures).toHaveLength(0)
|
|
95
|
-
expect(state.selectionBounds).toBeNull()
|
|
96
88
|
|
|
97
89
|
// replaceAll replaces everything
|
|
98
90
|
state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f3'), replaceAll: true })
|
|
@@ -131,26 +123,12 @@ describe('TOGGLE_SELECTED_FEATURES action', () => {
|
|
|
131
123
|
})
|
|
132
124
|
})
|
|
133
125
|
|
|
134
|
-
describe('UPDATE_SELECTED_BOUNDS action', () => {
|
|
135
|
-
it('updates selectionBounds correctly', () => {
|
|
136
|
-
const state = { ...initialState, selectionBounds: { sw: [0, 0], ne: [1, 1] } }
|
|
137
|
-
const newBounds = { sw: [0, 0], ne: [2, 2] }
|
|
138
|
-
const result = actions.UPDATE_SELECTED_BOUNDS(state, newBounds)
|
|
139
|
-
expect(result.selectionBounds).toEqual(newBounds)
|
|
140
|
-
|
|
141
|
-
// unchanged bounds returns same state
|
|
142
|
-
const result2 = actions.UPDATE_SELECTED_BOUNDS(state, { sw: [0, 0], ne: [1, 1] })
|
|
143
|
-
expect(result2).toBe(state)
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
126
|
describe('TOGGLE_SELECTED_MARKERS action', () => {
|
|
148
127
|
it('selects a marker in single-select mode and clears features', () => {
|
|
149
|
-
const state = { ...initialState, selectedFeatures: [{ featureId: 'f1' }]
|
|
128
|
+
const state = { ...initialState, selectedFeatures: [{ featureId: 'f1' }] }
|
|
150
129
|
const result = actions.TOGGLE_SELECTED_MARKERS(state, { markerId: 'm1', multiSelect: false })
|
|
151
130
|
expect(result.selectedMarkers).toEqual(['m1'])
|
|
152
131
|
expect(result.selectedFeatures).toEqual([])
|
|
153
|
-
expect(result.selectionBounds).toBeNull()
|
|
154
132
|
})
|
|
155
133
|
|
|
156
134
|
it('toggles off the only selected marker in single-select mode', () => {
|
|
@@ -174,17 +152,15 @@ describe('TOGGLE_SELECTED_MARKERS action', () => {
|
|
|
174
152
|
})
|
|
175
153
|
|
|
176
154
|
describe('CLEAR_SELECTED_FEATURES action', () => {
|
|
177
|
-
it('resets features
|
|
155
|
+
it('resets features and markers', () => {
|
|
178
156
|
const state = {
|
|
179
157
|
...initialState,
|
|
180
158
|
selectedFeatures: [1],
|
|
181
|
-
selectedMarkers: ['m1']
|
|
182
|
-
selectionBounds: { sw: [0, 0], ne: [1, 1] }
|
|
159
|
+
selectedMarkers: ['m1']
|
|
183
160
|
}
|
|
184
161
|
const result = actions.CLEAR_SELECTED_FEATURES(state)
|
|
185
162
|
expect(result.selectedFeatures).toEqual([])
|
|
186
163
|
expect(result.selectedMarkers).toEqual([])
|
|
187
|
-
expect(result.selectionBounds).toBeNull()
|
|
188
164
|
expect(result).not.toBe(state)
|
|
189
165
|
})
|
|
190
166
|
})
|
|
@@ -209,7 +185,6 @@ describe('SELECT_MARKER action', () => {
|
|
|
209
185
|
const result = actions.SELECT_MARKER(state, { markerId: 'm1', multiSelect: false })
|
|
210
186
|
expect(result.selectedMarkers).toEqual(['m1'])
|
|
211
187
|
expect(result.selectedFeatures).toEqual([])
|
|
212
|
-
expect(result.selectionBounds).toBeNull()
|
|
213
188
|
})
|
|
214
189
|
|
|
215
190
|
it('adds a marker in multi-select mode without clearing selectedFeatures', () => {
|
|
@@ -241,16 +216,14 @@ describe('UNSELECT_MARKER action', () => {
|
|
|
241
216
|
})
|
|
242
217
|
|
|
243
218
|
describe('SET_SELECTED_FEATURES action', () => {
|
|
244
|
-
it('replaces selectedFeatures
|
|
219
|
+
it('replaces selectedFeatures', () => {
|
|
245
220
|
const state = {
|
|
246
221
|
...initialState,
|
|
247
|
-
selectedFeatures: [{ featureId: 'A' }, { featureId: 'B' }, { featureId: 'C' }]
|
|
248
|
-
selectionBounds: [0, 0, 1, 1]
|
|
222
|
+
selectedFeatures: [{ featureId: 'A' }, { featureId: 'B' }, { featureId: 'C' }]
|
|
249
223
|
}
|
|
250
224
|
const trimmed = [{ featureId: 'A' }]
|
|
251
225
|
const result = actions.SET_SELECTED_FEATURES(state, trimmed)
|
|
252
226
|
expect(result.selectedFeatures).toEqual(trimmed)
|
|
253
|
-
expect(result.selectionBounds).toBeNull()
|
|
254
227
|
expect(result).not.toBe(state)
|
|
255
228
|
})
|
|
256
229
|
})
|
|
@@ -262,7 +235,6 @@ describe('actions object', () => {
|
|
|
262
235
|
'DISABLE',
|
|
263
236
|
'TOGGLE_SELECTED_FEATURES',
|
|
264
237
|
'TOGGLE_SELECTED_MARKERS',
|
|
265
|
-
'UPDATE_SELECTED_BOUNDS',
|
|
266
238
|
'CLEAR_SELECTED_FEATURES',
|
|
267
239
|
'SET_SELECTED_FEATURES',
|
|
268
240
|
'SELECT_MARKER',
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
padding: 0;
|
|
4
4
|
min-height: auto;
|
|
5
5
|
border-radius: 0;
|
|
6
|
+
filter: var(--search-drop-shadow);
|
|
7
|
+
-webkit-transform: translate3d(0, 0, 0);
|
|
8
|
+
-moz-transform: translate3d(0, 0, 0);
|
|
9
|
+
-ms-transform: translate3d(0, 0, 0);
|
|
10
|
+
transform: translate3d(0, 0, 0);
|
|
6
11
|
}
|
|
7
12
|
.im-c-search-form:before {
|
|
8
13
|
border-radius: 0;
|
|
@@ -16,7 +21,6 @@
|
|
|
16
21
|
right: 0;
|
|
17
22
|
height: auto;
|
|
18
23
|
overflow: visible;
|
|
19
|
-
filter: var(--search-drop-shadow);
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
.im-o-app--tablet .im-c-search-form,
|
|
@@ -25,7 +29,6 @@
|
|
|
25
29
|
overflow: visible;
|
|
26
30
|
border-radius: var(--button-border-radius);
|
|
27
31
|
box-shadow: none;
|
|
28
|
-
filter: var(--search-drop-shadow);
|
|
29
32
|
}
|
|
30
33
|
.im-o-app--tablet .im-c-search-form:before,
|
|
31
34
|
.im-o-app--desktop .im-c-search-form:before {
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
padding: 0;
|
|
4
4
|
min-height: auto;
|
|
5
5
|
border-radius: 0;
|
|
6
|
+
filter: var(--search-drop-shadow);
|
|
7
|
+
-webkit-transform: translate3d(0, 0, 0);
|
|
8
|
+
-moz-transform: translate3d(0, 0, 0);
|
|
9
|
+
-ms-transform: translate3d(0, 0, 0);
|
|
10
|
+
transform: translate3d(0, 0, 0);
|
|
6
11
|
|
|
7
12
|
&:before {
|
|
8
13
|
border-radius: 0;
|
|
@@ -17,7 +22,6 @@
|
|
|
17
22
|
right: 0;
|
|
18
23
|
height: auto;
|
|
19
24
|
overflow: visible;
|
|
20
|
-
filter: var(--search-drop-shadow);
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
.im-o-app--tablet .im-c-search-form,
|
|
@@ -26,7 +30,6 @@
|
|
|
26
30
|
overflow: visible;
|
|
27
31
|
border-radius: var(--button-border-radius);
|
|
28
32
|
box-shadow: none;
|
|
29
|
-
filter: var(--search-drop-shadow);
|
|
30
33
|
|
|
31
34
|
&:before {
|
|
32
35
|
border-radius: var(--button-border-radius);
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
content: '';
|
|
33
33
|
z-index: 1;
|
|
34
34
|
position: absolute;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
top: -1px;
|
|
36
|
+
right: -1px;
|
|
37
|
+
bottom: -1px;
|
|
38
|
+
left: -1px;
|
|
39
39
|
border-radius: var(--button-border-radius);
|
|
40
40
|
border: 2px solid var(--foreground-color);
|
|
41
41
|
pointer-events: none;
|