@defra/interactive-map 0.0.30-alpha → 0.0.32-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/context.md +4 -4
- package/docs/api.md +166 -37
- package/docs/assets/css/docusaurus.scss +36 -4
- package/docs/assets/images/add-polygons.jpg +0 -0
- package/docs/assets/images/add-symbols.jpg +0 -0
- package/docs/assets/images/basic-map.jpg +0 -0
- package/docs/assets/images/button-first.jpg +0 -0
- package/docs/assets/images/deuteranopia-ozs-map-thumb.jpg +0 -0
- package/docs/assets/images/draw.jpg +0 -0
- package/docs/assets/images/marker-panel.jpg +0 -0
- package/docs/assets/images/night-ozs-map-thumb.jpg +0 -0
- package/docs/assets/images/outdoor-ozs-map-thumb.jpg +0 -0
- package/docs/assets/images/place-marker.jpg +0 -0
- package/docs/assets/images/screenshot.jpg +0 -0
- package/docs/assets/images/search.jpg +0 -0
- package/docs/assets/images/select-feature.jpg +0 -0
- package/docs/assets/images/style-switcher.jpg +0 -0
- package/docs/assets/images/toggle-marker-label.jpg +0 -0
- package/docs/assets/images/tritanopia-ozs-map-thumb.jpg +0 -0
- package/docs/examples/add-marker-with-panel.mdx +3 -3
- package/docs/examples/add-polygons.mdx +225 -0
- package/docs/examples/add-symbols.mdx +177 -0
- package/docs/examples/basic-map.mdx +2 -2
- package/docs/examples/draw-tools.mdx +271 -0
- package/docs/examples/index.mdx +95 -11
- package/docs/examples/place-marker.mdx +151 -0
- package/docs/examples/search-control.mdx +71 -0
- package/docs/examples/select-feature.mdx +146 -0
- package/docs/examples/style-switcher.mdx +135 -0
- package/docs/examples/toggle-marker-label.mdx +3 -3
- package/docs/plugins/datasets.md +157 -122
- package/docs/plugins/interact.md +1 -4
- package/docusaurus.config.cjs +9 -2
- package/govuk-prototype-kit.config.json +52 -2
- package/package.json +1 -1
- package/plugins/beta/datasets/dist/css/index.css +5 -2
- package/plugins/beta/datasets/dist/esm/im-datasets-ml-adapter.js +1 -0
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/esm/index.js +1 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-ml-adapter.js +2 -0
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
- package/plugins/beta/datasets/dist/umd/index.js +2 -0
- package/plugins/beta/datasets/src/adapters/loadLayerAdapter.js +33 -0
- package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +44 -120
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +145 -245
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +516 -0
- package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +209 -0
- package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.test.js +483 -0
- package/plugins/beta/datasets/src/api/addDataset.js +2 -5
- package/plugins/beta/datasets/src/api/addDataset.test.js +16 -0
- package/plugins/beta/datasets/src/api/getOpacity.js +14 -14
- package/plugins/beta/datasets/src/api/getOpacity.test.js +72 -0
- package/plugins/beta/datasets/src/api/getStyle.js +8 -9
- package/plugins/beta/datasets/src/api/getStyle.test.js +42 -0
- package/plugins/beta/datasets/src/api/removeDataset.js +7 -7
- package/plugins/beta/datasets/src/api/removeDataset.test.js +45 -0
- package/plugins/beta/datasets/src/api/setData.js +9 -3
- package/plugins/beta/datasets/src/api/setData.test.js +62 -0
- package/plugins/beta/datasets/src/api/setDatasetVisibility.js +5 -30
- package/plugins/beta/datasets/src/api/setDatasetVisibility.test.js +31 -0
- package/plugins/beta/datasets/src/api/setFeatureVisibility.js +9 -18
- package/plugins/beta/datasets/src/api/setFeatureVisibility.test.js +74 -0
- package/plugins/beta/datasets/src/api/setGlobals.js +20 -0
- package/plugins/beta/datasets/src/api/setGlobals.test.js +63 -0
- package/plugins/beta/datasets/src/api/setOpacity.js +8 -27
- package/plugins/beta/datasets/src/api/setOpacity.test.js +31 -0
- package/plugins/beta/datasets/src/api/setStyle.js +5 -21
- package/plugins/beta/datasets/src/api/setStyle.test.js +53 -0
- package/plugins/beta/datasets/src/components/Key/Key.jsx +51 -0
- package/plugins/beta/datasets/src/components/Key/KeyGroupItem.jsx +18 -0
- package/plugins/beta/datasets/src/components/Key/KeyItem.jsx +20 -0
- package/plugins/beta/datasets/src/components/Key/KeySvg.jsx +22 -0
- package/plugins/beta/datasets/src/components/Key/KeySvg.test.jsx +87 -0
- package/plugins/beta/datasets/src/components/Key/KeySvgLine.jsx +19 -0
- package/plugins/beta/datasets/src/components/Key/KeySvgLine.test.jsx +71 -0
- package/plugins/beta/datasets/src/components/{KeySvgPattern.jsx → Key/KeySvgPattern.jsx} +3 -4
- package/plugins/beta/datasets/src/components/Key/KeySvgPattern.test.jsx +55 -0
- package/plugins/beta/datasets/src/components/Key/KeySvgRect.jsx +22 -0
- package/plugins/beta/datasets/src/components/Key/KeySvgRect.test.jsx +85 -0
- package/plugins/beta/datasets/src/components/{KeySvgSymbol.jsx → Key/KeySvgSymbol.jsx} +5 -5
- package/plugins/beta/datasets/src/components/Key/KeySvgSymbol.test.jsx +80 -0
- package/plugins/beta/datasets/src/components/Key/svgProperties.test.js +61 -0
- package/plugins/beta/datasets/src/{panels → components/LayersMenu}/Layers.module.scss +15 -11
- package/plugins/beta/datasets/src/components/LayersMenu/LayersMenu.jsx +33 -0
- package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuCheckbox.jsx +31 -0
- package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuGroupWrapper.jsx +19 -0
- package/plugins/beta/datasets/src/datasets.scss +2 -2
- package/plugins/beta/datasets/src/fetch/createDynamicSource.js +8 -8
- package/plugins/beta/datasets/src/{DatasetsInit.jsx → initialise/DatasetsInit.jsx} +19 -21
- package/plugins/beta/datasets/src/{defaults.js → initialise/defaults.js} +17 -2
- package/plugins/beta/datasets/src/initialise/defaults.test.js +127 -0
- package/plugins/beta/datasets/src/{datasets.js → initialise/initialiseDatasets.js} +30 -26
- package/plugins/beta/datasets/src/initialise/initialiseDatasets.test.js +273 -0
- package/plugins/beta/datasets/src/manifest.js +8 -6
- package/plugins/beta/datasets/src/reducers/__data__/demoDatasets.js +253 -0
- package/plugins/beta/datasets/src/reducers/datasetsToMenu.js +83 -0
- package/plugins/beta/datasets/src/reducers/datasetsToMenu.test.js +84 -0
- package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.js +41 -0
- package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.test.js +117 -0
- package/plugins/beta/datasets/src/reducers/pluginState.js +206 -0
- package/plugins/beta/datasets/src/reducers/pluginState.test.js +386 -0
- package/plugins/beta/datasets/src/registry/__mocks__/datasetRegistry.js +29 -0
- package/plugins/beta/datasets/src/registry/createDataset.js +5 -0
- package/plugins/beta/datasets/src/registry/dataset.js +176 -0
- package/plugins/beta/datasets/src/registry/dataset.test.js +497 -0
- package/plugins/beta/datasets/src/registry/datasetDefinitionCache.js +35 -0
- package/plugins/beta/datasets/src/registry/datasetRegistry.js +116 -0
- package/plugins/beta/datasets/src/registry/datasetRegistry.test.js +335 -0
- package/plugins/beta/datasets/src/registry/dynamicGeoJson.js +17 -0
- package/plugins/beta/datasets/src/registry/globalDataset.js +36 -0
- 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 +8 -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/events.js +13 -1
- package/plugins/beta/draw-ml/src/index.js +1 -1
- package/plugins/beta/draw-ml/src/manifest.js +3 -0
- package/plugins/beta/draw-ml/src/modes/createDrawMode.js +21 -12
- package/plugins/beta/draw-ml/src/modes/editVertexMode.js +4 -0
- 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/edit/keyboardHandler.js +4 -2
- 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 +3 -5
- package/plugins/interact/src/InteractInit.test.js +1 -3
- package/plugins/interact/src/events.js +2 -29
- package/plugins/interact/src/events.test.js +4 -102
- package/plugins/interact/src/hooks/useAttachEvents.js +3 -4
- package/plugins/interact/src/hooks/useAttachEvents.test.js +1 -3
- 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 +15 -12
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +28 -29
- package/plugins/interact/src/manifest.js +0 -35
- package/plugins/interact/src/manifest.test.js +3 -43
- 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/esri/dist/esm/im-esri-provider.js +1 -1
- package/providers/beta/esri/src/esriProvider.js +4 -0
- 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/src/openlayersProvider.js +4 -0
- package/providers/beta/openlayers/src/openlayersProvider.test.js +5 -0
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/src/maplibreProvider.js +6 -4
- package/providers/maplibre/src/maplibreProvider.test.js +5 -0
- package/providers/maplibre/src/utils/highlightFeatures.js +74 -25
- package/providers/maplibre/src/utils/highlightFeatures.test.js +132 -0
- package/providers/maplibre/src/utils/patternImages.js +2 -4
- package/providers/maplibre/src/utils/symbolImages.js +1 -4
- package/rollup.esm.mjs +2 -8
- package/src/App/components/CrossHair/CrossHair.module.scss +8 -8
- package/src/App/components/MapButton/MapButton.jsx +16 -1
- package/src/App/components/MapButton/MapButton.module.scss +12 -1
- package/src/App/components/MapButton/MapButton.test.jsx +40 -1
- package/src/App/hooks/useContinueEnabledEvaluator.js +31 -0
- package/src/App/hooks/useContinueEnabledEvaluator.test.js +118 -0
- package/src/App/hooks/useEvaluateProp.js +15 -15
- package/src/App/hooks/useEvaluateProp.test.js +80 -76
- package/src/App/hooks/useLayoutMeasurements.js +11 -5
- package/src/App/hooks/useResizeObserver.js +15 -2
- package/src/App/hooks/useResizeObserver.test.js +1 -0
- package/src/App/renderer/PluginInits.jsx +2 -0
- package/src/App/renderer/mapButtons.js +21 -1
- package/src/App/renderer/mapButtons.test.js +46 -1
- package/src/App/store/appReducer.js +1 -1
- package/src/App/store/appReducer.test.js +37 -0
- package/src/InteractiveMap/InteractiveMap.js +14 -4
- package/src/InteractiveMap/InteractiveMap.test.js +11 -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/appConfig.js +25 -0
- package/src/config/appConfig.test.js +50 -0
- package/src/config/defaults.js +1 -0
- package/src/config/events.js +21 -3
- package/src/scss/settings/_dimensions.scss +1 -0
- package/src/services/patternRegistry.js +8 -4
- package/src/types.js +7 -0
- package/src/utils/patternUtils.js +1 -2
- package/src/utils/patternUtils.test.js +9 -0
- package/webpack.dev.mjs +10 -0
- package/webpack.umd.mjs +1 -1
- package/docs/assets/images/screens-blue.jpg +0 -0
- package/docs/assets/images/screens-white.jpg +0 -0
- package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +0 -1
- package/plugins/beta/datasets/dist/adapters/maplibre/esm/index.js +0 -1
- package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +0 -2
- package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +0 -2
- package/plugins/beta/datasets/dist/umd/maplibre/index.js +0 -2
- package/plugins/beta/datasets/src/adapters/maplibre/index.js +0 -18
- package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +0 -75
- package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +0 -27
- package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +0 -31
- package/plugins/beta/datasets/src/components/KeySvg.jsx +0 -24
- package/plugins/beta/datasets/src/components/KeySvgLine.jsx +0 -19
- package/plugins/beta/datasets/src/components/KeySvgRect.jsx +0 -22
- package/plugins/beta/datasets/src/index.maplibre.umd.js +0 -23
- package/plugins/beta/datasets/src/panels/Key.jsx +0 -62
- package/plugins/beta/datasets/src/panels/Layers.jsx +0 -141
- package/plugins/beta/datasets/src/reducer.js +0 -219
- package/plugins/beta/datasets/src/reducers/keyReducer.js +0 -34
- package/plugins/beta/datasets/src/utils/filters.js +0 -34
- package/plugins/beta/datasets/src/utils/mergeSublayer.js +0 -86
- package/plugins/beta/draw-ml/dist/css/index.css +0 -21
- /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-maplibre-adapter.js.LICENSE.txt → im-datasets-ml-adapter.js.LICENSE.txt} +0 -0
- /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-plugin.js.LICENSE.txt → im-datasets-plugin.js.LICENSE.txt} +0 -0
- /package/plugins/beta/datasets/dist/umd/{maplibre/index.js.LICENSE.txt → index.js.LICENSE.txt} +0 -0
- /package/plugins/beta/datasets/src/components/{EmptyKey.jsx → Key/EmptyKey.jsx} +0 -0
- /package/plugins/beta/datasets/src/components/{EmptyKey.test.jsx → Key/EmptyKey.test.jsx} +0 -0
- /package/plugins/beta/datasets/src/{panels → components/Key}/Key.module.scss +0 -0
- /package/plugins/beta/datasets/src/components/{svgProperties.js → Key/svgProperties.js} +0 -0
|
@@ -2,11 +2,10 @@ import { attachEvents } from './events.js'
|
|
|
2
2
|
|
|
3
3
|
const MOCK_POINT = { x: 1, y: 2 }
|
|
4
4
|
const MOCK_COORDS = [1, 2]
|
|
5
|
-
const INTERACT_DONE = 'interact:done'
|
|
6
5
|
|
|
7
6
|
const createParams = () => {
|
|
8
7
|
const appState = { layoutRefs: { viewportRef: { current: document.body } }, disabledButtons: new Set() }
|
|
9
|
-
const pluginState = { dispatch: jest.fn(),
|
|
8
|
+
const pluginState = { dispatch: jest.fn(), selectedFeatures: [], selectedMarkers: [], multiSelect: false }
|
|
10
9
|
const clickReadyRef = { current: false }
|
|
11
10
|
return {
|
|
12
11
|
appState,
|
|
@@ -18,11 +17,10 @@ const createParams = () => {
|
|
|
18
17
|
markers: { remove: jest.fn(), getMarker: jest.fn(() => null) },
|
|
19
18
|
crossHair: { getDetail: jest.fn(() => ({ point: { x: 0, y: 0 }, coords: [0, 0] })) }
|
|
20
19
|
},
|
|
21
|
-
buttonConfig: {
|
|
20
|
+
buttonConfig: { selectAtTarget: {} },
|
|
22
21
|
events: { MAP_CLICK: 'map:click' },
|
|
23
22
|
eventBus: { on: jest.fn(), off: jest.fn(), emit: jest.fn() },
|
|
24
|
-
handleInteraction: jest.fn()
|
|
25
|
-
closeApp: jest.fn()
|
|
23
|
+
handleInteraction: jest.fn()
|
|
26
24
|
}
|
|
27
25
|
}
|
|
28
26
|
|
|
@@ -109,102 +107,6 @@ describe('attachEvents — click handling', () => {
|
|
|
109
107
|
})
|
|
110
108
|
})
|
|
111
109
|
|
|
112
|
-
describe('attachEvents — button actions', () => {
|
|
113
|
-
let cleanup = null
|
|
114
|
-
|
|
115
|
-
beforeEach(() => { jest.useFakeTimers() })
|
|
116
|
-
afterEach(() => { cleanup?.(); jest.useRealTimers() })
|
|
117
|
-
|
|
118
|
-
it('selectDone emits correct payload and respects closeOnAction', () => {
|
|
119
|
-
const params = createParams()
|
|
120
|
-
cleanup = attachEvents(params)
|
|
121
|
-
|
|
122
|
-
params.mapState.markers.getMarker.mockReturnValue({ coords: MOCK_COORDS })
|
|
123
|
-
params.buttonConfig.selectDone.onClick()
|
|
124
|
-
expect(params.closeApp).toHaveBeenCalled()
|
|
125
|
-
|
|
126
|
-
params.closeApp.mockClear()
|
|
127
|
-
params.pluginState.closeOnAction = false
|
|
128
|
-
params.buttonConfig.selectDone.onClick()
|
|
129
|
-
expect(params.closeApp).not.toHaveBeenCalled()
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
it('selectDone emits selectedFeatures and selectionBounds when no marker', () => {
|
|
133
|
-
const params = createParams()
|
|
134
|
-
cleanup = attachEvents(params)
|
|
135
|
-
|
|
136
|
-
params.pluginState.selectedFeatures = [{ id: 'f1' }]
|
|
137
|
-
params.pluginState.selectionBounds = { sw: [0, 0], ne: [1, 1] }
|
|
138
|
-
params.buttonConfig.selectDone.onClick()
|
|
139
|
-
|
|
140
|
-
expect(params.eventBus.emit).toHaveBeenCalledWith(INTERACT_DONE, {
|
|
141
|
-
selectedFeatures: [{ id: 'f1' }],
|
|
142
|
-
selectionBounds: { sw: [0, 0], ne: [1, 1] }
|
|
143
|
-
})
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('selectDone includes selectedMarkers in payload when present', () => {
|
|
147
|
-
const params = createParams()
|
|
148
|
-
cleanup = attachEvents(params)
|
|
149
|
-
|
|
150
|
-
params.pluginState.selectedMarkers = ['m1', 'm2']
|
|
151
|
-
params.buttonConfig.selectDone.onClick()
|
|
152
|
-
|
|
153
|
-
expect(params.eventBus.emit).toHaveBeenCalledWith(INTERACT_DONE,
|
|
154
|
-
expect.objectContaining({ selectedMarkers: ['m1', 'm2'] })
|
|
155
|
-
)
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
it('selectDone omits selectedMarkers from payload when empty', () => {
|
|
159
|
-
const params = createParams()
|
|
160
|
-
cleanup = attachEvents(params)
|
|
161
|
-
|
|
162
|
-
params.buttonConfig.selectDone.onClick()
|
|
163
|
-
|
|
164
|
-
const payload = params.eventBus.emit.mock.calls.find(c => c[0] === INTERACT_DONE)[1]
|
|
165
|
-
expect(payload).not.toHaveProperty('selectedMarkers')
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
it('does not emit or closeApp if selectDone button is disabled', () => {
|
|
169
|
-
const params = createParams()
|
|
170
|
-
cleanup = attachEvents(params)
|
|
171
|
-
|
|
172
|
-
params.appState.disabledButtons.add('selectDone')
|
|
173
|
-
params.buttonConfig.selectDone.onClick()
|
|
174
|
-
|
|
175
|
-
expect(params.eventBus.emit).not.toHaveBeenCalled()
|
|
176
|
-
expect(params.closeApp).not.toHaveBeenCalled()
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('selectCancel emits cancel and respects closeOnAction', () => {
|
|
180
|
-
const params = createParams()
|
|
181
|
-
cleanup = attachEvents(params)
|
|
182
|
-
|
|
183
|
-
params.buttonConfig.selectCancel.onClick()
|
|
184
|
-
expect(params.closeApp).toHaveBeenCalled()
|
|
185
|
-
|
|
186
|
-
cleanup()
|
|
187
|
-
const params2 = createParams()
|
|
188
|
-
cleanup = attachEvents(params2)
|
|
189
|
-
params2.pluginState.closeOnAction = false
|
|
190
|
-
params2.buttonConfig.selectCancel.onClick()
|
|
191
|
-
expect(params2.closeApp).not.toHaveBeenCalled()
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
it('respects default closeOnAction when value is nullish', () => {
|
|
195
|
-
const params = createParams()
|
|
196
|
-
params.pluginState.closeOnAction = null
|
|
197
|
-
cleanup = attachEvents(params)
|
|
198
|
-
|
|
199
|
-
params.buttonConfig.selectDone.onClick()
|
|
200
|
-
expect(params.closeApp).toHaveBeenCalledTimes(1)
|
|
201
|
-
|
|
202
|
-
params.closeApp.mockClear()
|
|
203
|
-
params.buttonConfig.selectCancel.onClick()
|
|
204
|
-
expect(params.closeApp).toHaveBeenCalledTimes(1)
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
|
|
208
110
|
describe('attachEvents — programmatic selection', () => {
|
|
209
111
|
let cleanup = null
|
|
210
112
|
|
|
@@ -232,7 +134,7 @@ describe('attachEvents — cleanup', () => {
|
|
|
232
134
|
const params = createParams()
|
|
233
135
|
const cleanup = attachEvents(params)
|
|
234
136
|
cleanup()
|
|
235
|
-
|
|
137
|
+
expect(params.buttonConfig.selectAtTarget.onClick).toBeNull()
|
|
236
138
|
jest.useRealTimers()
|
|
237
139
|
})
|
|
238
140
|
})
|
|
@@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react'
|
|
|
2
2
|
import { EVENTS } from '../../../../src/config/events.js'
|
|
3
3
|
import { attachEvents } from '../events.js'
|
|
4
4
|
|
|
5
|
-
export function useAttachEvents ({ pluginState, appState, mapState, buttonConfig, eventBus, handleInteraction
|
|
5
|
+
export function useAttachEvents ({ pluginState, appState, mapState, buttonConfig, eventBus, handleInteraction }) {
|
|
6
6
|
// Refs updated synchronously each render — keeps callbacks fresh without re-attaching events
|
|
7
7
|
const handleInteractionRef = useRef(handleInteraction)
|
|
8
8
|
handleInteractionRef.current = handleInteraction
|
|
@@ -37,10 +37,9 @@ export function useAttachEvents ({ pluginState, appState, mapState, buttonConfig
|
|
|
37
37
|
events: EVENTS,
|
|
38
38
|
eventBus,
|
|
39
39
|
handleInteraction: (event) => handleInteractionRef.current(event),
|
|
40
|
-
clickReadyRef
|
|
41
|
-
closeApp
|
|
40
|
+
clickReadyRef
|
|
42
41
|
})
|
|
43
42
|
|
|
44
43
|
return cleanupEvents
|
|
45
|
-
}, [pluginState.enabled, buttonConfig, eventBus
|
|
44
|
+
}, [pluginState.enabled, buttonConfig, eventBus])
|
|
46
45
|
}
|
|
@@ -21,7 +21,6 @@ beforeEach(() => {
|
|
|
21
21
|
mapState: { mapStyle: {} },
|
|
22
22
|
buttonConfig: {},
|
|
23
23
|
eventBus: { emit: jest.fn() },
|
|
24
|
-
closeApp: jest.fn(),
|
|
25
24
|
handleInteraction: handleInteractionMock
|
|
26
25
|
}
|
|
27
26
|
})
|
|
@@ -36,8 +35,7 @@ describe('useAttachEvents', () => {
|
|
|
36
35
|
mapState: props.mapState,
|
|
37
36
|
buttonConfig: props.buttonConfig,
|
|
38
37
|
events: EVENTS,
|
|
39
|
-
eventBus: props.eventBus
|
|
40
|
-
closeApp: props.closeApp
|
|
38
|
+
eventBus: props.eventBus
|
|
41
39
|
}))
|
|
42
40
|
|
|
43
41
|
const { getAppState, getPluginState, handleInteraction } = attachEvents.mock.calls.at(-1)[0]
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef } from 'react'
|
|
2
|
-
import { getInterfaceType, subscribeToInterfaceChangesImmediate } from '../../../../src/utils/detectInterfaceType.js'
|
|
3
2
|
|
|
4
3
|
export function useCrossHairVisibility ({ crossHair, enabled, selectMarkerOnly, appState }) {
|
|
5
4
|
const enabledRef = useRef(enabled)
|
|
@@ -9,9 +8,11 @@ export function useCrossHairVisibility ({ crossHair, enabled, selectMarkerOnly,
|
|
|
9
8
|
const crossHairRef = useRef(crossHair)
|
|
10
9
|
crossHairRef.current = crossHair
|
|
11
10
|
const listboxFocusRef = useRef(false)
|
|
11
|
+
const interfaceTypeRef = useRef(appState.interfaceType)
|
|
12
|
+
interfaceTypeRef.current = appState.interfaceType
|
|
12
13
|
|
|
13
14
|
const updateCrossHair = useCallback(() => {
|
|
14
|
-
const type =
|
|
15
|
+
const type = interfaceTypeRef.current
|
|
15
16
|
const isToK = ['touch', 'keyboard'].includes(type)
|
|
16
17
|
if (enabledRef.current && !listboxFocusRef.current && isToK && !(type === 'touch' && selectMarkerOnlyRef.current)) {
|
|
17
18
|
crossHairRef.current.fixAtCenter()
|
|
@@ -20,17 +21,10 @@ export function useCrossHairVisibility ({ crossHair, enabled, selectMarkerOnly,
|
|
|
20
21
|
}
|
|
21
22
|
}, [])
|
|
22
23
|
|
|
23
|
-
// Toggle target marker visibility on enabled/interactionModes changes
|
|
24
24
|
useEffect(() => {
|
|
25
25
|
updateCrossHair()
|
|
26
|
-
}, [enabled, selectMarkerOnly, updateCrossHair])
|
|
26
|
+
}, [enabled, selectMarkerOnly, appState.interfaceType, updateCrossHair])
|
|
27
27
|
|
|
28
|
-
// Toggle target marker visibility immediately on interface type change (no 150ms React delay)
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
return subscribeToInterfaceChangesImmediate(updateCrossHair)
|
|
31
|
-
}, [updateCrossHair])
|
|
32
|
-
|
|
33
|
-
// Hide crosshair when listbox has focus
|
|
34
28
|
useEffect(() => {
|
|
35
29
|
const container = appState.layoutRefs?.appContainerRef?.current
|
|
36
30
|
if (!container) { return undefined }
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import { act, renderHook } from '@testing-library/react'
|
|
2
2
|
import { useCrossHairVisibility } from './useCrossHairVisibility.js'
|
|
3
|
-
import { getInterfaceType } from '../../../../src/utils/detectInterfaceType.js'
|
|
4
|
-
|
|
5
|
-
jest.mock('../../../../src/utils/detectInterfaceType.js', () => ({
|
|
6
|
-
getInterfaceType: jest.fn(() => 'mouse'),
|
|
7
|
-
subscribeToInterfaceChangesImmediate: jest.fn(() => () => {})
|
|
8
|
-
}))
|
|
9
3
|
|
|
10
4
|
let crossHair
|
|
11
5
|
let appState
|
|
12
6
|
|
|
13
7
|
beforeEach(() => {
|
|
14
|
-
jest.clearAllMocks()
|
|
15
8
|
crossHair = { fixAtCenter: jest.fn(), hide: jest.fn() }
|
|
16
|
-
appState = {
|
|
17
|
-
|
|
9
|
+
appState = {
|
|
10
|
+
interfaceType: 'mouse',
|
|
11
|
+
layoutRefs: { appContainerRef: { current: document.createElement('div') } }
|
|
12
|
+
}
|
|
18
13
|
})
|
|
19
14
|
|
|
20
15
|
describe('useCrossHairVisibility', () => {
|
|
@@ -26,7 +21,7 @@ describe('useCrossHairVisibility', () => {
|
|
|
26
21
|
|
|
27
22
|
it('shows crosshair on touch/keyboard and hides when listbox has focus', () => {
|
|
28
23
|
const container = appState.layoutRefs.appContainerRef.current
|
|
29
|
-
|
|
24
|
+
appState.interfaceType = 'touch'
|
|
30
25
|
renderHook(() => useCrossHairVisibility({ crossHair, enabled: true, selectMarkerOnly: false, appState }))
|
|
31
26
|
|
|
32
27
|
expect(crossHair.fixAtCenter).toHaveBeenCalled()
|
|
@@ -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
|
/**
|
|
@@ -166,6 +160,15 @@ const resolveContiguousDispatch = ({ featureId, feature, config, selectedFeature
|
|
|
166
160
|
return true
|
|
167
161
|
}
|
|
168
162
|
|
|
163
|
+
const logDebugFeatures = (coords, features) => {
|
|
164
|
+
const [lng, lat] = coords
|
|
165
|
+
console.groupCollapsed(`[interact] click (${lng.toFixed(4)}, ${lat.toFixed(4)}) — ${features.length} feature${features.length !== 1 ? 's' : ''}`) // NOSONAR
|
|
166
|
+
features.forEach(f => { // NOSONAR
|
|
167
|
+
console.log({ layer: f.layer.id, type: f.layer.type, id: f.id ?? '—', sourceLayer: f.sourceLayer ?? '—', properties: f.properties }) // NOSONAR
|
|
168
|
+
})
|
|
169
|
+
console.groupEnd() // NOSONAR
|
|
170
|
+
}
|
|
171
|
+
|
|
169
172
|
/**
|
|
170
173
|
* Core interaction hook. Processes map clicks in fixed priority order:
|
|
171
174
|
* selectMarker → selectFeature → placeMarker (fallback).
|
|
@@ -185,7 +188,7 @@ const useHandleInteraction = ({ mapProvider, layers, interactionModes, multiSele
|
|
|
185
188
|
return useCallback(({ point, coords }) => {
|
|
186
189
|
const debugFeatures = debug ? getFeaturesAtPoint(mapProvider, point, { radius: tolerance }) : null
|
|
187
190
|
if (debugFeatures) {
|
|
188
|
-
|
|
191
|
+
logDebugFeatures(coords, debugFeatures)
|
|
189
192
|
}
|
|
190
193
|
if (interactionModes.includes('selectMarker')) {
|
|
191
194
|
const markerHit = findMarkerAtPoint(markers, point, scale)
|
|
@@ -211,7 +214,7 @@ export const useInteractionHandlers = ({ mapState, pluginState, services, mapPro
|
|
|
211
214
|
const {
|
|
212
215
|
dispatch, layers, interactionModes, multiSelect, contiguous,
|
|
213
216
|
marker: markerOptions, tolerance, selectedFeatures, selectedMarkers,
|
|
214
|
-
|
|
217
|
+
deselectOnClickOutside
|
|
215
218
|
} = pluginState
|
|
216
219
|
const { eventBus } = services
|
|
217
220
|
const layerConfigMap = buildLayerConfigMap(layers)
|
|
@@ -263,6 +266,6 @@ export const useInteractionHandlers = ({ mapState, pluginState, services, mapPro
|
|
|
263
266
|
scale
|
|
264
267
|
})
|
|
265
268
|
|
|
266
|
-
useSelectionChangeEmitter(eventBus, selectedFeatures, selectedMarkers
|
|
269
|
+
useSelectionChangeEmitter(eventBus, selectedFeatures, selectedMarkers)
|
|
267
270
|
return { handleInteraction }
|
|
268
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
|
}),
|
|
@@ -423,18 +403,37 @@ it('skips emission when selection remains empty after being cleared', () => {
|
|
|
423
403
|
/* ------------------------------------------------------------------ */
|
|
424
404
|
|
|
425
405
|
it('logs features when debug mode is enabled', () => {
|
|
406
|
+
const groupSpy = jest.spyOn(console, 'groupCollapsed').mockImplementation(() => {})
|
|
426
407
|
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
|
|
408
|
+
const groupEndSpy = jest.spyOn(console, 'groupEnd').mockImplementation(() => {})
|
|
427
409
|
|
|
428
410
|
const { result } = setup({ debug: true })
|
|
429
411
|
|
|
430
412
|
click(result)
|
|
431
413
|
|
|
432
|
-
expect(
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
)
|
|
414
|
+
expect(groupSpy).toHaveBeenCalledWith(expect.stringContaining('[interact] click ('))
|
|
415
|
+
expect(groupSpy).toHaveBeenCalledWith(expect.stringContaining('1 feature'))
|
|
416
|
+
expect(logSpy).toHaveBeenCalledWith(expect.objectContaining({ layer: 'parcels' }))
|
|
417
|
+
expect(groupEndSpy).toHaveBeenCalled()
|
|
436
418
|
|
|
419
|
+
groupSpy.mockRestore()
|
|
437
420
|
logSpy.mockRestore()
|
|
421
|
+
groupEndSpy.mockRestore()
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
it('uses plural label when multiple features are found', () => {
|
|
425
|
+
const groupSpy = jest.spyOn(console, 'groupCollapsed').mockImplementation(() => {})
|
|
426
|
+
jest.spyOn(console, 'log').mockImplementation(() => {})
|
|
427
|
+
jest.spyOn(console, 'groupEnd').mockImplementation(() => {})
|
|
428
|
+
|
|
429
|
+
featureQueries.getFeaturesAtPoint.mockReturnValue([baseFeature, baseFeature])
|
|
430
|
+
|
|
431
|
+
const { result } = setup({ debug: true })
|
|
432
|
+
click(result)
|
|
433
|
+
|
|
434
|
+
expect(groupSpy).toHaveBeenCalledWith(expect.stringContaining('2 features'))
|
|
435
|
+
|
|
436
|
+
jest.restoreAllMocks()
|
|
438
437
|
})
|
|
439
438
|
|
|
440
439
|
/* ------------------------------------------------------------------ */
|
|
@@ -21,23 +21,6 @@ export const manifest = {
|
|
|
21
21
|
},
|
|
22
22
|
|
|
23
23
|
buttons: [{
|
|
24
|
-
id: 'selectCancel',
|
|
25
|
-
label: 'Back',
|
|
26
|
-
variant: 'tertiary',
|
|
27
|
-
hiddenWhen: ({ appConfig, appState, pluginState }) => !pluginState.enabled || !(['hybrid', 'buttonFirst'].includes(appConfig.behaviour) && appState.isFullscreen),
|
|
28
|
-
mobile: {
|
|
29
|
-
slot: 'actions',
|
|
30
|
-
showLabel: true
|
|
31
|
-
},
|
|
32
|
-
tablet: {
|
|
33
|
-
slot: 'actions',
|
|
34
|
-
showLabel: true
|
|
35
|
-
},
|
|
36
|
-
desktop: {
|
|
37
|
-
slot: 'actions',
|
|
38
|
-
showLabel: true
|
|
39
|
-
}
|
|
40
|
-
}, {
|
|
41
24
|
id: 'selectAtTarget',
|
|
42
25
|
label: 'Select',
|
|
43
26
|
variant: 'primary',
|
|
@@ -54,24 +37,6 @@ export const manifest = {
|
|
|
54
37
|
desktop: {
|
|
55
38
|
slot: 'actions'
|
|
56
39
|
}
|
|
57
|
-
}, {
|
|
58
|
-
id: 'selectDone',
|
|
59
|
-
label: 'Continue',
|
|
60
|
-
variant: 'primary',
|
|
61
|
-
excludeWhen: ({ appState, pluginState }) => !pluginState.enabled || !appState.isFullscreen,
|
|
62
|
-
enableWhen: ({ mapState, pluginState }) => !!mapState.markers.items.some(m => m.id === 'location') || !!pluginState.selectionBounds,
|
|
63
|
-
mobile: {
|
|
64
|
-
slot: 'actions',
|
|
65
|
-
showLabel: true
|
|
66
|
-
},
|
|
67
|
-
tablet: {
|
|
68
|
-
slot: 'actions',
|
|
69
|
-
showLabel: true
|
|
70
|
-
},
|
|
71
|
-
desktop: {
|
|
72
|
-
slot: 'actions',
|
|
73
|
-
showLabel: true
|
|
74
|
-
}
|
|
75
40
|
}],
|
|
76
41
|
|
|
77
42
|
keyboardShortcuts: [
|
|
@@ -18,7 +18,9 @@ describe('manifest', () => {
|
|
|
18
18
|
|
|
19
19
|
it('defines buttons with correct ids', () => {
|
|
20
20
|
const ids = manifest.buttons.map(b => b.id)
|
|
21
|
-
expect(ids).toEqual(expect.arrayContaining(['
|
|
21
|
+
expect(ids).toEqual(expect.arrayContaining(['selectAtTarget']))
|
|
22
|
+
expect(ids).not.toContain('selectDone')
|
|
23
|
+
expect(ids).not.toContain('selectCancel')
|
|
22
24
|
})
|
|
23
25
|
|
|
24
26
|
it('buttons have slots and showLabel', () => {
|
|
@@ -33,54 +35,12 @@ describe('manifest', () => {
|
|
|
33
35
|
})
|
|
34
36
|
|
|
35
37
|
it('button logic functions cover all branches', () => {
|
|
36
|
-
const done = manifest.buttons.find(b => b.id === 'selectDone')
|
|
37
38
|
const atTarget = manifest.buttons.find(b => b.id === 'selectAtTarget')
|
|
38
|
-
const cancel = manifest.buttons.find(b => b.id === 'selectCancel')
|
|
39
|
-
|
|
40
|
-
// selectDone.excludeWhen
|
|
41
|
-
expect(done.excludeWhen({ appState: { isFullscreen: false }, pluginState: { enabled: true } })).toBe(true)
|
|
42
|
-
expect(done.excludeWhen({ appState: { isFullscreen: true }, pluginState: { enabled: true } })).toBe(false)
|
|
43
|
-
|
|
44
|
-
// selectDone.enableWhen
|
|
45
|
-
expect(done.enableWhen({
|
|
46
|
-
mapState: { markers: { items: [{ id: 'location' }] } },
|
|
47
|
-
pluginState: { selectionBounds: null }
|
|
48
|
-
})).toBe(true)
|
|
49
|
-
expect(done.enableWhen({
|
|
50
|
-
mapState: { markers: { items: [] } },
|
|
51
|
-
pluginState: { selectionBounds: null }
|
|
52
|
-
})).toBe(false)
|
|
53
|
-
expect(done.enableWhen({
|
|
54
|
-
mapState: { markers: { items: [] } },
|
|
55
|
-
pluginState: { selectionBounds: { sw: [0, 0], ne: [1, 1] } }
|
|
56
|
-
})).toBe(true)
|
|
57
39
|
|
|
58
40
|
// selectAtTarget.hiddenWhen
|
|
59
41
|
expect(atTarget.hiddenWhen({ appState: { interfaceType: 'pointer' }, pluginState: { enabled: true } })).toBe(true)
|
|
60
42
|
expect(atTarget.hiddenWhen({ appState: { interfaceType: 'touch' }, pluginState: { enabled: true } })).toBe(false)
|
|
61
43
|
expect(atTarget.hiddenWhen({ appState: { interfaceType: 'touch' }, pluginState: { enabled: false } })).toBe(true)
|
|
62
|
-
|
|
63
|
-
// selectCancel.hiddenWhen
|
|
64
|
-
expect(cancel.hiddenWhen({
|
|
65
|
-
appConfig: { behaviour: 'always' },
|
|
66
|
-
appState: { isFullscreen: true },
|
|
67
|
-
pluginState: { enabled: true }
|
|
68
|
-
})).toBe(true)
|
|
69
|
-
expect(cancel.hiddenWhen({
|
|
70
|
-
appConfig: { behaviour: 'hybrid' },
|
|
71
|
-
appState: { isFullscreen: true },
|
|
72
|
-
pluginState: { enabled: true }
|
|
73
|
-
})).toBe(false)
|
|
74
|
-
expect(cancel.hiddenWhen({
|
|
75
|
-
appConfig: { behaviour: 'hybrid' },
|
|
76
|
-
appState: { isFullscreen: false },
|
|
77
|
-
pluginState: { enabled: true }
|
|
78
|
-
})).toBe(true)
|
|
79
|
-
expect(cancel.hiddenWhen({
|
|
80
|
-
appConfig: { behaviour: 'hybrid' },
|
|
81
|
-
appState: { isFullscreen: true },
|
|
82
|
-
pluginState: { enabled: false }
|
|
83
|
-
})).toBe(true)
|
|
84
44
|
})
|
|
85
45
|
|
|
86
46
|
it('keyboardShortcuts array exists', () => {
|