@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
|
@@ -11,6 +11,12 @@ jest.mock('../../store/mapContext.js', () => ({ useMap: jest.fn() }))
|
|
|
11
11
|
jest.mock('../../store/serviceContext.js', () => ({ useService: jest.fn() }))
|
|
12
12
|
jest.mock('../../../config/appConfig.js', () => ({ scaleFactor: { small: 1, medium: 1.5, large: 2 } }))
|
|
13
13
|
|
|
14
|
+
const MARKER_ID = 'marker-1'
|
|
15
|
+
const SEL_CHANGE = 'interact:selectionchange'
|
|
16
|
+
const INTERACT_ACTIVE = 'interact:active'
|
|
17
|
+
const SELECTED_CLASS = 'im-c-marker--selected'
|
|
18
|
+
const SVG_SEL = 'svg'
|
|
19
|
+
|
|
14
20
|
const makeEventBus = () => {
|
|
15
21
|
const listeners = {}
|
|
16
22
|
return {
|
|
@@ -29,7 +35,7 @@ const makeSymbolRegistry = (overrides = {}) => ({
|
|
|
29
35
|
})
|
|
30
36
|
|
|
31
37
|
const makeMarker = (overrides = {}) => ({
|
|
32
|
-
id:
|
|
38
|
+
id: MARKER_ID, isVisible: true, symbol: 'pin', ...overrides
|
|
33
39
|
})
|
|
34
40
|
|
|
35
41
|
const setup = ({ markers = [], mapSize = 'small', eventBus, symbolRegistry, mapStyle = 'outdoor' } = {}) => {
|
|
@@ -41,36 +47,62 @@ const setup = ({ markers = [], mapSize = 'small', eventBus, symbolRegistry, mapS
|
|
|
41
47
|
useService.mockReturnValue({ symbolRegistry: sr, eventBus: eb })
|
|
42
48
|
useMarkers.mockReturnValue({
|
|
43
49
|
markers: { items: markers, markerRefs },
|
|
44
|
-
markerRef: (id) => (el) => { if (el) markerRefs.set(id, el) }
|
|
50
|
+
markerRef: (id) => (el) => { if (el) { markerRefs.set(id, el) } }
|
|
45
51
|
})
|
|
46
52
|
return { eb, sr, result: render(<Markers />) }
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
// ─── Markers — basic rendering ────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
describe('Markers — basic rendering', () => {
|
|
50
58
|
it('renders nothing when mapStyle is not set', () => {
|
|
51
59
|
expect(setup({ mapStyle: null }).result.container.firstChild).toBeNull()
|
|
52
60
|
})
|
|
53
61
|
|
|
54
62
|
it('renders nothing when there are no markers', () => {
|
|
55
|
-
expect(setup().result.container.querySelectorAll(
|
|
63
|
+
expect(setup().result.container.querySelectorAll(SVG_SEL)).toHaveLength(0)
|
|
56
64
|
})
|
|
57
65
|
|
|
58
66
|
it('renders one svg per marker with correct id and classes', () => {
|
|
59
|
-
const { result } = setup({ markers: [makeMarker(), makeMarker({ id: 'b', symbol:
|
|
60
|
-
const [svg1, svg2] = result.container.querySelectorAll(
|
|
67
|
+
const { result } = setup({ markers: [makeMarker(), makeMarker({ id: 'b', symbol: null })] })
|
|
68
|
+
const [svg1, svg2] = result.container.querySelectorAll(SVG_SEL)
|
|
61
69
|
expect(svg1.getAttribute('id')).toBe('test-app-marker-marker-1')
|
|
62
70
|
expect(svg1).toHaveClass('im-c-marker', 'im-c-marker--pin')
|
|
63
71
|
expect(svg2).toHaveClass('im-c-marker--pin')
|
|
64
72
|
})
|
|
73
|
+
})
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
75
|
+
// ─── Markers — routing ────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
describe('Markers — routing', () => {
|
|
78
|
+
it('renders a LabelMarker for a marker with symbol: null', () => {
|
|
79
|
+
const marker = makeMarker({ id: 'lbl', label: 'My label', symbol: null, symbolSvgContent: null })
|
|
80
|
+
const { result } = setup({ markers: [marker] })
|
|
81
|
+
const wrapper = result.container.querySelector('.im-c-marker-wrapper--label')
|
|
82
|
+
expect(wrapper).toBeTruthy()
|
|
83
|
+
expect(wrapper.querySelector('.im-c-marker__label--standalone').textContent).toBe('My label')
|
|
84
|
+
expect(result.container.querySelector(SVG_SEL)).toBeNull()
|
|
72
85
|
})
|
|
73
86
|
|
|
87
|
+
it('renders a SymbolLabelMarker when showLabel is true', () => {
|
|
88
|
+
const marker = makeMarker({ label: 'Tooltip', showLabel: true })
|
|
89
|
+
const { result } = setup({ markers: [marker] })
|
|
90
|
+
const wrapper = result.container.querySelector('.im-c-marker-wrapper')
|
|
91
|
+
expect(wrapper).toBeTruthy()
|
|
92
|
+
expect(wrapper.querySelector('.im-c-marker__label').textContent).toBe('Tooltip')
|
|
93
|
+
expect(wrapper.querySelector(SVG_SEL)).toBeTruthy()
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('renders an svg without label when showLabel is false or absent', () => {
|
|
97
|
+
const { result } = setup({ markers: [makeMarker({ label: 'hidden', showLabel: false })] })
|
|
98
|
+
expect(result.container.querySelector(SVG_SEL)).toBeTruthy()
|
|
99
|
+
expect(result.container.querySelector('.im-c-marker__label')).toBeNull()
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// ─── Markers — symbol resolution ─────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
describe('Markers — symbol resolution', () => {
|
|
74
106
|
it('uses inline symbolSvgContent over the symbol registry', () => {
|
|
75
107
|
const sr = makeSymbolRegistry()
|
|
76
108
|
setup({ markers: [makeMarker({ symbolSvgContent: '<rect/>' })], symbolRegistry: sr })
|
|
@@ -81,23 +113,23 @@ describe('Markers', () => {
|
|
|
81
113
|
const sr = makeSymbolRegistry({
|
|
82
114
|
getDefaults: jest.fn(() => ({ symbolSvgContent: '<default-svg/>', viewBox: '0 0 38 38', anchor: [0.5, 1] }))
|
|
83
115
|
})
|
|
84
|
-
setup({ markers: [makeMarker({ symbol:
|
|
116
|
+
setup({ markers: [makeMarker({ symbol: null })], symbolRegistry: sr })
|
|
85
117
|
expect(sr.get).not.toHaveBeenCalled()
|
|
86
118
|
})
|
|
87
119
|
|
|
88
120
|
it('uses marker.viewBox when provided', () => {
|
|
89
|
-
const svg = setup({ markers: [makeMarker({ viewBox: '0 0 50 60' })] }).result.container.querySelector(
|
|
121
|
+
const svg = setup({ markers: [makeMarker({ viewBox: '0 0 50 60' })] }).result.container.querySelector(SVG_SEL)
|
|
90
122
|
expect(svg.getAttribute('viewBox')).toBe('0 0 50 60')
|
|
91
123
|
expect(svg.getAttribute('width')).toBe('50')
|
|
92
124
|
expect(svg.getAttribute('height')).toBe('60')
|
|
93
125
|
})
|
|
94
126
|
|
|
95
|
-
it("falls back to '0 0
|
|
127
|
+
it("falls back to '0 0 44 44' viewBox when none is provided", () => {
|
|
96
128
|
const sr = makeSymbolRegistry({
|
|
97
129
|
get: jest.fn(() => ({ svg: '<circle/>' })),
|
|
98
130
|
getDefaults: jest.fn(() => ({ symbol: 'pin' }))
|
|
99
131
|
})
|
|
100
|
-
expect(setup({ markers: [makeMarker()], symbolRegistry: sr }).result.container.querySelector(
|
|
132
|
+
expect(setup({ markers: [makeMarker()], symbolRegistry: sr }).result.container.querySelector(SVG_SEL).getAttribute('viewBox')).toBe('0 0 44 44')
|
|
101
133
|
})
|
|
102
134
|
|
|
103
135
|
it.each([
|
|
@@ -105,8 +137,8 @@ describe('Markers', () => {
|
|
|
105
137
|
['symbolDef.anchor', makeMarker(), { get: jest.fn(() => ({ svg: '<circle/>', viewBox: '0 0 38 38', anchor: [0, 0.5] })), getDefaults: jest.fn(() => ({ symbol: 'pin', viewBox: '0 0 38 38' })) }, '0px', '-19px'],
|
|
106
138
|
['[0.5, 0.5] fallback', makeMarker(), { get: jest.fn(() => ({ svg: '<circle/>', viewBox: '0 0 38 38' })), getDefaults: jest.fn(() => ({ symbol: 'pin', viewBox: '0 0 38 38' })) }, '-19px', '-19px']
|
|
107
139
|
])('resolveAnchor uses %s', (_, marker, srOverrides, left, top) => {
|
|
108
|
-
const sr = srOverrides ? makeSymbolRegistry(srOverrides) :
|
|
109
|
-
expect(setup({ markers: [marker], symbolRegistry: sr }).result.container.querySelector(
|
|
140
|
+
const sr = srOverrides ? makeSymbolRegistry(srOverrides) : null
|
|
141
|
+
expect(setup({ markers: [marker], symbolRegistry: sr }).result.container.querySelector(SVG_SEL)).toHaveStyle({ marginLeft: left, marginTop: top })
|
|
110
142
|
})
|
|
111
143
|
|
|
112
144
|
it.each([
|
|
@@ -115,24 +147,37 @@ describe('Markers', () => {
|
|
|
115
147
|
['large', '76', '76'],
|
|
116
148
|
['huge', '38', '38']
|
|
117
149
|
])('scales svg dimensions for mapSize=%s', (mapSize, width, height) => {
|
|
118
|
-
const svg = setup({ markers: [makeMarker()], mapSize }).result.container.querySelector(
|
|
150
|
+
const svg = setup({ markers: [makeMarker()], mapSize }).result.container.querySelector(SVG_SEL)
|
|
119
151
|
expect(svg.getAttribute('width')).toBe(width)
|
|
120
152
|
expect(svg.getAttribute('height')).toBe(height)
|
|
121
153
|
})
|
|
122
154
|
|
|
123
155
|
it('scales anchor offsets for medium mapSize', () => {
|
|
124
|
-
expect(setup({ markers: [makeMarker()], mapSize: 'medium' }).result.container.querySelector(
|
|
156
|
+
expect(setup({ markers: [makeMarker()], mapSize: 'medium' }).result.container.querySelector(SVG_SEL))
|
|
125
157
|
.toHaveStyle({ marginLeft: '-28.5px', marginTop: '-57px' })
|
|
126
158
|
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// ─── Markers — selection ──────────────────────────────────────────────────────
|
|
127
162
|
|
|
163
|
+
describe('Markers — selection', () => {
|
|
128
164
|
it('adds selected class and calls resolveSelected when marker is selected', () => {
|
|
129
165
|
const { eb, sr, result } = setup({ markers: [makeMarker()] })
|
|
130
|
-
act(() => eb.emit(
|
|
131
|
-
expect(result.container.querySelector(
|
|
166
|
+
act(() => eb.emit(SEL_CHANGE, { selectedMarkers: [MARKER_ID] }))
|
|
167
|
+
expect(result.container.querySelector(SVG_SEL)).toHaveClass(SELECTED_CLASS)
|
|
132
168
|
expect(sr.resolveSelected).toHaveBeenCalled()
|
|
133
169
|
expect(sr.resolve).not.toHaveBeenCalledAfter?.(sr.resolveSelected)
|
|
134
170
|
})
|
|
135
171
|
|
|
172
|
+
it('calls resolveActive when the marker is the active listbox item', () => {
|
|
173
|
+
const SET_ACTIVE = 'map:setactivefeature'
|
|
174
|
+
const sr = makeSymbolRegistry({ resolveActive: jest.fn(() => '<circle class="active"/>') })
|
|
175
|
+
const { eb } = setup({ markers: [makeMarker()], symbolRegistry: sr })
|
|
176
|
+
act(() => eb.emit(SET_ACTIVE, { id: MARKER_ID }))
|
|
177
|
+
expect(sr.resolveActive).toHaveBeenCalled()
|
|
178
|
+
expect(sr.resolveSelected).not.toHaveBeenCalled()
|
|
179
|
+
})
|
|
180
|
+
|
|
136
181
|
it('uses resolve (not resolveSelected) for unselected markers', () => {
|
|
137
182
|
const { sr } = setup({ markers: [makeMarker()] })
|
|
138
183
|
expect(sr.resolve).toHaveBeenCalled()
|
|
@@ -144,103 +189,35 @@ describe('Markers', () => {
|
|
|
144
189
|
['missing selectedMarkers key', {}]
|
|
145
190
|
])('deselects when selectionchange has %s', (_, payload) => {
|
|
146
191
|
const { eb, result } = setup({ markers: [makeMarker()] })
|
|
147
|
-
act(() => eb.emit(
|
|
148
|
-
act(() => eb.emit(
|
|
149
|
-
expect(result.container.querySelector(
|
|
192
|
+
act(() => eb.emit(SEL_CHANGE, { selectedMarkers: [MARKER_ID] }))
|
|
193
|
+
act(() => eb.emit(SEL_CHANGE, payload))
|
|
194
|
+
expect(result.container.querySelector(SVG_SEL)).not.toHaveClass(SELECTED_CLASS)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('handles interact:active with selectMarker in interactionModes', () => {
|
|
198
|
+
const { eb } = setup()
|
|
199
|
+
expect(() => act(() => eb.emit(INTERACT_ACTIVE, { active: true, interactionModes: ['selectMarker'] }))).not.toThrow()
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
it('handles interact:active with no interactionModes (uses default [])', () => {
|
|
203
|
+
const { eb } = setup()
|
|
204
|
+
expect(() => act(() => eb.emit(INTERACT_ACTIVE, { active: true }))).not.toThrow()
|
|
150
205
|
})
|
|
151
206
|
|
|
152
207
|
it('wires interact:active and interact:selectionchange on mount and removes them on unmount', () => {
|
|
153
208
|
const { eb, result } = setup()
|
|
154
|
-
expect(eb.on).toHaveBeenCalledWith(
|
|
155
|
-
expect(eb.on).toHaveBeenCalledWith(
|
|
209
|
+
expect(eb.on).toHaveBeenCalledWith(INTERACT_ACTIVE, expect.any(Function))
|
|
210
|
+
expect(eb.on).toHaveBeenCalledWith(SEL_CHANGE, expect.any(Function))
|
|
156
211
|
result.unmount()
|
|
157
|
-
expect(eb.off).toHaveBeenCalledWith(
|
|
158
|
-
expect(eb.off).toHaveBeenCalledWith(
|
|
212
|
+
expect(eb.off).toHaveBeenCalledWith(INTERACT_ACTIVE, expect.any(Function))
|
|
213
|
+
expect(eb.off).toHaveBeenCalledWith(SEL_CHANGE, expect.any(Function))
|
|
159
214
|
})
|
|
160
215
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
document.body.appendChild(viewport)
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
afterEach(() => {
|
|
171
|
-
if (viewport.parentNode) document.body.removeChild(viewport)
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
const activate = (eb) => act(() => eb.emit('interact:active', { active: true, interactionModes: ['selectMarker'] }))
|
|
175
|
-
const deactivate = (eb) => act(() => eb.emit('interact:active', { active: false, interactionModes: ['selectMarker'] }))
|
|
176
|
-
const fireMove = (clientX, clientY) => act(() => {
|
|
177
|
-
viewport.dispatchEvent(new MouseEvent('mousemove', { bubbles: true, clientX, clientY }))
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
const setupCursor = (markerBounds) => {
|
|
181
|
-
const eb = makeEventBus()
|
|
182
|
-
const markerRefs = new Map()
|
|
183
|
-
if (markerBounds) markerRefs.set('marker-1', { getBoundingClientRect: () => markerBounds })
|
|
184
|
-
useConfig.mockReturnValue({ id: 'test-app' })
|
|
185
|
-
useMap.mockReturnValue({ mapStyle: 'outdoor', mapSize: 'small' })
|
|
186
|
-
useService.mockReturnValue({ symbolRegistry: makeSymbolRegistry(), eventBus: eb })
|
|
187
|
-
useMarkers.mockReturnValue({ markers: { items: [makeMarker()], markerRefs }, markerRef: () => () => {} })
|
|
188
|
-
render(<Markers />)
|
|
189
|
-
return eb
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
it('does not track mousemove when interact is not active', () => {
|
|
193
|
-
setupCursor({ left: 0, top: 0, right: 50, bottom: 50 })
|
|
194
|
-
fireMove(20, 20)
|
|
195
|
-
expect(viewport.style.cursor).toBe('')
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
it('does not track mousemove when selectMarker is not in interactionModes', () => {
|
|
199
|
-
const eb = setupCursor({ left: 10, top: 10, right: 50, bottom: 50 })
|
|
200
|
-
act(() => eb.emit('interact:active', { active: true, interactionModes: ['selectFeature'] }))
|
|
201
|
-
fireMove(20, 20)
|
|
202
|
-
expect(viewport.style.cursor).toBe('')
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
it('does not track mousemove when interactionModes is absent from payload', () => {
|
|
206
|
-
const eb = setupCursor({ left: 10, top: 10, right: 50, bottom: 50 })
|
|
207
|
-
act(() => eb.emit('interact:active', { active: true }))
|
|
208
|
-
fireMove(20, 20)
|
|
209
|
-
expect(viewport.style.cursor).toBe('')
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it('does not track mousemove when viewport element is absent', () => {
|
|
213
|
-
document.body.removeChild(viewport)
|
|
214
|
-
const eb = setupCursor({ left: 0, top: 0, right: 50, bottom: 50 })
|
|
215
|
-
activate(eb)
|
|
216
|
-
expect(viewport.style.cursor).toBe('')
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
it('sets cursor to pointer when mousemove lands inside a marker', () => {
|
|
220
|
-
const eb = setupCursor({ left: 10, top: 10, right: 50, bottom: 50 })
|
|
221
|
-
activate(eb)
|
|
222
|
-
fireMove(20, 20)
|
|
223
|
-
expect(viewport.style.cursor).toBe('pointer')
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
it.each([
|
|
227
|
-
['outside all markers', 100, 100],
|
|
228
|
-
['marker has no ref element', 20, 20]
|
|
229
|
-
])('cursor stays empty when %s', (label, x, y) => {
|
|
230
|
-
const bounds = label.includes('no ref') ? null : { left: 10, top: 10, right: 50, bottom: 50 }
|
|
231
|
-
const eb = setupCursor(bounds)
|
|
232
|
-
activate(eb)
|
|
233
|
-
fireMove(x, y)
|
|
234
|
-
expect(viewport.style.cursor).toBe('')
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
it('clears cursor and stops tracking when interact becomes inactive', () => {
|
|
238
|
-
const eb = setupCursor({ left: 10, top: 10, right: 50, bottom: 50 })
|
|
239
|
-
activate(eb)
|
|
240
|
-
fireMove(20, 20)
|
|
241
|
-
expect(viewport.style.cursor).toBe('pointer')
|
|
242
|
-
deactivate(eb)
|
|
243
|
-
expect(viewport.style.cursor).toBe('')
|
|
244
|
-
})
|
|
216
|
+
it('adds selected class to SymbolLabelMarker wrapper and svg when selected', () => {
|
|
217
|
+
const { eb, result } = setup({ markers: [makeMarker({ label: 'Test', showLabel: true })] })
|
|
218
|
+
act(() => eb.emit(SEL_CHANGE, { selectedMarkers: [MARKER_ID] }))
|
|
219
|
+
const wrapper = result.container.querySelector('.im-c-marker-wrapper')
|
|
220
|
+
expect(wrapper).toHaveClass('im-c-marker-wrapper--selected')
|
|
221
|
+
expect(wrapper.querySelector(SVG_SEL)).toHaveClass(SELECTED_CLASS)
|
|
245
222
|
})
|
|
246
223
|
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { stringToKebab } from '../../../utils/stringToKebab.js'
|
|
2
|
+
|
|
3
|
+
const SymbolLabelMarker = ({ marker, mapId, markerRef, isSelected, symbolProps }) => { // NOSONAR: project does not use PropTypes
|
|
4
|
+
const { resolvedSvg, viewBox, anchor, shapeId, scaledWidth, scaledHeight } = symbolProps
|
|
5
|
+
return (
|
|
6
|
+
<div
|
|
7
|
+
ref={markerRef(marker.id)}
|
|
8
|
+
id={`${mapId}-marker-${marker.id}`}
|
|
9
|
+
className={[
|
|
10
|
+
'im-c-marker-wrapper',
|
|
11
|
+
`im-c-marker-wrapper--${stringToKebab(shapeId)}`,
|
|
12
|
+
isSelected && 'im-c-marker-wrapper--selected'
|
|
13
|
+
].filter(Boolean).join(' ')}
|
|
14
|
+
style={{
|
|
15
|
+
display: marker.isVisible ? 'block' : 'none',
|
|
16
|
+
marginLeft: `${-anchor[0] * scaledWidth}px`,
|
|
17
|
+
marginTop: `${-anchor[1] * scaledHeight}px`,
|
|
18
|
+
width: `${scaledWidth}px`,
|
|
19
|
+
height: `${scaledHeight}px`
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
<svg
|
|
23
|
+
className={[
|
|
24
|
+
'im-c-marker__svg',
|
|
25
|
+
`im-c-marker--${stringToKebab(shapeId)}`,
|
|
26
|
+
isSelected && 'im-c-marker--selected'
|
|
27
|
+
].filter(Boolean).join(' ')}
|
|
28
|
+
width={scaledWidth}
|
|
29
|
+
height={scaledHeight}
|
|
30
|
+
viewBox={viewBox}
|
|
31
|
+
overflow='visible'
|
|
32
|
+
aria-hidden='true'
|
|
33
|
+
>
|
|
34
|
+
<g dangerouslySetInnerHTML={{ __html: resolvedSvg }} />
|
|
35
|
+
</svg>
|
|
36
|
+
<div role='note' className='im-c-marker__label'>
|
|
37
|
+
{marker.label}
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default SymbolLabelMarker
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { render } from '@testing-library/react'
|
|
2
|
+
import SymbolLabelMarker from './SymbolLabelMarker.jsx'
|
|
3
|
+
|
|
4
|
+
const makeMarker = (overrides = {}) => ({ id: 'marker-1', isVisible: true, symbol: 'pin', label: 'Test', ...overrides })
|
|
5
|
+
const markerRef = () => () => {}
|
|
6
|
+
const makeSymbolProps = (overrides = {}) => ({
|
|
7
|
+
resolvedSvg: '<circle/>',
|
|
8
|
+
viewBox: '0 0 38 38',
|
|
9
|
+
anchor: [0.5, 0.5],
|
|
10
|
+
shapeId: 'pin',
|
|
11
|
+
scaledWidth: 38,
|
|
12
|
+
scaledHeight: 38,
|
|
13
|
+
...overrides
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('SymbolLabelMarker', () => {
|
|
17
|
+
it('renders the label text', () => {
|
|
18
|
+
const { getByText } = render(<SymbolLabelMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
19
|
+
expect(getByText('Test')).toBeTruthy()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('has wrapper id of mapId-marker-markerId', () => {
|
|
23
|
+
const { container } = render(<SymbolLabelMarker marker={makeMarker()} mapId='my-map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
24
|
+
expect(container.querySelector('.im-c-marker-wrapper').getAttribute('id')).toBe('my-map-marker-marker-1')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('applies shapeId to wrapper and svg class names', () => {
|
|
28
|
+
const { container } = render(<SymbolLabelMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps({ shapeId: 'flag' })} />)
|
|
29
|
+
expect(container.querySelector('.im-c-marker-wrapper')).toHaveClass('im-c-marker-wrapper--flag')
|
|
30
|
+
expect(container.querySelector('svg')).toHaveClass('im-c-marker--flag')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('adds selected classes to wrapper and svg when isSelected', () => {
|
|
34
|
+
const { container } = render(<SymbolLabelMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected symbolProps={makeSymbolProps()} />)
|
|
35
|
+
expect(container.querySelector('.im-c-marker-wrapper')).toHaveClass('im-c-marker-wrapper--selected')
|
|
36
|
+
expect(container.querySelector('svg')).toHaveClass('im-c-marker--selected')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('svg is aria-hidden', () => {
|
|
40
|
+
const { container } = render(<SymbolLabelMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
41
|
+
expect(container.querySelector('svg').getAttribute('aria-hidden')).toBe('true')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('label element has role note', () => {
|
|
45
|
+
const { container } = render(<SymbolLabelMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
46
|
+
expect(container.querySelector('.im-c-marker__label').getAttribute('role')).toBe('note')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it.each([
|
|
50
|
+
[true, 'block'],
|
|
51
|
+
[false, 'none']
|
|
52
|
+
])('display is %s when isVisible=%s', (isVisible, display) => {
|
|
53
|
+
const { container } = render(<SymbolLabelMarker marker={makeMarker({ isVisible })} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
54
|
+
expect(container.querySelector('.im-c-marker-wrapper')).toHaveStyle({ display })
|
|
55
|
+
})
|
|
56
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { stringToKebab } from '../../../utils/stringToKebab.js'
|
|
2
|
+
|
|
3
|
+
const SymbolMarker = ({ marker, mapId, markerRef, isSelected, symbolProps }) => { // NOSONAR: project does not use PropTypes
|
|
4
|
+
const { resolvedSvg, viewBox, anchor, shapeId, scaledWidth, scaledHeight } = symbolProps
|
|
5
|
+
return (
|
|
6
|
+
<svg // NOSONAR: role='img' with aria-label is the correct ARIA pattern for SVG icons; <img> is not appropriate here
|
|
7
|
+
ref={markerRef(marker.id)}
|
|
8
|
+
id={`${mapId}-marker-${marker.id}`}
|
|
9
|
+
className={[
|
|
10
|
+
'im-c-marker',
|
|
11
|
+
`im-c-marker--${stringToKebab(shapeId)}`,
|
|
12
|
+
isSelected && 'im-c-marker--selected'
|
|
13
|
+
].filter(Boolean).join(' ')}
|
|
14
|
+
width={scaledWidth}
|
|
15
|
+
height={scaledHeight}
|
|
16
|
+
viewBox={viewBox}
|
|
17
|
+
overflow='visible'
|
|
18
|
+
role='img'
|
|
19
|
+
aria-label={marker.label || 'Map marker'}
|
|
20
|
+
style={{
|
|
21
|
+
display: marker.isVisible ? 'block' : 'none',
|
|
22
|
+
marginLeft: `${-anchor[0] * scaledWidth}px`,
|
|
23
|
+
marginTop: `${-anchor[1] * scaledHeight}px`
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
<g dangerouslySetInnerHTML={{ __html: resolvedSvg }} />
|
|
27
|
+
</svg>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default SymbolMarker
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { render } from '@testing-library/react'
|
|
2
|
+
import SymbolMarker from './SymbolMarker.jsx'
|
|
3
|
+
|
|
4
|
+
const makeMarker = (overrides = {}) => ({ id: 'marker-1', isVisible: true, symbol: 'pin', ...overrides })
|
|
5
|
+
const markerRef = () => () => {}
|
|
6
|
+
const makeSymbolProps = (overrides = {}) => ({
|
|
7
|
+
resolvedSvg: '<circle/>',
|
|
8
|
+
viewBox: '0 0 38 38',
|
|
9
|
+
anchor: [0.5, 0.5],
|
|
10
|
+
shapeId: 'pin',
|
|
11
|
+
scaledWidth: 38,
|
|
12
|
+
scaledHeight: 38,
|
|
13
|
+
...overrides
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('SymbolMarker', () => {
|
|
17
|
+
it('has id of mapId-marker-markerId', () => {
|
|
18
|
+
const { container } = render(<SymbolMarker marker={makeMarker()} mapId='my-map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
19
|
+
expect(container.querySelector('svg').getAttribute('id')).toBe('my-map-marker-marker-1')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('has role img', () => {
|
|
23
|
+
const { container } = render(<SymbolMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
24
|
+
expect(container.querySelector('svg').getAttribute('role')).toBe('img')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('aria-label uses marker.label', () => {
|
|
28
|
+
const { container } = render(<SymbolMarker marker={makeMarker({ label: 'My location' })} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
29
|
+
expect(container.querySelector('svg').getAttribute('aria-label')).toBe('My location')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('aria-label falls back to Map marker when no label', () => {
|
|
33
|
+
const { container } = render(<SymbolMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
34
|
+
expect(container.querySelector('svg').getAttribute('aria-label')).toBe('Map marker')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('applies shapeId class', () => {
|
|
38
|
+
const { container } = render(<SymbolMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps({ shapeId: 'flag' })} />)
|
|
39
|
+
expect(container.querySelector('svg')).toHaveClass('im-c-marker--flag')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('adds selected class when isSelected', () => {
|
|
43
|
+
const { container } = render(<SymbolMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected symbolProps={makeSymbolProps()} />)
|
|
44
|
+
expect(container.querySelector('svg')).toHaveClass('im-c-marker--selected')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('does not add selected class when not selected', () => {
|
|
48
|
+
const { container } = render(<SymbolMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
49
|
+
expect(container.querySelector('svg')).not.toHaveClass('im-c-marker--selected')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it.each([
|
|
53
|
+
[true, 'block'],
|
|
54
|
+
[false, 'none']
|
|
55
|
+
])('display is %s when isVisible=%s', (isVisible, display) => {
|
|
56
|
+
const { container } = render(<SymbolMarker marker={makeMarker({ isVisible })} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps()} />)
|
|
57
|
+
expect(container.querySelector('svg')).toHaveStyle({ display })
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('sets width, height and viewBox from symbolProps', () => {
|
|
61
|
+
const { container } = render(<SymbolMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps({ scaledWidth: 50, scaledHeight: 60, viewBox: '0 0 50 60' })} />)
|
|
62
|
+
const svg = container.querySelector('svg')
|
|
63
|
+
expect(svg.getAttribute('width')).toBe('50')
|
|
64
|
+
expect(svg.getAttribute('height')).toBe('60')
|
|
65
|
+
expect(svg.getAttribute('viewBox')).toBe('0 0 50 60')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('sets marginLeft and marginTop from anchor and dimensions', () => {
|
|
69
|
+
const { container } = render(<SymbolMarker marker={makeMarker()} mapId='map' markerRef={markerRef} isSelected={false} symbolProps={makeSymbolProps({ anchor: [0.5, 1], scaledWidth: 38, scaledHeight: 38 })} />)
|
|
70
|
+
expect(container.querySelector('svg')).toHaveStyle({ marginLeft: '-19px', marginTop: '-38px' })
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -11,7 +11,7 @@ const computePanelState = (bpConfig, triggeringElement, focus, focusOnOpen) => {
|
|
|
11
11
|
const isDialog = !isAside && bpConfig.dismissible
|
|
12
12
|
const isModal = bpConfig.modal === true
|
|
13
13
|
const isDismissible = bpConfig.dismissible !== false
|
|
14
|
-
const shouldFocus = isModal || (focusOnOpen !== false &&
|
|
14
|
+
const shouldFocus = isModal || focusOnOpen === true || (focusOnOpen !== false && focus !== false && (focus === true || Boolean(triggeringElement)))
|
|
15
15
|
const buttonContainerEl = bpConfig.slot.endsWith('button') ? triggeringElement?.parentNode : undefined
|
|
16
16
|
return { isAside, isDialog, isModal, isDismissible, shouldFocus, buttonContainerEl }
|
|
17
17
|
}
|