@defra/interactive-map 0.0.42-fmp-5 → 0.0.43-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/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/package.json +1 -1
- package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/datasets/src/registry/dataset.js +8 -12
- package/plugins/datasets/src/registry/dataset.test.js +17 -0
- package/plugins/datasets/src/registry/datasetRegistry.js +4 -5
- package/plugins/draw/dist/esm/im-draw-ml-adapter.js +1 -1
- package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/esm/im-draw-plugin.js +1 -1
- package/plugins/draw/dist/umd/im-draw-ml-adapter.js +1 -1
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/umd/im-draw-plugin.js +1 -1
- package/plugins/draw/src/DrawInit.jsx +23 -13
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.js +15 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.test.js +18 -0
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +9 -0
- package/plugins/draw/src/adapters/openlayers/point/drawPointMode.js +12 -1
- package/plugins/draw/src/adapters/openlayers/point/drawPointMode.test.js +18 -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/hooks/useCrossHairVisibility.js +8 -3
- package/plugins/interact/src/hooks/useMapItemList.js +41 -12
- package/plugins/interact/src/hooks/useMapItemList.test.js +68 -3
- package/plugins/interact/src/utils/spatial.js +24 -1
- package/plugins/interact/src/utils/spatial.test.js +26 -1
- package/plugins/map-key/dist/css/index.css +1 -1
- package/plugins/map-key/dist/esm/im-map-key-plugin.js +1 -1
- package/plugins/map-key/dist/umd/im-map-key-plugin.js +1 -1
- package/plugins/map-key/dist/umd/index.js +1 -1
- package/plugins/map-key/src/components/Key/Key.jsx +3 -4
- package/plugins/map-key/src/components/Key/Key.module.scss +10 -8
- package/plugins/map-key/src/components/Key/KeyGroupItem.test.jsx +2 -2
- package/plugins/map-key/src/components/Key/KeyItem.jsx +2 -2
- package/plugins/map-key/src/components/Key/KeySvg.jsx +9 -7
- package/plugins/map-key/src/components/Key/KeySvg.test.jsx +4 -4
- package/plugins/map-key/src/components/Key/MapKey.jsx +5 -3
- package/plugins/map-key/src/manifest.js +1 -1
- package/plugins/map-key/src/utils/groupStyles.js +7 -0
- package/plugins/map-key/src/utils/mergeKeyGroupItems.js +12 -0
- package/plugins/map-key/src/utils/mergeKeyGroupItems.test.js +53 -0
- package/plugins/menu/dist/esm/im-menu-plugin.js +1 -1
- package/plugins/menu/dist/umd/im-menu-plugin.js +1 -1
- package/plugins/menu/dist/umd/index.js +1 -1
- package/plugins/menu/src/initialise/MenuInit.jsx +15 -3
- package/plugins/menu/src/initialise/MenuInit.test.jsx +25 -0
- package/plugins/menu/src/initialise/createMenu.js +3 -1
- package/plugins/menu/src/initialise/createMenu.test.js +20 -5
- package/plugins/menu/src/initialise/updateUrl.js +44 -0
- package/plugins/menu/src/initialise/updateUrl.test.js +91 -0
- package/plugins/menu/src/reducers/menuStateReducer.js +19 -1
- package/plugins/menu/src/reducers/menuStateReducer.test.js +53 -20
- package/plugins/menu/src/reducers/pluginState.js +1 -6
- package/plugins/menu/src/reducers/pluginState.test.js +0 -25
- 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 +3 -3
- package/providers/beta/openlayers/src/openlayersProvider.test.js +6 -3
- package/providers/beta/openlayers/src/utils/queryFeatures.js +72 -0
- package/providers/beta/openlayers/src/utils/queryFeatures.test.js +122 -1
- package/src/App/components/CrossHair/CrossHair.jsx +19 -11
- package/src/App/components/CrossHair/CrossHair.module.scss +33 -12
- package/src/App/components/CrossHair/CrossHair.test.jsx +35 -11
- package/src/App/components/Markers/SymbolMarker.jsx +2 -3
- package/src/App/components/Markers/SymbolMarker.test.jsx +8 -9
- package/src/App/components/Panel/Panel.jsx +9 -1
- package/src/App/components/Panel/Panel.test.jsx +26 -4
- package/src/App/components/Viewport/Features.jsx +18 -4
- package/src/App/components/Viewport/Features.module.scss +4 -4
- package/src/App/components/Viewport/Features.test.jsx +155 -11
- package/src/App/components/Viewport/Viewport.jsx +3 -3
- package/src/App/hooks/useCrossHairAPI.js +29 -1
- package/src/App/hooks/useCrossHairAPI.test.js +17 -1
- package/src/App/hooks/useFeatureFocus.js +86 -28
- package/src/App/hooks/useFeatureFocus.test.js +167 -0
- package/src/App/hooks/useFeatureItems.js +1 -1
- package/src/App/hooks/useInterfaceAPI.js +2 -1
- package/src/App/hooks/useInterfaceAPI.test.js +9 -0
- package/src/InteractiveMap/InteractiveMap.js +13 -2
- package/src/InteractiveMap/InteractiveMap.test.js +33 -0
- package/src/config/appConfig.js +2 -2
|
@@ -154,6 +154,14 @@ describe('useFeatureFocus — onBlur', () => {
|
|
|
154
154
|
const { result } = renderHook(() => useFeatureFocus({ ...makeRefs(), items: ITEMS }))
|
|
155
155
|
expect(() => act(() => result.current.onBlur())).not.toThrow()
|
|
156
156
|
})
|
|
157
|
+
|
|
158
|
+
// The "moving between sibling options shouldn't count as a real blur/focus" behavior is
|
|
159
|
+
// driven by an internal isInternalFocusMoveRef, set only while focusOption()'s own .focus()
|
|
160
|
+
// call is synchronously in flight — not observable by calling onFocus/onBlur directly here,
|
|
161
|
+
// since that requires a real DOM focus transition bubbling through React. See the
|
|
162
|
+
// "Features + useFeatureFocus — roving tabindex real focus events" suite in Features.test.jsx,
|
|
163
|
+
// which renders the real markup and is the only level this can be faithfully exercised at
|
|
164
|
+
// (and is where the regression this guards against was actually found).
|
|
157
165
|
})
|
|
158
166
|
|
|
159
167
|
// ─── useFeatureFocus — keydown listener lifecycle ────────────────────────────
|
|
@@ -348,6 +356,70 @@ describe('useFeatureFocus — ArrowUp navigation', () => {
|
|
|
348
356
|
})
|
|
349
357
|
})
|
|
350
358
|
|
|
359
|
+
// ─── useFeatureFocus — Home/End navigation ───────────────────────────────────
|
|
360
|
+
|
|
361
|
+
describe('useFeatureFocus — Home/End navigation', () => {
|
|
362
|
+
const setup = () => {
|
|
363
|
+
const refs = makeRefs()
|
|
364
|
+
const el = refs.featuresRef.current
|
|
365
|
+
document.body.appendChild(el)
|
|
366
|
+
const { result, unmount } = renderHook(() => useFeatureFocus({ ...refs, items: ITEMS }))
|
|
367
|
+
return { result, el, unmount }
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
it('jumps to the first item on Home from anywhere in the list', () => {
|
|
371
|
+
const { result, el, unmount } = setup()
|
|
372
|
+
act(() => result.current.onFocus())
|
|
373
|
+
fireKey(el, 'ArrowDown')
|
|
374
|
+
fireKey(el, 'Home')
|
|
375
|
+
expect(result.current.activeFeatureId).toBe('a')
|
|
376
|
+
unmount(); el.remove()
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('jumps to the last item on End from anywhere in the list', () => {
|
|
380
|
+
const { result, el, unmount } = setup()
|
|
381
|
+
act(() => result.current.onFocus())
|
|
382
|
+
fireKey(el, 'End')
|
|
383
|
+
expect(result.current.activeFeatureId).toBe('c')
|
|
384
|
+
unmount(); el.remove()
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
it('does nothing when items is empty', () => {
|
|
388
|
+
const refs = makeRefs()
|
|
389
|
+
const el = refs.featuresRef.current
|
|
390
|
+
document.body.appendChild(el)
|
|
391
|
+
const { result, unmount } = renderHook(() => useFeatureFocus(refs))
|
|
392
|
+
fireKey(el, 'Home')
|
|
393
|
+
fireKey(el, 'End')
|
|
394
|
+
expect(result.current.activeFeatureId).toBeNull()
|
|
395
|
+
unmount(); el.remove()
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
it('stops propagation on Home/End so the viewport keyboard handler does not also handle it', () => {
|
|
399
|
+
const { el, unmount } = setup()
|
|
400
|
+
for (const key of ['Home', 'End']) {
|
|
401
|
+
const event = new KeyboardEvent('keydown', { key, bubbles: true, cancelable: true })
|
|
402
|
+
const stopSpy = jest.spyOn(event, 'stopPropagation')
|
|
403
|
+
act(() => el.dispatchEvent(event))
|
|
404
|
+
expect(stopSpy).toHaveBeenCalled()
|
|
405
|
+
}
|
|
406
|
+
unmount()
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('emits map:setactivefeature with the boundary id', () => {
|
|
410
|
+
const eb = makeEventBus()
|
|
411
|
+
const refs = makeRefs()
|
|
412
|
+
const el = refs.featuresRef.current
|
|
413
|
+
document.body.appendChild(el)
|
|
414
|
+
const { result, unmount } = renderHook(() => useFeatureFocus({ ...refs, items: ITEMS, eventBus: eb }))
|
|
415
|
+
act(() => result.current.onFocus())
|
|
416
|
+
eb.emit.mockClear()
|
|
417
|
+
fireKey(el, 'End')
|
|
418
|
+
expect(eb.emit).toHaveBeenCalledWith(SET_ACTIVE, { id: 'c' })
|
|
419
|
+
unmount(); el.remove()
|
|
420
|
+
})
|
|
421
|
+
})
|
|
422
|
+
|
|
351
423
|
// ─── useFeatureFocus — eventBus integration ───────────────────────────────────
|
|
352
424
|
|
|
353
425
|
describe('useFeatureFocus — eventBus: map:setactivefeature listener', () => {
|
|
@@ -633,3 +705,98 @@ describe('useFeatureFocus — items change while focused', () => {
|
|
|
633
705
|
unmount()
|
|
634
706
|
})
|
|
635
707
|
})
|
|
708
|
+
|
|
709
|
+
// ─── useFeatureFocus — tabbableId (roving tabindex resting position) ─────────
|
|
710
|
+
|
|
711
|
+
describe('useFeatureFocus — tabbableId', () => {
|
|
712
|
+
it('is null when items is empty', () => {
|
|
713
|
+
const { result } = renderHook(() => useFeatureFocus(makeRefs()))
|
|
714
|
+
expect(result.current.tabbableId).toBeNull()
|
|
715
|
+
})
|
|
716
|
+
|
|
717
|
+
it('defaults to the first item before the list has ever been focused', () => {
|
|
718
|
+
const { result } = renderHook(() => useFeatureFocus({ ...makeRefs(), items: ITEMS }))
|
|
719
|
+
expect(result.current.tabbableId).toBe('a')
|
|
720
|
+
})
|
|
721
|
+
|
|
722
|
+
// Unlike onFocus's own resolution, tabbableId deliberately ignores selection — "prefer the
|
|
723
|
+
// selected item" is a real-Tab-in-only concern. If tabbableId also preferred it, selecting a
|
|
724
|
+
// single marker by clicking it on the map (which never touches lastActiveIdRef) would relocate
|
|
725
|
+
// tabIndex to match it on every click.
|
|
726
|
+
it('is not influenced by a selection change before any position is established', () => {
|
|
727
|
+
const eb = makeEventBus()
|
|
728
|
+
const { result } = renderHook(() => useFeatureFocus({ ...makeRefs(), items: ITEMS, eventBus: eb }))
|
|
729
|
+
act(() => eb.trigger(SELECTION_CHANGE, { selectedFeatures: [{ featureId: 'b', layerId: 'roads' }] }))
|
|
730
|
+
expect(result.current.tabbableId).toBe('a')
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
it('does not chase a single item toggled selected/deselected purely via mouse clicks, with no keyboard interaction at all', () => {
|
|
734
|
+
const eb = makeEventBus()
|
|
735
|
+
const { result } = renderHook(() => useFeatureFocus({ ...makeRefs(), items: ITEMS, eventBus: eb }))
|
|
736
|
+
act(() => eb.trigger(SELECTION_CHANGE, { selectedMarkers: ['a'] }))
|
|
737
|
+
expect(result.current.tabbableId).toBe('a')
|
|
738
|
+
act(() => eb.trigger(SELECTION_CHANGE, { selectedMarkers: ['b'] })) // clicked a different marker
|
|
739
|
+
expect(result.current.tabbableId).toBe('a') // still the structural first item — never moved
|
|
740
|
+
act(() => eb.trigger(SELECTION_CHANGE, { selectedMarkers: ['c'] }))
|
|
741
|
+
expect(result.current.tabbableId).toBe('a')
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
it('stays on the established position when a selection change arrives from elsewhere (e.g. clicking a marker on the map)', () => {
|
|
745
|
+
const eb = makeEventBus()
|
|
746
|
+
const refs = makeRefs()
|
|
747
|
+
const el = refs.featuresRef.current
|
|
748
|
+
document.body.appendChild(el)
|
|
749
|
+
const { result, unmount } = renderHook(() => useFeatureFocus({ ...refs, items: ITEMS, eventBus: eb }))
|
|
750
|
+
act(() => result.current.onFocus()) // establishes position at 'a'
|
|
751
|
+
fireKey(el, 'ArrowDown') // establishes position at 'b'
|
|
752
|
+
act(() => result.current.onBlur())
|
|
753
|
+
// A selection made outside the list (e.g. a mouse click on a map marker) never touches
|
|
754
|
+
// lastActiveIdRef — it only changes selectedIds via interact:selectionchange.
|
|
755
|
+
act(() => eb.trigger(SELECTION_CHANGE, { selectedMarkers: ['c'] }))
|
|
756
|
+
expect(result.current.tabbableId).toBe('b') // stays put — does not jump to the newly-selected 'c'
|
|
757
|
+
unmount(); el.remove()
|
|
758
|
+
})
|
|
759
|
+
})
|
|
760
|
+
|
|
761
|
+
// ─── useFeatureFocus — selectItem (click / touch / voice-control activation) ─
|
|
762
|
+
|
|
763
|
+
describe('useFeatureFocus — selectItem', () => {
|
|
764
|
+
afterEach(() => { document.body.innerHTML = '' })
|
|
765
|
+
|
|
766
|
+
it('sets activeFeatureId to the clicked item', () => {
|
|
767
|
+
const { result } = renderHook(() => useFeatureFocus({ ...makeRefs(), items: ITEMS }))
|
|
768
|
+
act(() => result.current.selectItem('b'))
|
|
769
|
+
expect(result.current.activeFeatureId).toBe('b')
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
it('emits map:setactivefeature then map:selectfeature for the clicked item', () => {
|
|
773
|
+
const eb = makeEventBus()
|
|
774
|
+
const { result } = renderHook(() => useFeatureFocus({ ...makeRefs(), items: ITEMS, eventBus: eb }))
|
|
775
|
+
act(() => result.current.selectItem('b'))
|
|
776
|
+
expect(eb.emit).toHaveBeenNthCalledWith(1, SET_ACTIVE, { id: 'b' })
|
|
777
|
+
expect(eb.emit).toHaveBeenNthCalledWith(2, SELECT)
|
|
778
|
+
})
|
|
779
|
+
|
|
780
|
+
it('moves real DOM focus to the clicked option', () => {
|
|
781
|
+
const refs = makeRefs()
|
|
782
|
+
const option = document.createElement('li')
|
|
783
|
+
option.dataset.id = 'b'
|
|
784
|
+
option.focus = jest.fn()
|
|
785
|
+
refs.featuresRef.current.appendChild(option)
|
|
786
|
+
const { result } = renderHook(() => useFeatureFocus({ ...refs, items: ITEMS }))
|
|
787
|
+
act(() => result.current.selectItem('b'))
|
|
788
|
+
expect(option.focus).toHaveBeenCalledWith({ preventScroll: true })
|
|
789
|
+
})
|
|
790
|
+
|
|
791
|
+
it('does not throw when eventBus is not provided', () => {
|
|
792
|
+
const { result } = renderHook(() => useFeatureFocus({ ...makeRefs(), items: ITEMS }))
|
|
793
|
+
expect(() => act(() => result.current.selectItem('b'))).not.toThrow()
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
it('does not throw when featuresRef.current is null (e.g. unmounted)', () => {
|
|
797
|
+
const refs = { ...makeRefs(), featuresRef: { current: null } }
|
|
798
|
+
const { result } = renderHook(() => useFeatureFocus({ ...refs, items: ITEMS }))
|
|
799
|
+
expect(() => act(() => result.current.selectItem('b'))).not.toThrow()
|
|
800
|
+
expect(result.current.activeFeatureId).toBe('b') // state still updates even though there's no element to focus
|
|
801
|
+
})
|
|
802
|
+
})
|
|
@@ -9,7 +9,7 @@ const SET_FEATURES_SUPPRESSED = 'map:setfeaturessuppressed'
|
|
|
9
9
|
* actually visible — which is what drives <Features>'s own tabIndex/aria-hidden.
|
|
10
10
|
*
|
|
11
11
|
* @param {object} eventBus
|
|
12
|
-
* @returns {{ items: Array<{ id: string, label: string }>, multiselectable: boolean }}
|
|
12
|
+
* @returns {{ items: Array<{ id: string, label: string, x?: number, y?: number }>, multiselectable: boolean }}
|
|
13
13
|
*/
|
|
14
14
|
export function useFeatureItems (eventBus) {
|
|
15
15
|
const [items, setItems] = useState([])
|
|
@@ -58,7 +58,8 @@ export const useInterfaceAPI = () => {
|
|
|
58
58
|
const handleAppClosed = () => dispatchRef.current({ type: 'TOGGLE_APP_VISIBLE', payload: false })
|
|
59
59
|
const handleAddPanel = ({ id, config }) => dispatchRef.current({ type: 'ADD_PANEL', payload: { id, config } })
|
|
60
60
|
const handleRemovePanel = (id) => dispatchRef.current({ type: 'REMOVE_PANEL', payload: id })
|
|
61
|
-
const handleShowPanel = ({ id, focus = true }) =>
|
|
61
|
+
const handleShowPanel = ({ id, focus = true, triggeringElement }) =>
|
|
62
|
+
dispatchRef.current({ type: 'OPEN_PANEL', payload: { panelId: id, focusOnOpen: focus, props: triggeringElement ? { triggeringElement } : undefined } })
|
|
62
63
|
const handleHidePanel = (id) => dispatchRef.current({ type: 'CLOSE_PANEL', payload: id })
|
|
63
64
|
const handleAddControl = ({ id, config }) => dispatchRef.current({ type: 'ADD_CONTROL', payload: { id, config } })
|
|
64
65
|
|
|
@@ -92,6 +92,15 @@ describe('useInterfaceAPI', () => {
|
|
|
92
92
|
expect(mockDispatch).toHaveBeenCalledWith({ type: 'OPEN_PANEL', payload: { panelId: 'panel1', focusOnOpen: false } })
|
|
93
93
|
})
|
|
94
94
|
|
|
95
|
+
it('dispatches OPEN_PANEL with props.triggeringElement when one is given, so Panel can return focus to it on close', () => {
|
|
96
|
+
const triggeringElement = document.createElement('button')
|
|
97
|
+
renderHook(() => useInterfaceAPI())
|
|
98
|
+
act(() => mockEventBus.emit('app:showpanel', { id: 'panel1', triggeringElement }))
|
|
99
|
+
expect(mockDispatch).toHaveBeenCalledWith({
|
|
100
|
+
type: 'OPEN_PANEL', payload: { panelId: 'panel1', focusOnOpen: true, props: { triggeringElement } }
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
95
104
|
it('dispatches CLOSE_PANEL on app:hidepanel', () => {
|
|
96
105
|
renderHook(() => useInterfaceAPI())
|
|
97
106
|
act(() => mockEventBus.emit('app:hidepanel', 'panel1'))
|
|
@@ -461,12 +461,23 @@ export default class InteractiveMap {
|
|
|
461
461
|
* Focus is moved to the panel by default. Set `focus: false` in options to
|
|
462
462
|
* suppress this — useful when showing a panel and you want focus to remain on the button.
|
|
463
463
|
*
|
|
464
|
+
* When the panel is later closed, focus returns to `triggeringElement` if one is given or
|
|
465
|
+
* can be inferred, falling back to the map viewport otherwise. If `triggeringElement` isn't
|
|
466
|
+
* passed explicitly, whatever currently has real DOM focus is captured automatically (e.g. a
|
|
467
|
+
* selected item in the map features list) — this only needs to be passed explicitly when the
|
|
468
|
+
* panel is opened from something that doesn't hold focus at the time (e.g a timer, or a
|
|
469
|
+
* genuine mouse click, which doesn't reliably focus its target in every browser).
|
|
470
|
+
*
|
|
464
471
|
* @param {string} id - Panel identifier to show.
|
|
465
472
|
* @param {object} [options]
|
|
466
473
|
* @param {boolean} [options.focus=true] - Whether to move focus to the panel.
|
|
474
|
+
* @param {HTMLElement} [options.triggeringElement] - Element to return focus to on close.
|
|
475
|
+
* Defaults to the currently focused element, if any.
|
|
467
476
|
*/
|
|
468
|
-
showPanel (id, { focus = true } = {}) {
|
|
469
|
-
|
|
477
|
+
showPanel (id, { focus = true, triggeringElement } = {}) {
|
|
478
|
+
const resolvedTriggeringElement = triggeringElement ??
|
|
479
|
+
(document.activeElement && document.activeElement !== document.body ? document.activeElement : undefined)
|
|
480
|
+
this.eventBus.emit(events.APP_SHOW_PANEL, { id, focus, triggeringElement: resolvedTriggeringElement })
|
|
470
481
|
}
|
|
471
482
|
|
|
472
483
|
/**
|
|
@@ -580,6 +580,39 @@ describe('InteractiveMap — Public API Methods', () => {
|
|
|
580
580
|
expect(map.eventBus.emit).toHaveBeenCalledWith('app:togglebuttonstate', { id: 'btn-1', prop: 'disabled', value: true })
|
|
581
581
|
})
|
|
582
582
|
|
|
583
|
+
describe('showPanel — triggeringElement', () => {
|
|
584
|
+
afterEach(() => { document.body.innerHTML = '' })
|
|
585
|
+
|
|
586
|
+
it('passes an explicit triggeringElement through unchanged', () => {
|
|
587
|
+
const el = document.createElement('button')
|
|
588
|
+
map.showPanel('panel1', { triggeringElement: el })
|
|
589
|
+
expect(map.eventBus.emit).toHaveBeenCalledWith('app:showpanel', { id: 'panel1', focus: true, triggeringElement: el })
|
|
590
|
+
})
|
|
591
|
+
|
|
592
|
+
it('auto-captures the currently focused element when none is given', () => {
|
|
593
|
+
const el = document.createElement('button')
|
|
594
|
+
document.body.appendChild(el)
|
|
595
|
+
el.focus()
|
|
596
|
+
map.showPanel('panel1')
|
|
597
|
+
expect(map.eventBus.emit).toHaveBeenCalledWith('app:showpanel', { id: 'panel1', focus: true, triggeringElement: el })
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
it('does not fall back to document.body as a triggeringElement', () => {
|
|
601
|
+
document.body.focus?.() // no-op in jsdom, but document.activeElement defaults to body regardless
|
|
602
|
+
map.showPanel('panel1')
|
|
603
|
+
expect(map.eventBus.emit).toHaveBeenCalledWith('app:showpanel', { id: 'panel1', focus: true, triggeringElement: undefined })
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
it('an explicit triggeringElement takes priority over the currently focused element', () => {
|
|
607
|
+
const focused = document.createElement('button')
|
|
608
|
+
document.body.appendChild(focused)
|
|
609
|
+
focused.focus()
|
|
610
|
+
const explicit = document.createElement('button')
|
|
611
|
+
map.showPanel('panel1', { triggeringElement: explicit })
|
|
612
|
+
expect(map.eventBus.emit).toHaveBeenCalledWith('app:showpanel', { id: 'panel1', focus: true, triggeringElement: explicit })
|
|
613
|
+
})
|
|
614
|
+
})
|
|
615
|
+
|
|
583
616
|
it('setContinueEnabled enables and disables the journey continue button', () => {
|
|
584
617
|
map.setContinueEnabled(true)
|
|
585
618
|
expect(map.eventBus.emit).toHaveBeenCalledWith('app:togglebuttonstate', { id: 'journeyContinue', prop: 'disabled', value: false })
|
package/src/config/appConfig.js
CHANGED
|
@@ -98,7 +98,7 @@ export const defaultAppConfig = {
|
|
|
98
98
|
desktop: buttonSlots
|
|
99
99
|
}, {
|
|
100
100
|
id: 'moveControls',
|
|
101
|
-
label: '
|
|
101
|
+
label: 'Map controls',
|
|
102
102
|
iconId: 'move',
|
|
103
103
|
keepFocus: true,
|
|
104
104
|
isExpanded: false,
|
|
@@ -157,7 +157,7 @@ export const defaultAppConfig = {
|
|
|
157
157
|
svgContent: '<path d="m6 9 6 6 6-6"/>'
|
|
158
158
|
}, {
|
|
159
159
|
id: 'move',
|
|
160
|
-
svgContent: '<path d="
|
|
160
|
+
svgContent: '<path d="M11.146 15.854a1.207 1.207 0 0 1 1.708 0l1.56 1.56A2 2 0 0 1 15 18.828V21a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-2.172a2 2 0 0 1 .586-1.414z"/><path d="M18.828 15a2 2 0 0 1-1.414-.586l-1.56-1.56a1.207 1.207 0 0 1 0-1.708l1.56-1.56A2 2 0 0 1 18.828 9H21a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1z"/><path d="M6.586 14.414A2 2 0 0 1 5.172 15H3a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2.172a2 2 0 0 1 1.414.586l1.56 1.56a1.207 1.207 0 0 1 0 1.708z"/><path d="M9 3a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2.172a2 2 0 0 1-.586 1.414l-1.56 1.56a1.207 1.207 0 0 1-1.708 0l-1.56-1.56A2 2 0 0 1 9 5.172z"/>'
|
|
161
161
|
}, {
|
|
162
162
|
id: 'precision',
|
|
163
163
|
svgContent: '<circle cx="12" cy="12" r="10"/><line x1="22" x2="18" y1="12" y2="12"/><line x1="6" x2="2" y1="12" y2="12"/><line x1="12" x2="12" y1="6" y2="2"/><line x1="12" x2="12" y1="22" y2="18"/>'
|