@defra/interactive-map 0.0.21-alpha → 0.0.23-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/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/im-shell.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api/button-definition.md +39 -0
- package/docs/api/context.md +4 -4
- package/docs/api/map-style-config.md +11 -2
- package/docs/api/marker-config.md +38 -7
- package/docs/api/panel-definition.md +7 -3
- package/docs/api/symbol-config.md +10 -26
- package/docs/api/symbol-registry.md +28 -13
- package/docs/api.md +65 -4
- package/docs/assets/css/{docusaurus.css → docusaurus.scss} +18 -12
- package/docs/assets/images/toggle-marker-label.jpg +0 -0
- package/docs/examples/index.mdx +12 -0
- package/docs/examples/toggle-marker-label.mdx +89 -0
- package/docs/plugins/datasets.md +39 -1
- package/docs/plugins/interact.md +4 -11
- package/docusaurus.config.cjs +2 -1
- package/package.json +10 -12
- package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +1 -0
- package/plugins/beta/datasets/dist/css/index.css +2 -2
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +2 -0
- package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +2 -0
- package/plugins/beta/datasets/dist/umd/maplibre/index.js +2 -0
- package/plugins/beta/datasets/src/adapters/maplibre/index.js +1 -1
- package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +5 -5
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +24 -29
- package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +2 -2
- package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +3 -3
- package/plugins/beta/datasets/src/components/KeySvg.jsx +2 -2
- package/plugins/beta/datasets/src/components/KeySvgPattern.jsx +1 -2
- package/plugins/beta/datasets/src/components/svgProperties.js +2 -2
- package/plugins/beta/datasets/src/index.maplibre.umd.js +23 -0
- package/plugins/beta/datasets/src/panels/Key.module.scss +2 -2
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.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/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/dist/umd/index.js +1 -1
- package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -1
- package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -1
- package/plugins/beta/use-location/src/manifest.js +1 -0
- 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 +23 -55
- package/plugins/interact/src/InteractInit.test.js +58 -80
- package/plugins/interact/src/defaults.js +1 -3
- package/plugins/interact/src/hooks/useAttachEvents.js +46 -0
- package/plugins/interact/src/hooks/useAttachEvents.test.js +65 -0
- package/plugins/interact/src/hooks/useCrossHairVisibility.js +47 -0
- package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +54 -0
- package/plugins/interact/src/hooks/useHighlightSync.js +30 -8
- package/plugins/interact/src/hooks/useHighlightSync.test.js +127 -80
- package/plugins/interact/src/hooks/useInteractionHandlers.js +18 -17
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +22 -61
- package/plugins/interact/src/hooks/useMapItemList.js +187 -0
- package/plugins/interact/src/hooks/useMapItemList.test.js +584 -0
- package/plugins/interact/src/index.js +0 -2
- package/plugins/interact/src/index.test.js +0 -2
- package/plugins/interact/src/manifest.js +37 -6
- package/plugins/interact/src/reducer.js +8 -4
- package/plugins/interact/src/reducer.test.js +18 -3
- package/plugins/interact/src/utils/buildStylesMap.js +21 -10
- package/plugins/interact/src/utils/buildStylesMap.test.js +29 -29
- package/plugins/interact/src/utils/spatial.js +0 -15
- package/plugins/interact/src/utils/spatial.test.js +1 -13
- package/plugins/search/dist/css/index.css +1 -1
- package/plugins/search/dist/esm/im-search-plugin.js +1 -1
- package/plugins/search/dist/umd/im-search-plugin.js +1 -1
- package/plugins/search/src/components/CloseButton/CloseButton.module.scss +2 -0
- package/plugins/search/src/components/OpenButton/OpenButton.jsx +1 -1
- package/plugins/search/src/components/OpenButton/OpenButton.module.scss +3 -0
- package/plugins/search/src/search.scss +1 -0
- package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
- package/providers/beta/esri/src/esriProvider.js +4 -0
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/esm/index.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/index.js +1 -1
- package/providers/maplibre/src/index.js +7 -1
- package/providers/maplibre/src/index.test.js +15 -2
- package/providers/maplibre/src/mapEvents.js +11 -5
- package/providers/maplibre/src/mapEvents.test.js +14 -3
- package/providers/maplibre/src/maplibreProvider.js +18 -11
- package/providers/maplibre/src/maplibreProvider.test.js +43 -36
- package/providers/maplibre/src/utils/highlightFeatures.js +102 -57
- package/providers/maplibre/src/utils/highlightFeatures.test.js +162 -51
- package/providers/maplibre/src/utils/maplibreFixes.js +10 -0
- package/providers/maplibre/src/utils/maplibreFixes.test.js +31 -0
- package/providers/maplibre/src/utils/patternImages.js +10 -12
- package/providers/maplibre/src/utils/patternImages.test.js +145 -167
- package/providers/maplibre/src/utils/rasteriseToImageData.js +1 -4
- package/providers/maplibre/src/utils/rasteriseToImageData.test.js +23 -24
- package/providers/maplibre/src/utils/symbolImages.js +23 -86
- package/providers/maplibre/src/utils/symbolImages.test.js +60 -105
- package/rollup.esm.mjs +1 -1
- package/src/App/components/Actions/Actions.module.scss +1 -0
- package/src/App/components/Actions/Actions.test.jsx +17 -4
- package/src/App/components/Hints/Hints.jsx +52 -0
- package/src/App/components/Hints/Hints.module.scss +37 -0
- package/src/App/components/Hints/Hints.test.jsx +104 -0
- package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +57 -19
- package/src/App/components/KeyboardHelp/KeyboardHelp.module.scss +10 -0
- package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +140 -31
- package/src/App/components/Markers/LabelMarker.jsx +14 -0
- package/src/App/components/Markers/LabelMarker.test.jsx +36 -0
- package/src/App/components/Markers/Markers.jsx +44 -75
- package/src/App/components/Markers/Markers.module.scss +58 -0
- package/src/App/components/Markers/Markers.test.jsx +90 -113
- package/src/App/components/Markers/SymbolLabelMarker.jsx +43 -0
- package/src/App/components/Markers/SymbolLabelMarker.test.jsx +56 -0
- package/src/App/components/Markers/SymbolMarker.jsx +31 -0
- package/src/App/components/Markers/SymbolMarker.test.jsx +72 -0
- package/src/App/components/Panel/Panel.jsx +1 -1
- package/src/App/components/Panel/Panel.module.scss +1 -0
- package/src/App/components/PopupMenu/PopupMenu.test.jsx +117 -1
- package/src/App/components/PopupMenu/usePopupMenu.js +132 -85
- package/src/App/components/Tabs/Tabs.jsx +68 -0
- package/src/App/components/Tabs/Tabs.module.scss +57 -0
- package/src/App/components/Tabs/Tabs.test.jsx +121 -0
- package/src/App/components/Tooltip/Tooltip.module.scss +1 -1
- package/src/App/components/Viewport/Features.jsx +35 -0
- package/src/App/components/Viewport/Features.module.scss +31 -0
- package/src/App/components/Viewport/Features.test.jsx +124 -0
- package/src/App/components/Viewport/MapController.jsx +1 -1
- package/src/App/components/Viewport/MapStatus.jsx +0 -4
- package/src/App/components/Viewport/Viewport.jsx +35 -29
- package/src/App/components/Viewport/Viewport.module.scss +7 -21
- package/src/App/components/Viewport/Viewport.test.jsx +108 -65
- package/src/App/controls/keyboardActions.js +9 -3
- package/src/App/controls/keyboardActions.test.js +122 -87
- package/src/App/controls/keyboardShortcuts.js +1 -14
- package/src/App/hooks/useFeatureFocus.js +199 -0
- package/src/App/hooks/useFeatureFocus.test.js +635 -0
- package/src/App/hooks/useFeatureItems.js +30 -0
- package/src/App/hooks/useFeatureItems.test.js +97 -0
- package/src/App/hooks/useFocusVisible.js +4 -7
- package/src/App/hooks/useFocusVisible.test.js +6 -6
- package/src/App/hooks/useKeyboardHint.js +9 -34
- package/src/App/hooks/useKeyboardHint.test.js +34 -84
- package/src/App/hooks/useKeyboardShortcuts.js +8 -5
- package/src/App/hooks/useKeyboardShortcuts.test.js +16 -12
- package/src/App/hooks/useLayoutMeasurements.js +10 -4
- package/src/App/hooks/useLayoutMeasurements.test.js +30 -0
- package/src/App/hooks/useMapURLSync.js +3 -3
- package/src/App/hooks/useMapURLSync.test.js +20 -3
- package/src/App/hooks/useMarkerCursor.js +33 -0
- package/src/App/hooks/useMarkerCursor.test.js +87 -0
- package/src/App/hooks/useMarkersAPI.js +32 -9
- package/src/App/hooks/useMarkersAPI.test.js +162 -94
- package/src/App/layout/Layout.jsx +2 -0
- package/src/App/layout/Layout.test.jsx +3 -0
- package/src/App/registry/keyboardShortcutRegistry.js +3 -2
- package/src/App/registry/keyboardShortcutRegistry.test.js +6 -8
- package/src/App/renderer/mapButtons.js +5 -1
- package/src/App/renderer/mapButtons.test.js +37 -1
- package/src/App/store/ServiceProvider.jsx +4 -1
- package/src/App/store/ServiceProvider.test.jsx +11 -0
- package/src/App/store/appActionsMap.js +5 -1
- package/src/App/store/appActionsMap.test.js +13 -0
- package/src/App/store/appReducer.js +1 -0
- package/src/App/store/mapActionsMap.js +2 -1
- package/src/InteractiveMap/InteractiveMap.js +13 -1
- package/src/InteractiveMap/InteractiveMap.test.js +6 -4
- package/src/InteractiveMap/behaviourController.js +1 -2
- package/src/InteractiveMap/buttonManager.js +1 -3
- package/src/InteractiveMap/buttonManager.test.js +16 -8
- package/src/InteractiveMap/domStateManager.js +2 -3
- package/src/InteractiveMap/historyManager.js +4 -3
- package/src/config/appConfig.js +3 -1
- package/src/config/appConfig.test.js +4 -0
- package/src/config/defaults.js +3 -2
- package/src/config/events.js +8 -0
- package/src/config/mapTheme.js +8 -4
- package/src/config/mergeConfig.js +8 -2
- package/src/config/mergeConfig.test.js +19 -0
- package/src/config/symbolConfig.js +23 -16
- package/src/scss/main.scss +3 -0
- package/src/scss/tools/_border-focus.scss +0 -1
- package/src/services/eventBus.js +15 -0
- package/src/services/eventBus.test.js +11 -0
- package/src/services/hints.js +47 -0
- package/src/services/hints.test.js +110 -0
- package/src/services/patternRegistry.js +69 -1
- package/src/services/patternRegistry.test.js +112 -1
- package/src/services/reverseGeocode.js +4 -0
- package/src/services/reverseGeocode.test.js +11 -1
- package/src/services/symbolRegistry.js +152 -17
- package/src/services/symbolRegistry.test.js +141 -31
- package/src/types.js +93 -16
- package/src/utils/constrainKeyboardFocus.js +5 -5
- package/src/utils/detectInterfaceType.js +27 -5
- package/src/utils/detectInterfaceType.test.js +29 -3
- package/src/utils/mapStateSync.js +2 -2
- package/src/utils/mapStateSync.test.js +18 -0
- package/src/utils/patternUtils.js +4 -67
- package/src/utils/patternUtils.test.js +1 -123
- package/src/utils/symbolUtils.js +11 -13
- package/src/utils/symbolUtils.test.js +18 -26
- package/webpack.dev.mjs +4 -0
- package/webpack.umd.mjs +9 -6
- package/plugins/beta/datasets/adapters/maplibre/dist/esm/im-datasets-ml-adapter.js +0 -1
- package/plugins/beta/datasets/adapters/maplibre/dist/umd/im-datasets-ml-adapter.js +0 -2
- package/plugins/beta/datasets/adapters/maplibre/dist/umd/index.js +0 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +0 -2
- package/plugins/beta/datasets/dist/umd/index.js +0 -2
- package/plugins/interact/dist/css/index.css +0 -1
- package/plugins/interact/src/interact.scss +0 -24
- /package/plugins/beta/datasets/{adapters/maplibre/dist → dist/adapters/maplibre}/esm/index.js +0 -0
- /package/plugins/beta/datasets/{adapters/maplibre/dist/umd/im-datasets-ml-adapter.js.LICENSE.txt → dist/umd/maplibre/im-datasets-maplibre-adapter.js.LICENSE.txt} +0 -0
- /package/plugins/beta/datasets/dist/umd/{im-datasets-plugin.js.LICENSE.txt → maplibre/im-datasets-plugin.js.LICENSE.txt} +0 -0
- /package/plugins/beta/datasets/dist/umd/{index.js.LICENSE.txt → maplibre/index.js.LICENSE.txt} +0 -0
|
@@ -38,7 +38,8 @@ describe('mapButtons module', () => {
|
|
|
38
38
|
pressedButtons: new Set(),
|
|
39
39
|
expandedButtons: new Set(),
|
|
40
40
|
buttonConfig: {},
|
|
41
|
-
panelConfig: {}
|
|
41
|
+
panelConfig: {},
|
|
42
|
+
layoutRefs: { viewportRef: { current: { focus: jest.fn() } } }
|
|
42
43
|
}
|
|
43
44
|
appState.buttonConfig = ({})
|
|
44
45
|
getPanelConfig.mockReturnValue({})
|
|
@@ -227,6 +228,41 @@ describe('mapButtons module', () => {
|
|
|
227
228
|
// dispatch should NOT be called because panelId is missing
|
|
228
229
|
expect(appState.dispatch).not.toHaveBeenCalled()
|
|
229
230
|
})
|
|
231
|
+
|
|
232
|
+
it('focuses viewport via requestAnimationFrame after onClick when not toggle and no keepFocus', () => {
|
|
233
|
+
const rafSpy = jest.spyOn(global, 'requestAnimationFrame').mockImplementation(cb => { cb(); return 1 })
|
|
234
|
+
const viewportFocusSpy = jest.spyOn(appState.layoutRefs.viewportRef.current, 'focus')
|
|
235
|
+
const onClick = jest.fn()
|
|
236
|
+
render({ ...baseBtn, onClick }).props.onClick({})
|
|
237
|
+
expect(viewportFocusSpy).toHaveBeenCalled()
|
|
238
|
+
rafSpy.mockRestore()
|
|
239
|
+
viewportFocusSpy.mockRestore()
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('does not call requestAnimationFrame when keepFocus is true', () => {
|
|
243
|
+
const rafSpy = jest.spyOn(global, 'requestAnimationFrame')
|
|
244
|
+
const onClick = jest.fn()
|
|
245
|
+
render({ ...baseBtn, onClick, keepFocus: true }).props.onClick({})
|
|
246
|
+
expect(rafSpy).not.toHaveBeenCalled()
|
|
247
|
+
rafSpy.mockRestore()
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('does not call requestAnimationFrame when button is a toggle (pressedWhen set)', () => {
|
|
251
|
+
const rafSpy = jest.spyOn(global, 'requestAnimationFrame')
|
|
252
|
+
const onClick = jest.fn()
|
|
253
|
+
render({ ...baseBtn, onClick, pressedWhen: jest.fn() }).props.onClick({})
|
|
254
|
+
expect(rafSpy).not.toHaveBeenCalled()
|
|
255
|
+
rafSpy.mockRestore()
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('includes focusOnOpen: false in OPEN_PANEL payload when keepFocus is true', () => {
|
|
259
|
+
const mockButtonEl = document.createElement('button')
|
|
260
|
+
render({ ...baseBtn, panelId: 'p1', keepFocus: true }).props.onClick({ currentTarget: mockButtonEl })
|
|
261
|
+
expect(appState.dispatch).toHaveBeenCalledWith({
|
|
262
|
+
type: 'OPEN_PANEL',
|
|
263
|
+
payload: { panelId: 'p1', props: { triggeringElement: mockButtonEl }, focusOnOpen: false }
|
|
264
|
+
})
|
|
265
|
+
})
|
|
230
266
|
})
|
|
231
267
|
|
|
232
268
|
// -------------------------
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/App/store/ServiceProvider.jsx
|
|
2
2
|
import React, { createContext, useMemo, useRef } from 'react'
|
|
3
3
|
import { createAnnouncer } from '../../services/announcer.js'
|
|
4
|
+
import { createHints } from '../../services/hints.js'
|
|
4
5
|
import { reverseGeocode } from '../../services/reverseGeocode.js'
|
|
5
6
|
import { useConfig } from '../store/configContext.js'
|
|
6
7
|
import { closeApp } from '../../services/closeApp.js'
|
|
@@ -13,18 +14,20 @@ export const ServiceProvider = ({ eventBus, children }) => {
|
|
|
13
14
|
const { id, handleExitClick, symbolDefaults: constructorSymbolDefaults } = useConfig()
|
|
14
15
|
const mapStatusRef = useRef(null)
|
|
15
16
|
const announce = useMemo(() => createAnnouncer(mapStatusRef), [])
|
|
17
|
+
const hints = useMemo(() => createHints(announce), [announce])
|
|
16
18
|
|
|
17
19
|
symbolRegistry.setDefaults(constructorSymbolDefaults || {})
|
|
18
20
|
|
|
19
21
|
const services = useMemo(() => ({
|
|
20
22
|
announce,
|
|
23
|
+
hints,
|
|
21
24
|
reverseGeocode: (zoom, center) => reverseGeocode(zoom, center),
|
|
22
25
|
eventBus,
|
|
23
26
|
mapStatusRef,
|
|
24
27
|
closeApp: () => closeApp(id, handleExitClick, eventBus),
|
|
25
28
|
symbolRegistry,
|
|
26
29
|
patternRegistry
|
|
27
|
-
}), [announce])
|
|
30
|
+
}), [announce, hints])
|
|
28
31
|
|
|
29
32
|
return (
|
|
30
33
|
<ServiceContext.Provider value={services}>
|
|
@@ -11,6 +11,11 @@ jest.mock('../../services/announcer.js', () => ({
|
|
|
11
11
|
createAnnouncer: jest.fn(() => jest.fn())
|
|
12
12
|
}))
|
|
13
13
|
|
|
14
|
+
const mockShow = jest.fn()
|
|
15
|
+
jest.mock('../../services/hints.js', () => ({
|
|
16
|
+
createHints: jest.fn(() => ({ show: mockShow, dismiss: jest.fn(), subscribe: jest.fn() }))
|
|
17
|
+
}))
|
|
18
|
+
|
|
14
19
|
jest.mock('../../services/reverseGeocode.js', () => ({
|
|
15
20
|
reverseGeocode: jest.fn(() => 'mockedReverseGeocode')
|
|
16
21
|
}))
|
|
@@ -57,6 +62,12 @@ describe('ServiceProvider', () => {
|
|
|
57
62
|
expect(result.current).toBeTruthy()
|
|
58
63
|
})
|
|
59
64
|
|
|
65
|
+
test('hints.show() delegates to createHints show', () => {
|
|
66
|
+
const { result } = renderHook(() => React.useContext(ServiceContext), { wrapper })
|
|
67
|
+
result.current.hints.show('<b>test</b>', { duration: 2000 })
|
|
68
|
+
expect(mockShow).toHaveBeenCalledWith('<b>test</b>', { duration: 2000 })
|
|
69
|
+
})
|
|
70
|
+
|
|
60
71
|
test('closeApp calls closeApp service with id and handleExitClick', () => {
|
|
61
72
|
const mockHandleExitClick = jest.fn()
|
|
62
73
|
const { useConfig } = require('../store/configContext.js')
|
|
@@ -23,7 +23,7 @@ function buildOpenPanels (state, panelId, breakpoint, props, focusOnOpen) {
|
|
|
23
23
|
return {
|
|
24
24
|
...(isExclusiveNonModal ? {} : filteredPanels),
|
|
25
25
|
...(isModal ? state.openPanels : {}),
|
|
26
|
-
[panelId]: { props, ...(focusOnOpen && { focusOnOpen
|
|
26
|
+
[panelId]: { props, ...(focusOnOpen !== undefined && { focusOnOpen }) }
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -158,6 +158,9 @@ const setPluginsEvaluated = (state) =>
|
|
|
158
158
|
const clearPluginsEvaluated = (state) =>
|
|
159
159
|
state.arePluginsEvaluated ? { ...state, arePluginsEvaluated: false } : state
|
|
160
160
|
|
|
161
|
+
const setListboxActive = (state) =>
|
|
162
|
+
state.listboxIsActive ? state : { ...state, listboxIsActive: true }
|
|
163
|
+
|
|
161
164
|
const setSafeZoneInset = (state, { safeZoneInset, syncMapPadding = true }) => {
|
|
162
165
|
return shallowEqual(state.safeZoneInset, safeZoneInset)
|
|
163
166
|
? state
|
|
@@ -373,6 +376,7 @@ export const actionsMap = {
|
|
|
373
376
|
SET_MODE: setMode,
|
|
374
377
|
PLUGINS_EVALUATED: setPluginsEvaluated,
|
|
375
378
|
CLEAR_PLUGINS_EVALUATED: clearPluginsEvaluated,
|
|
379
|
+
SET_LISTBOX_ACTIVE: setListboxActive,
|
|
376
380
|
SET_SAFE_ZONE_INSET: setSafeZoneInset,
|
|
377
381
|
REVERT_MODE: revertMode,
|
|
378
382
|
OPEN_PANEL: openPanel,
|
|
@@ -425,4 +425,17 @@ describe('actionsMap full coverage', () => {
|
|
|
425
425
|
expect(res.openPanels).toHaveProperty('p1')
|
|
426
426
|
})
|
|
427
427
|
})
|
|
428
|
+
|
|
429
|
+
describe('SET_LISTBOX_ACTIVE', () => {
|
|
430
|
+
test('sets listboxIsActive to true when currently false', () => {
|
|
431
|
+
const res = actionsMap.SET_LISTBOX_ACTIVE({ ...state, listboxIsActive: false })
|
|
432
|
+
expect(res.listboxIsActive).toBe(true)
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
test('returns same state reference when listboxIsActive is already true', () => {
|
|
436
|
+
const activeState = { ...state, listboxIsActive: true }
|
|
437
|
+
const res = actionsMap.SET_LISTBOX_ACTIVE(activeState)
|
|
438
|
+
expect(res).toBe(activeState)
|
|
439
|
+
})
|
|
440
|
+
})
|
|
428
441
|
})
|
|
@@ -36,7 +36,8 @@ const updateCrossHair = (state, payload) => {
|
|
|
36
36
|
|
|
37
37
|
const upsertMarker = (state, payload) => {
|
|
38
38
|
const map = new Map(state.markers.items.map(item => [item.id, item]))
|
|
39
|
-
map.
|
|
39
|
+
const existing = map.get(payload.id) || {}
|
|
40
|
+
map.set(payload.id, { ...existing, ...payload })
|
|
40
41
|
const newItems = Array.from(map.values())
|
|
41
42
|
|
|
42
43
|
return {
|
|
@@ -144,7 +144,7 @@ export default class InteractiveMap {
|
|
|
144
144
|
if (history.state?.isBack) {
|
|
145
145
|
history.back()
|
|
146
146
|
} else {
|
|
147
|
-
const key = this.config.
|
|
147
|
+
const key = this.config.mapViewQueryParam
|
|
148
148
|
const newUrl = this._removeMapParamFromUrl(location.href, key)
|
|
149
149
|
history.replaceState(history.state, '', newUrl)
|
|
150
150
|
}
|
|
@@ -336,6 +336,18 @@ export default class InteractiveMap {
|
|
|
336
336
|
this.eventBus.emit(events.APP_ADD_MARKER, { id, coords, options })
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Update an existing marker's options without replacing it. No-op if the marker id is not found.
|
|
341
|
+
* Merges the provided options into the existing marker — unspecified properties are preserved.
|
|
342
|
+
* Pass `coords` inside options to move the marker to new coordinates.
|
|
343
|
+
*
|
|
344
|
+
* @param {string} id - Marker identifier to update.
|
|
345
|
+
* @param {MarkerOptions & { coords?: [number, number] }} [options] - Properties to merge into the marker.
|
|
346
|
+
*/
|
|
347
|
+
updateMarker (id, options) {
|
|
348
|
+
this.eventBus.emit(events.APP_UPDATE_MARKER, { id, options })
|
|
349
|
+
}
|
|
350
|
+
|
|
339
351
|
/**
|
|
340
352
|
* Remove a marker from the map.
|
|
341
353
|
*
|
|
@@ -274,7 +274,7 @@ describe('InteractiveMap Core Functionality', () => {
|
|
|
274
274
|
const map = new InteractiveMap('map', {
|
|
275
275
|
behaviour: 'buttonFirst',
|
|
276
276
|
mapProvider: mapProviderMock,
|
|
277
|
-
|
|
277
|
+
mapViewQueryParam: 'mv',
|
|
278
278
|
manageHistoryState: true,
|
|
279
279
|
preserveStateOnClose: false
|
|
280
280
|
})
|
|
@@ -303,7 +303,7 @@ describe('InteractiveMap Core Functionality', () => {
|
|
|
303
303
|
const map = new InteractiveMap('map', {
|
|
304
304
|
behaviour: 'buttonFirst',
|
|
305
305
|
mapProvider: mapProviderMock,
|
|
306
|
-
|
|
306
|
+
mapViewQueryParam: 'mv',
|
|
307
307
|
manageHistoryState: true,
|
|
308
308
|
preserveStateOnClose: true
|
|
309
309
|
})
|
|
@@ -360,7 +360,7 @@ describe('InteractiveMap Core Functionality', () => {
|
|
|
360
360
|
const map = new InteractiveMap('map', {
|
|
361
361
|
behaviour: 'buttonFirst',
|
|
362
362
|
mapProvider: mapProviderMock,
|
|
363
|
-
|
|
363
|
+
mapViewQueryParam: 'mv',
|
|
364
364
|
manageHistoryState: true,
|
|
365
365
|
preserveStateOnClose: false
|
|
366
366
|
})
|
|
@@ -380,7 +380,7 @@ describe('InteractiveMap Core Functionality', () => {
|
|
|
380
380
|
const map = new InteractiveMap('map', {
|
|
381
381
|
behaviour: 'buttonFirst',
|
|
382
382
|
mapProvider: mapProviderMock,
|
|
383
|
-
|
|
383
|
+
mapViewQueryParam: 'mv',
|
|
384
384
|
manageHistoryState: false,
|
|
385
385
|
preserveStateOnClose: false
|
|
386
386
|
})
|
|
@@ -586,6 +586,7 @@ describe('InteractiveMap Public API Methods', () => {
|
|
|
586
586
|
map.off('testEvent', cb)
|
|
587
587
|
map.emit('customEvent', 123)
|
|
588
588
|
map.addMarker('marker-1', coords, options)
|
|
589
|
+
map.updateMarker('marker-1', options)
|
|
589
590
|
map.removeMarker('marker-1')
|
|
590
591
|
map.setMode('test-mode')
|
|
591
592
|
|
|
@@ -593,6 +594,7 @@ describe('InteractiveMap Public API Methods', () => {
|
|
|
593
594
|
expect(map.eventBus.off).toHaveBeenCalledWith('testEvent', cb)
|
|
594
595
|
expect(map.eventBus.emit).toHaveBeenCalledWith('customEvent', 123)
|
|
595
596
|
expect(map.eventBus.emit).toHaveBeenCalledWith('app:addmarker', { id: 'marker-1', coords, options })
|
|
597
|
+
expect(map.eventBus.emit).toHaveBeenCalledWith('app:updatemarker', { id: 'marker-1', options })
|
|
596
598
|
expect(map.eventBus.emit).toHaveBeenCalledWith('app:removemarker', 'marker-1')
|
|
597
599
|
expect(map.eventBus.emit).toHaveBeenCalledWith('app:setmode', 'test-mode')
|
|
598
600
|
})
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getQueryParam } from '../utils/queryString.js'
|
|
2
2
|
import { isHybridFullscreen } from '../utils/getIsFullscreen.js'
|
|
3
3
|
import { updateDOMState } from './domStateManager.js'
|
|
4
|
-
import defaults from '../config/defaults.js'
|
|
5
4
|
|
|
6
5
|
// -----------------------------------------------------------------------------
|
|
7
6
|
// Public API
|
|
@@ -18,7 +17,7 @@ import defaults from '../config/defaults.js'
|
|
|
18
17
|
*/
|
|
19
18
|
function shouldLoadComponent (config) {
|
|
20
19
|
const { id, behaviour } = config
|
|
21
|
-
const hasViewParam = getQueryParam(
|
|
20
|
+
const hasViewParam = getQueryParam(config.mapViewQueryParam) === id
|
|
22
21
|
|
|
23
22
|
return ['mapOnly', 'inline'].includes(behaviour) ||
|
|
24
23
|
(behaviour === 'hybrid' && !isHybridFullscreen(config)) ||
|
|
@@ -12,8 +12,6 @@
|
|
|
12
12
|
* @returns {HTMLAnchorElement} The newly created button element.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import defaults from '../config/defaults.js'
|
|
16
|
-
|
|
17
15
|
export function createButton (config, rootEl, handleClick) {
|
|
18
16
|
const { id, buttonText, buttonClass } = config
|
|
19
17
|
|
|
@@ -33,7 +31,7 @@ export function createButton (config, rootEl, handleClick) {
|
|
|
33
31
|
|
|
34
32
|
// Build URL keeping existing params
|
|
35
33
|
const url = new URL(window.location.href)
|
|
36
|
-
url.searchParams.set(
|
|
34
|
+
url.searchParams.set(config.mapViewQueryParam, id)
|
|
37
35
|
button.setAttribute('href', url.toString())
|
|
38
36
|
|
|
39
37
|
button.addEventListener('click', (e) => {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createButton } from './buttonManager.js'
|
|
2
|
-
import defaults from '../config/defaults.js'
|
|
3
2
|
|
|
4
3
|
describe('createButton', () => {
|
|
5
4
|
let rootEl, handleClick, button
|
|
@@ -19,11 +18,11 @@ describe('createButton', () => {
|
|
|
19
18
|
})
|
|
20
19
|
|
|
21
20
|
it('creates button with correct attributes and inserts before rootEl', () => {
|
|
22
|
-
const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn' }
|
|
21
|
+
const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'mv' }
|
|
23
22
|
button = createButton(config, rootEl, handleClick)
|
|
24
23
|
|
|
25
24
|
const expectedHref = new URL(window.location.href)
|
|
26
|
-
expectedHref.searchParams.set(
|
|
25
|
+
expectedHref.searchParams.set('mv', 'map')
|
|
27
26
|
|
|
28
27
|
expect(button).toBeInstanceOf(HTMLAnchorElement)
|
|
29
28
|
expect(button.getAttribute('href')).toBe(expectedHref.toString())
|
|
@@ -34,7 +33,7 @@ describe('createButton', () => {
|
|
|
34
33
|
})
|
|
35
34
|
|
|
36
35
|
it('prevents default and calls handleClick on click', () => {
|
|
37
|
-
const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn' }
|
|
36
|
+
const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'mv' }
|
|
38
37
|
button = createButton(config, rootEl, handleClick)
|
|
39
38
|
|
|
40
39
|
const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true })
|
|
@@ -47,21 +46,30 @@ describe('createButton', () => {
|
|
|
47
46
|
})
|
|
48
47
|
|
|
49
48
|
it('builds href with map param and preserves existing query params', () => {
|
|
50
|
-
const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn' }
|
|
49
|
+
const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'mv' }
|
|
51
50
|
button = createButton(config, rootEl, handleClick)
|
|
52
51
|
|
|
53
52
|
const url = new URL(button.getAttribute('href'))
|
|
54
53
|
expect(url.searchParams.get('foo')).toBe('bar') // existing param preserved
|
|
55
|
-
expect(url.searchParams.get(
|
|
54
|
+
expect(url.searchParams.get('mv')).toBe('map') // new param added
|
|
56
55
|
})
|
|
57
56
|
|
|
58
57
|
it('sets correct href even when no existing query params', () => {
|
|
59
58
|
window.history.replaceState({}, '', '/test') // no query
|
|
60
|
-
const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn' }
|
|
59
|
+
const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'mv' }
|
|
61
60
|
button = createButton(config, rootEl, handleClick)
|
|
62
61
|
|
|
63
62
|
const url = new URL(button.getAttribute('href'))
|
|
64
63
|
expect(url.pathname).toBe('/test')
|
|
65
|
-
expect(url.searchParams.get(
|
|
64
|
+
expect(url.searchParams.get('mv')).toBe('map')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('uses a custom mapViewQueryParam in the button href', () => {
|
|
68
|
+
const config = { id: 'map', buttonText: 'View Map', buttonClass: 'custom-btn', mapViewQueryParam: 'view' }
|
|
69
|
+
button = createButton(config, rootEl, handleClick)
|
|
70
|
+
|
|
71
|
+
const url = new URL(button.getAttribute('href'))
|
|
72
|
+
expect(url.searchParams.get('view')).toBe('map')
|
|
73
|
+
expect(url.searchParams.has('mv')).toBe(false)
|
|
66
74
|
})
|
|
67
75
|
})
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getQueryParam } from '../utils/queryString.js'
|
|
2
2
|
import { toggleInertElements } from '../utils/toggleInertElements.js'
|
|
3
3
|
import { isHybridFullscreen } from '../utils/getIsFullscreen.js'
|
|
4
|
-
import defaults from '../config/defaults.js'
|
|
5
4
|
|
|
6
5
|
// -----------------------------------------------------------------------------
|
|
7
6
|
// Internal (not exported)
|
|
@@ -25,10 +24,10 @@ function getIsFullscreen (config) {
|
|
|
25
24
|
if (manageHistoryState === false) {
|
|
26
25
|
return true
|
|
27
26
|
}
|
|
28
|
-
return getQueryParam(
|
|
27
|
+
return getQueryParam(config.mapViewQueryParam) === id
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
return isHybridFullscreen(config) && getQueryParam(
|
|
30
|
+
return isHybridFullscreen(config) && getQueryParam(config.mapViewQueryParam) === id
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
// -----------------------------------------------------------------------------
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getQueryParam } from '../utils/queryString.js'
|
|
2
2
|
import { isHybridFullscreen } from '../utils/getIsFullscreen.js'
|
|
3
|
-
import defaults from '../config/defaults.js'
|
|
4
3
|
|
|
5
4
|
// -----------------------------------------------------------------------------
|
|
6
5
|
// Internal helpers
|
|
@@ -81,8 +80,10 @@ function syncMapInstance (mapInstance, viewId) {
|
|
|
81
80
|
* @returns {void}
|
|
82
81
|
*/
|
|
83
82
|
function handlePopstate () {
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
components.forEach(mapInstance => {
|
|
84
|
+
const viewId = getQueryParam(mapInstance.config.mapViewQueryParam)
|
|
85
|
+
syncMapInstance(mapInstance, viewId)
|
|
86
|
+
})
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
// -----------------------------------------------------------------------------
|
package/src/config/appConfig.js
CHANGED
|
@@ -45,6 +45,7 @@ export const defaultAppConfig = {
|
|
|
45
45
|
group: { name: 'zoom', label: 'Zoom controls', order: 0 },
|
|
46
46
|
label: 'Zoom in',
|
|
47
47
|
iconId: 'plus',
|
|
48
|
+
keepFocus: true,
|
|
48
49
|
onClick: (_e, { mapProvider, appConfig }) => mapProvider.zoomIn(appConfig.zoomDelta),
|
|
49
50
|
excludeWhen: ({ appState, appConfig }) => !appConfig.enableZoomControls || appState.interfaceType === 'touch',
|
|
50
51
|
enableWhen: ({ mapState }) => !mapState.isAtMaxZoom,
|
|
@@ -56,6 +57,7 @@ export const defaultAppConfig = {
|
|
|
56
57
|
group: { name: 'zoom', label: 'Zoom controls', order: 0 },
|
|
57
58
|
label: 'Zoom out',
|
|
58
59
|
iconId: 'minus',
|
|
60
|
+
keepFocus: true,
|
|
59
61
|
onClick: (_e, { mapProvider, appConfig }) => mapProvider.zoomOut(appConfig.zoomDelta),
|
|
60
62
|
excludeWhen: ({ appState, appConfig }) => !appConfig.enableZoomControls || appState.interfaceType === 'touch',
|
|
61
63
|
enableWhen: ({ mapState }) => !mapState.isAtMinZoom,
|
|
@@ -78,7 +80,7 @@ export const defaultAppConfig = {
|
|
|
78
80
|
...keyboardBasePanelSlots,
|
|
79
81
|
width: '500px'
|
|
80
82
|
},
|
|
81
|
-
render: () => <KeyboardHelp />
|
|
83
|
+
render: (props) => <KeyboardHelp context={props?.context} />
|
|
82
84
|
}],
|
|
83
85
|
|
|
84
86
|
icons: [{
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { render } from '@testing-library/react'
|
|
2
2
|
import { defaultAppConfig, defaultButtonConfig, scaleFactor } from './appConfig'
|
|
3
3
|
|
|
4
|
+
jest.mock('../App/store/appContext.js', () => ({
|
|
5
|
+
useApp: () => ({ listboxIsActive: false })
|
|
6
|
+
}))
|
|
7
|
+
|
|
4
8
|
describe('defaultAppConfig', () => {
|
|
5
9
|
const appState = {
|
|
6
10
|
layoutRefs: { appContainerRef: { current: document.createElement('div') } },
|
package/src/config/defaults.js
CHANGED
|
@@ -21,11 +21,12 @@ const defaults = {
|
|
|
21
21
|
hasExitButton: false,
|
|
22
22
|
hybridWidth: null, // Defaults to maxMobileWidth if not set
|
|
23
23
|
keyboardHintText: '<span class="im-u-visually-hidden">Press </span><kbd>Alt</kbd> + <kbd>K</kbd> <span class="im-u-visually-hidden">to view </span>keyboard shortcuts',
|
|
24
|
-
mapLabel: 'Interactive map',
|
|
24
|
+
mapLabel: 'Interactive map application',
|
|
25
25
|
mapProvider: null,
|
|
26
26
|
mapSize: 'small',
|
|
27
27
|
manageHistoryState: true,
|
|
28
|
-
|
|
28
|
+
mapViewQueryParam: 'mv',
|
|
29
|
+
urlPosition: 'sync',
|
|
29
30
|
maxMobileWidth: 640,
|
|
30
31
|
minDesktopWidth: 835,
|
|
31
32
|
nudgePanDelta: 5,
|
package/src/config/events.js
CHANGED
|
@@ -25,6 +25,8 @@ export const EVENTS = {
|
|
|
25
25
|
|
|
26
26
|
/** @internal Add a marker. Payload: { id, coords, options } */
|
|
27
27
|
APP_ADD_MARKER: 'app:addmarker',
|
|
28
|
+
/** @internal Update an existing marker's options. No-op if id not found. Payload: { id, options } */
|
|
29
|
+
APP_UPDATE_MARKER: 'app:updatemarker',
|
|
28
30
|
/** @internal Remove a marker. Payload: id */
|
|
29
31
|
APP_REMOVE_MARKER: 'app:removemarker',
|
|
30
32
|
/** @internal Set application mode. Payload: mode */
|
|
@@ -121,6 +123,12 @@ export const EVENTS = {
|
|
|
121
123
|
MAP_SET_STYLE: 'map:setstyle',
|
|
122
124
|
/** @internal Set map size. Payload: { width, height } */
|
|
123
125
|
MAP_SET_SIZE: 'map:setsize',
|
|
126
|
+
/** @internal Set the accessible features list. Payload: { items: { id: string, label: string }[] } */
|
|
127
|
+
MAP_SET_FEATURES: 'map:setfeatures',
|
|
128
|
+
/** @internal Set the active feature in the accessible features list. Payload: { id: string | null } */
|
|
129
|
+
MAP_SET_ACTIVE_FEATURE: 'map:setactivefeature',
|
|
130
|
+
/** @internal Select the active listbox feature as the real selection (Enter key). Payload: none */
|
|
131
|
+
MAP_SELECT_FEATURE: 'map:selectfeature',
|
|
124
132
|
/** @internal Set pixel ratio. Payload: pixelRatio */
|
|
125
133
|
MAP_SET_PIXEL_RATIO: 'map:setpixelratio',
|
|
126
134
|
/** @internal Fit the map to a bounding box. Payload: [west, south, east, north] */
|
package/src/config/mapTheme.js
CHANGED
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
* used both by the symbol SVG renderer (JS canvas) and injected as
|
|
5
5
|
* CSS custom properties onto the app container for CSS-rendered elements.
|
|
6
6
|
*
|
|
7
|
-
* Per-style overrides take precedence: set `haloColor`, `selectedColor`, or
|
|
7
|
+
* Per-style overrides take precedence: set `haloColor`, `selectedColor`, `activeColor`, or
|
|
8
8
|
* `foregroundColor` directly on a `MapStyleConfig` to override these defaults.
|
|
9
9
|
*/
|
|
10
|
-
export const
|
|
10
|
+
export const THEME_COLORS = {
|
|
11
11
|
light: {
|
|
12
12
|
haloColor: '#ffffff',
|
|
13
13
|
selectedColor: '#0b0c0c',
|
|
14
|
+
activeColor: '#ffdd00',
|
|
14
15
|
foregroundColor: '#0b0c0c'
|
|
15
16
|
},
|
|
16
17
|
dark: {
|
|
17
18
|
haloColor: '#0b0c0c',
|
|
18
19
|
selectedColor: '#ffffff',
|
|
20
|
+
activeColor: '#ffdd00',
|
|
19
21
|
foregroundColor: '#ffffff'
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -30,10 +32,11 @@ export const SCHEME_COLORS = {
|
|
|
30
32
|
* @returns {{ haloColor: string, selectedColor: string, foregroundColor: string }}
|
|
31
33
|
*/
|
|
32
34
|
export const resolveMapTheme = (mapStyle) => {
|
|
33
|
-
const scheme =
|
|
35
|
+
const scheme = THEME_COLORS[mapStyle?.mapColorScheme] ?? THEME_COLORS.light
|
|
34
36
|
return {
|
|
35
37
|
haloColor: mapStyle?.haloColor ?? scheme.haloColor,
|
|
36
38
|
selectedColor: mapStyle?.selectedColor ?? scheme.selectedColor,
|
|
39
|
+
activeColor: mapStyle?.activeColor ?? scheme.activeColor,
|
|
37
40
|
foregroundColor: mapStyle?.foregroundColor ?? scheme.foregroundColor
|
|
38
41
|
}
|
|
39
42
|
}
|
|
@@ -47,10 +50,11 @@ export const resolveMapTheme = (mapStyle) => {
|
|
|
47
50
|
* @returns {Object} CSS custom property object
|
|
48
51
|
*/
|
|
49
52
|
export const getMapThemeVars = (mapStyle) => {
|
|
50
|
-
const { haloColor, selectedColor, foregroundColor } = resolveMapTheme(mapStyle)
|
|
53
|
+
const { haloColor, selectedColor, activeColor, foregroundColor } = resolveMapTheme(mapStyle)
|
|
51
54
|
return {
|
|
52
55
|
'--map-overlay-halo-color': haloColor,
|
|
53
56
|
'--map-overlay-selected-color': selectedColor,
|
|
57
|
+
'--map-overlay-active-color': activeColor,
|
|
54
58
|
'--map-overlay-foreground-color': foregroundColor
|
|
55
59
|
}
|
|
56
60
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import defaults from './defaults.js'
|
|
2
2
|
|
|
3
3
|
export const mergeConfig = (userConfig = {}) => {
|
|
4
|
-
|
|
4
|
+
const { mapViewParamKey, ...restUser } = userConfig
|
|
5
|
+
|
|
5
6
|
const config = {
|
|
6
7
|
...defaults,
|
|
7
|
-
...
|
|
8
|
+
...restUser
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (mapViewParamKey !== undefined) {
|
|
12
|
+
console.warn('[InteractiveMap] mapViewParamKey is deprecated — use mapViewQueryParam instead.')
|
|
13
|
+
config.mapViewQueryParam = mapViewParamKey
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
return config
|
|
@@ -21,4 +21,23 @@ describe('mergeConfig', () => {
|
|
|
21
21
|
const result = mergeConfig(userConfig)
|
|
22
22
|
expect(result.theme).toBe('dark')
|
|
23
23
|
})
|
|
24
|
+
|
|
25
|
+
it('maps deprecated mapViewParamKey to mapViewQueryParam', () => {
|
|
26
|
+
const result = mergeConfig({ mapViewParamKey: 'view' })
|
|
27
|
+
expect(result.mapViewQueryParam).toBe('view')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('warns when deprecated mapViewParamKey is used', () => {
|
|
31
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
32
|
+
mergeConfig({ mapViewParamKey: 'view' })
|
|
33
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('mapViewParamKey is deprecated'))
|
|
34
|
+
warnSpy.mockRestore()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('does not warn when mapViewParamKey is not used', () => {
|
|
38
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
39
|
+
mergeConfig({ mapViewQueryParam: 'view' })
|
|
40
|
+
expect(warnSpy).not.toHaveBeenCalled()
|
|
41
|
+
warnSpy.mockRestore()
|
|
42
|
+
})
|
|
24
43
|
})
|
|
@@ -7,11 +7,18 @@
|
|
|
7
7
|
export const symbolDefaults = {
|
|
8
8
|
symbol: 'pin',
|
|
9
9
|
backgroundColor: '#ca3535',
|
|
10
|
-
foregroundColor: '#ffffff'
|
|
11
|
-
haloWidth: '1',
|
|
12
|
-
selectedWidth: '6'
|
|
10
|
+
foregroundColor: '#ffffff'
|
|
13
11
|
}
|
|
14
12
|
|
|
13
|
+
/** Stroke width for the halo (background shape outline) in SVG units */
|
|
14
|
+
export const HALO_STROKE_WIDTH = 2
|
|
15
|
+
|
|
16
|
+
/** Stroke width for the selected state — symbol rings and feature highlight lines */
|
|
17
|
+
export const SELECTED_STROKE_WIDTH = 3
|
|
18
|
+
|
|
19
|
+
/** Stroke width for the active (keyboard cursor) state — double selected, extends 1× each side */
|
|
20
|
+
export const ACTIVE_STROKE_WIDTH = SELECTED_STROKE_WIDTH * 2
|
|
21
|
+
|
|
15
22
|
/**
|
|
16
23
|
* Built-in graphic path data strings for use with the `graphic` token.
|
|
17
24
|
*
|
|
@@ -46,35 +53,35 @@ export const graphics = {
|
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
// ─── Built-in symbol definitions ─────────────────────────────────────────────
|
|
49
|
-
// Each symbol uses a
|
|
56
|
+
// Each symbol uses a 44×44 viewBox. SVG templates use {{token}} placeholders
|
|
50
57
|
// resolved at render time by the symbolRegistry.
|
|
51
58
|
|
|
52
59
|
export const pin = {
|
|
53
60
|
id: 'pin',
|
|
54
|
-
viewBox: '0 0
|
|
61
|
+
viewBox: '0 0 44 44',
|
|
55
62
|
anchor: [0.5, 0.9], // NOSONAR
|
|
56
63
|
graphic: graphics.dot,
|
|
57
|
-
svg: `<path d="
|
|
58
|
-
<path d="
|
|
59
|
-
<g transform="translate(
|
|
64
|
+
svg: `<path d="M22 40.999c-3.621 0-8.306-5.864-10.258-8.3C9.02 29.302 6 23.66 6 19.002a16.01 16.01 0 0 1 16-16 16.01 16.01 0 0 1 16 16c0 4.658-3.02 10.3-5.742 13.697-1.952 2.437-6.637 8.3-10.258 8.3z" fill="{{selectedColor}}" stroke="{{activeColor}}" stroke-width="6" paint-order="stroke fill"/>
|
|
65
|
+
<path d="M22 7.001a12.01 12.01 0 0 1 12 12c0 7.623-10.377 17.998-12 17.998S10 26.624 10 19.001a12.01 12.01 0 0 1 12-12z" fill="{{backgroundColor}}" stroke="{{haloColor}}" stroke-width="2" paint-order="stroke fill"/>
|
|
66
|
+
<g transform="translate(22, 19) scale(0.8) translate(-8, -8)"><path d="{{graphic}}" fill="{{foregroundColor}}"/></g>`
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
export const circle = {
|
|
63
70
|
id: 'circle',
|
|
64
|
-
viewBox: '0 0
|
|
71
|
+
viewBox: '0 0 44 44',
|
|
65
72
|
anchor: [0.5, 0.5],
|
|
66
73
|
graphic: graphics.dot,
|
|
67
|
-
svg: `<
|
|
68
|
-
<
|
|
69
|
-
<g transform="translate(
|
|
74
|
+
svg: `<circle cx="22" cy="22" r="16" fill="{{selectedColor}}" stroke="{{activeColor}}" stroke-width="6" paint-order="stroke fill"/>
|
|
75
|
+
<circle cx="22" cy="22" r="12" fill="{{backgroundColor}}" stroke="{{haloColor}}" stroke-width="2" paint-order="stroke fill"/>
|
|
76
|
+
<g transform="translate(22, 22) scale(0.8) translate(-8, -8)"><path d="{{graphic}}" fill="{{foregroundColor}}"/></g>`
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
export const square = {
|
|
73
80
|
id: 'square',
|
|
74
|
-
viewBox: '0 0
|
|
81
|
+
viewBox: '0 0 44 44',
|
|
75
82
|
anchor: [0.5, 0.5],
|
|
76
83
|
graphic: graphics.dot,
|
|
77
|
-
svg: `<path d="
|
|
78
|
-
<path d="
|
|
79
|
-
<g transform="translate(
|
|
84
|
+
svg: `<path d="M13 6h18c3.863 0 7 3.137 7 7v18c0 3.863-3.137 7-7 7H13c-3.863 0-7-3.137-7-7V13c0-3.863 3.137-7 7-7" fill="{{selectedColor}}" stroke="{{activeColor}}" stroke-width="6" paint-order="stroke fill"/>
|
|
85
|
+
<path d="M13 34a3 3 0 0 1-3-3V13a3 3 0 0 1 3-3h18a3 3 0 0 1 3 3v18a3 3 0 0 1-3 3H13z" fill-rule="nonzero" fill="{{backgroundColor}}" stroke="{{haloColor}}" stroke-width="2" paint-order="stroke fill"/>
|
|
86
|
+
<g transform="translate(22, 22) scale(0.8) translate(-8, -8)"><path d="{{graphic}}" fill="{{foregroundColor}}"/></g>`
|
|
80
87
|
}
|