@defra/interactive-map 0.0.14-alpha → 0.0.16-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.
Files changed (199) hide show
  1. package/assets/css/docusaurus.css +104 -0
  2. package/assets/images/favicon.svg +1 -0
  3. package/assets/images/hero.png +0 -0
  4. package/dist/css/index.css +1 -1
  5. package/dist/esm/im-core.js +1 -1
  6. package/dist/umd/im-core.js +1 -1
  7. package/dist/umd/index.js +1 -1
  8. package/docs/api/slot-map.svg +1 -0
  9. package/docs/api/slots.md +89 -6
  10. package/docs/api.md +1 -1
  11. package/docs/architecture.md +3 -1
  12. package/docs/{demo.mdx → examples.mdx} +1 -1
  13. package/docs/getting-started.md +1 -3
  14. package/docs/index.mdx +42 -0
  15. package/docs/plugins/interact.md +176 -55
  16. package/docs/plugins/map-styles.md +64 -7
  17. package/docs/plugins/search.md +207 -63
  18. package/docs/plugins.md +7 -15
  19. package/docusaurus.config.cjs +34 -34
  20. package/jest.setup.js +1 -1
  21. package/package.json +5 -4
  22. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  23. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
  24. package/plugins/beta/datasets/src/DatasetsInit.jsx +1 -1
  25. package/plugins/beta/datasets/src/api/addDataset.js +1 -1
  26. package/plugins/beta/datasets/src/api/hideDataset.js +1 -1
  27. package/plugins/beta/datasets/src/api/hideFeatures.js +1 -1
  28. package/plugins/beta/datasets/src/api/removeDataset.js +1 -1
  29. package/plugins/beta/datasets/src/api/showDataset.js +1 -1
  30. package/plugins/beta/datasets/src/api/showFeatures.js +1 -1
  31. package/plugins/beta/datasets/src/datasets.js +4 -4
  32. package/plugins/beta/datasets/src/defaults.js +1 -1
  33. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +5 -5
  34. package/plugins/beta/datasets/src/handleSetMapStyle.js +1 -1
  35. package/plugins/beta/datasets/src/manifest.js +7 -7
  36. package/plugins/beta/datasets/src/mapLayers.js +2 -3
  37. package/plugins/beta/datasets/src/panels/Key.jsx +31 -29
  38. package/plugins/beta/datasets/src/panels/Layers.jsx +8 -9
  39. package/plugins/beta/datasets/src/utils/bbox.js +4 -4
  40. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  41. package/plugins/beta/draw-es/src/DrawInit.jsx +16 -16
  42. package/plugins/beta/draw-es/src/api/addFeature.js +3 -3
  43. package/plugins/beta/draw-es/src/api/deleteFeature.js +3 -3
  44. package/plugins/beta/draw-es/src/api/editFeature.js +3 -3
  45. package/plugins/beta/draw-es/src/api/newPolygon.js +3 -3
  46. package/plugins/beta/draw-es/src/events.js +52 -20
  47. package/plugins/beta/draw-es/src/events.test.js +301 -0
  48. package/plugins/beta/draw-es/src/graphic.js +1 -1
  49. package/plugins/beta/draw-es/src/manifest.js +4 -4
  50. package/plugins/beta/draw-es/src/reducer.js +1 -1
  51. package/plugins/beta/draw-es/src/sketchViewModel.js +1 -1
  52. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  53. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  54. package/plugins/beta/draw-ml/src/DrawInit.jsx +49 -52
  55. package/plugins/beta/draw-ml/src/api/deleteFeature.js +1 -1
  56. package/plugins/beta/draw-ml/src/api/editFeature.js +8 -5
  57. package/plugins/beta/draw-ml/src/api/newLine.js +0 -1
  58. package/plugins/beta/draw-ml/src/api/newPolygon.js +0 -1
  59. package/plugins/beta/draw-ml/src/api/split.js +4 -4
  60. package/plugins/beta/draw-ml/src/defaults.js +1 -1
  61. package/plugins/beta/draw-ml/src/events.js +8 -6
  62. package/plugins/beta/draw-ml/src/manifest.js +15 -15
  63. package/plugins/beta/draw-ml/src/mapboxDraw.js +1 -1
  64. package/plugins/beta/draw-ml/src/mapboxSnap.js +17 -18
  65. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +31 -31
  66. package/plugins/beta/draw-ml/src/modes/disabledMode.js +1 -1
  67. package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +11 -11
  68. package/plugins/beta/draw-ml/src/modes/editVertex/undoHandlers.js +7 -7
  69. package/plugins/beta/draw-ml/src/modes/editVertex/vertexOperations.js +8 -8
  70. package/plugins/beta/draw-ml/src/modes/editVertex/vertexQueries.js +7 -7
  71. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +32 -24
  72. package/plugins/beta/draw-ml/src/reducer.js +1 -1
  73. package/plugins/beta/draw-ml/src/undoStack.js +4 -4
  74. package/plugins/beta/draw-ml/src/utils/snapHelpers.js +12 -12
  75. package/plugins/beta/draw-ml/src/utils/spatial.js +11 -11
  76. package/plugins/beta/frame/src/Frame.jsx +4 -4
  77. package/plugins/beta/frame/src/FrameInit.jsx +4 -4
  78. package/plugins/beta/frame/src/api/addFrame.js +1 -1
  79. package/plugins/beta/frame/src/api/editFeature.js +1 -1
  80. package/plugins/beta/frame/src/config.js +1 -1
  81. package/plugins/beta/frame/src/manifest.js +3 -3
  82. package/plugins/beta/frame/src/reducer.js +1 -1
  83. package/plugins/beta/frame/src/utils.js +1 -1
  84. package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -1
  85. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
  86. package/plugins/beta/map-styles/src/MapStyles.jsx +18 -18
  87. package/plugins/beta/map-styles/src/manifest.js +2 -2
  88. package/plugins/beta/scale-bar/src/ScaleBar.jsx +5 -5
  89. package/plugins/beta/use-location/src/UseLocation.jsx +1 -1
  90. package/plugins/beta/use-location/src/defaults.js +1 -1
  91. package/plugins/beta/use-location/src/events.js +3 -3
  92. package/plugins/interact/src/InteractInit.jsx +1 -2
  93. package/plugins/interact/src/api/enable.js +8 -5
  94. package/plugins/interact/src/api/enable.test.js +2 -2
  95. package/plugins/interact/src/api/selectFeature.js +4 -4
  96. package/plugins/interact/src/api/unselectFeature.js +5 -5
  97. package/plugins/interact/src/defaults.js +0 -1
  98. package/plugins/interact/src/events.test.js +15 -15
  99. package/plugins/interact/src/hooks/useHighlightSync.js +1 -1
  100. package/plugins/interact/src/hooks/useInteractionHandlers.js +2 -2
  101. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +5 -5
  102. package/plugins/interact/src/manifest.js +2 -2
  103. package/plugins/interact/src/manifest.test.js +3 -4
  104. package/plugins/interact/src/reducer.js +3 -3
  105. package/plugins/interact/src/reducer.test.js +0 -1
  106. package/plugins/interact/src/utils/spatial.js +10 -10
  107. package/plugins/interact/src/utils/spatial.test.js +14 -14
  108. package/plugins/search/dist/css/index.css +1 -1
  109. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  110. package/plugins/search/dist/esm/index.js +1 -1
  111. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  112. package/plugins/search/dist/umd/index.js +1 -1
  113. package/plugins/search/src/Search.jsx +7 -6
  114. package/plugins/search/src/Search.test.jsx +23 -23
  115. package/plugins/search/src/components/CloseButton/CloseButton.jsx +15 -15
  116. package/plugins/search/src/components/CloseButton/CloseButton.test.jsx +2 -2
  117. package/plugins/search/src/components/Form/Form.jsx +14 -14
  118. package/plugins/search/src/components/Form/Form.test.jsx +11 -11
  119. package/plugins/search/src/components/OpenButton/OpenButton.jsx +16 -15
  120. package/plugins/search/src/components/OpenButton/OpenButton.test.jsx +6 -2
  121. package/plugins/search/src/components/SubmitButton/SubmitButton.jsx +15 -15
  122. package/plugins/search/src/components/Suggestions/Suggestions.jsx +6 -6
  123. package/plugins/search/src/components/Suggestions/Suggestions.test.jsx +4 -4
  124. package/plugins/search/src/datasets.js +12 -13
  125. package/plugins/search/src/datasets.test.js +1 -1
  126. package/plugins/search/src/defaults.js +1 -1
  127. package/plugins/search/src/events/fetchSuggestions.js +4 -4
  128. package/plugins/search/src/events/fetchSuggestions.test.js +5 -5
  129. package/plugins/search/src/events/formHandlers.js +3 -3
  130. package/plugins/search/src/events/formHandlers.test.js +1 -1
  131. package/plugins/search/src/events/index.js +2 -2
  132. package/plugins/search/src/events/index.test.js +2 -2
  133. package/plugins/search/src/events/inputHandlers.js +4 -4
  134. package/plugins/search/src/events/inputHandlers.test.js +1 -1
  135. package/plugins/search/src/events/suggestionHandlers.js +2 -2
  136. package/plugins/search/src/events/suggestionHandlers.test.js +1 -1
  137. package/plugins/search/src/index.js +2 -1
  138. package/plugins/search/src/index.test.js +3 -3
  139. package/plugins/search/src/manifest.js +6 -4
  140. package/plugins/search/src/reducer.js +1 -2
  141. package/plugins/search/src/reducer.test.js +2 -2
  142. package/plugins/search/src/search.scss +18 -6
  143. package/plugins/search/src/utils/parseOsNamesResults.js +1 -2
  144. package/plugins/search/src/utils/parseOsNamesResults.test.js +2 -2
  145. package/plugins/search/src/utils/updateMap.js +1 -1
  146. package/plugins/search/src/utils/updateMap.test.js +5 -5
  147. package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
  148. package/providers/beta/esri/src/esriProvider.js +5 -5
  149. package/providers/beta/esri/src/utils/coords.js +1 -1
  150. package/providers/beta/esri/src/utils/esriFixes.js +1 -1
  151. package/providers/beta/esri/src/utils/query.js +4 -4
  152. package/providers/beta/esri/src/utils/spatial.js +1 -2
  153. package/providers/beta/esri/src/utils/spatial.test.js +4 -1
  154. package/providers/beta/open-names/src/utils/mapToLocationModel.test.js +1 -1
  155. package/providers/maplibre/src/appEvents.test.js +1 -1
  156. package/providers/maplibre/src/index.js +1 -1
  157. package/providers/maplibre/src/index.test.js +3 -5
  158. package/providers/maplibre/src/mapEvents.test.js +15 -5
  159. package/providers/maplibre/src/maplibreProvider.test.js +6 -2
  160. package/providers/maplibre/src/utils/calculateLinearTextSize.js +4 -4
  161. package/providers/maplibre/src/utils/calculateLinearTextSize.test.js +3 -3
  162. package/providers/maplibre/src/utils/detectWebgl.test.js +1 -1
  163. package/providers/maplibre/src/utils/highlightFeatures.js +2 -2
  164. package/providers/maplibre/src/utils/highlightFeatures.test.js +12 -6
  165. package/providers/maplibre/src/utils/labels.js +19 -20
  166. package/providers/maplibre/src/utils/labels.test.js +15 -13
  167. package/providers/maplibre/src/utils/maplibreFixes.test.js +1 -1
  168. package/providers/maplibre/src/utils/queryFeatures.js +6 -6
  169. package/providers/maplibre/src/utils/queryFeatures.test.js +13 -13
  170. package/providers/maplibre/src/utils/spatial.js +0 -1
  171. package/providers/maplibre/src/utils/spatial.test.js +26 -27
  172. package/src/App/components/Panel/Panel.module.scss +1 -0
  173. package/src/App/hooks/useLayoutMeasurements.js +1 -10
  174. package/src/App/hooks/useLayoutMeasurements.test.js +2 -5
  175. package/src/App/hooks/useVisibleGeometry.js +7 -13
  176. package/src/App/hooks/useVisibleGeometry.test.js +72 -47
  177. package/src/App/layout/Layout.jsx +0 -3
  178. package/src/App/layout/Layout.test.jsx +0 -1
  179. package/src/App/layout/layout.module.scss +11 -77
  180. package/src/App/registry/pluginRegistry.js +17 -0
  181. package/src/App/registry/pluginRegistry.test.js +33 -0
  182. package/src/App/renderer/HtmlElementHost.jsx +0 -1
  183. package/src/App/renderer/HtmlElementHost.test.jsx +20 -11
  184. package/src/App/renderer/mapButtons.js +3 -2
  185. package/src/App/renderer/mapPanels.test.js +3 -3
  186. package/src/App/renderer/slotHelpers.js +2 -2
  187. package/src/App/renderer/slotHelpers.test.js +3 -3
  188. package/src/App/renderer/slots.js +0 -3
  189. package/src/App/store/AppProvider.jsx +0 -1
  190. package/src/App/store/appDispatchMiddleware.js +33 -1
  191. package/src/App/store/appDispatchMiddleware.test.js +250 -222
  192. package/src/config/appConfig.js +4 -4
  193. package/src/utils/getSafeZoneInset.js +139 -42
  194. package/src/utils/getSafeZoneInset.test.js +298 -122
  195. package/src/utils/logger.js +6 -0
  196. package/src/utils/logger.test.js +32 -0
  197. package/webpack.dev.mjs +22 -18
  198. package/docs/govuk-prototype.md +0 -23
  199. package/docs/index.md +0 -19
@@ -10,227 +10,255 @@ const run = (action, state, panelConfig = {}) =>
10
10
 
11
11
  const flushMicrotasks = () => new Promise(queueMicrotask)
12
12
 
13
- describe('appDispatchMiddleware', () => {
14
- beforeEach(() => {
15
- jest.clearAllMocks()
16
- })
17
-
18
- describe('CLOSE_PANEL', () => {
19
- it('emits panel closed event', async () => {
20
- run(
21
- { type: 'CLOSE_PANEL', payload: 'testPanel' },
22
- { openPanels: {} }
23
- )
24
-
25
- await flushMicrotasks()
26
-
27
- expect(eventBus.emit).toHaveBeenCalledWith(
28
- events.APP_PANEL_CLOSED,
29
- { panelId: 'testPanel' }
30
- )
31
- })
32
- })
33
-
34
- describe('CLOSE_ALL_PANELS', () => {
35
- it('emits closed event for each panel', async () => {
36
- run(
37
- { type: 'CLOSE_ALL_PANELS' },
38
- { openPanels: { panel1: {}, panel2: {} } }
39
- )
40
-
41
- await flushMicrotasks()
42
-
43
- expect(eventBus.emit).toHaveBeenCalledTimes(2)
44
- expect(eventBus.emit).toHaveBeenCalledWith(
45
- events.APP_PANEL_CLOSED,
46
- { panelId: 'panel1' }
47
- )
48
- expect(eventBus.emit).toHaveBeenCalledWith(
49
- events.APP_PANEL_CLOSED,
50
- { panelId: 'panel2' }
51
- )
52
- })
53
- })
54
-
55
- describe('OPEN_PANEL', () => {
56
- it('emits panel opened event for regular panel', async () => {
57
- run(
58
- {
59
- type: 'OPEN_PANEL',
60
- payload: { panelId: 'newPanel', props: { foo: 'bar' } }
61
- },
62
- { openPanels: {}, breakpoint: 'md' },
63
- { newPanel: { md: { exclusive: false, modal: false } } }
64
- )
65
-
66
- await flushMicrotasks()
67
-
68
- expect(eventBus.emit).toHaveBeenCalledWith(
69
- events.APP_PANEL_OPENED,
70
- { panelId: 'newPanel', props: { foo: 'bar' } }
71
- )
72
- })
73
-
74
- it('closes all non-exclusive panels when opening exclusive non-modal panel', async () => {
75
- run(
76
- {
77
- type: 'OPEN_PANEL',
78
- payload: { panelId: 'exclusivePanel', props: {} }
79
- },
80
- {
81
- openPanels: {
82
- regularPanel1: {},
83
- regularPanel2: {},
84
- exclusivePanel2: {}
85
- },
86
- breakpoint: 'md'
87
- },
88
- {
89
- exclusivePanel: { md: { exclusive: true, modal: false } },
90
- regularPanel1: { md: { exclusive: false, modal: false } },
91
- regularPanel2: { md: { exclusive: false, modal: false } },
92
- exclusivePanel2: { md: { exclusive: true, modal: false } }
93
- }
94
- )
95
-
96
- await flushMicrotasks()
97
-
98
- expect(eventBus.emit).toHaveBeenCalledTimes(3)
99
- expect(eventBus.emit).toHaveBeenCalledWith(
100
- events.APP_PANEL_CLOSED,
101
- { panelId: 'regularPanel1' }
102
- )
103
- expect(eventBus.emit).toHaveBeenCalledWith(
104
- events.APP_PANEL_CLOSED,
105
- { panelId: 'regularPanel2' }
106
- )
107
- expect(eventBus.emit).toHaveBeenCalledWith(
108
- events.APP_PANEL_OPENED,
109
- { panelId: 'exclusivePanel', props: {} }
110
- )
111
- })
112
-
113
- it('closes all exclusive non-modal panels when opening regular panel', async () => {
114
- run(
115
- {
116
- type: 'OPEN_PANEL',
117
- payload: { panelId: 'regularPanel', props: {} }
118
- },
119
- {
120
- openPanels: {
121
- exclusivePanel1: {},
122
- exclusivePanel2: {},
123
- exclusiveModalPanel: {},
124
- regularPanel2: {}
125
- },
126
- breakpoint: 'md'
127
- },
128
- {
129
- regularPanel: { md: { exclusive: false, modal: false } },
130
- exclusivePanel1: { md: { exclusive: true, modal: false } },
131
- exclusivePanel2: { md: { exclusive: true, modal: false } },
132
- exclusiveModalPanel: { md: { exclusive: true, modal: true } },
133
- regularPanel2: { md: { exclusive: false, modal: false } }
134
- }
135
- )
136
-
137
- await flushMicrotasks()
138
-
139
- expect(eventBus.emit).toHaveBeenCalledTimes(3)
140
- expect(eventBus.emit).toHaveBeenCalledWith(
141
- events.APP_PANEL_CLOSED,
142
- { panelId: 'exclusivePanel1' }
143
- )
144
- expect(eventBus.emit).toHaveBeenCalledWith(
145
- events.APP_PANEL_CLOSED,
146
- { panelId: 'exclusivePanel2' }
147
- )
148
- expect(eventBus.emit).toHaveBeenCalledWith(
149
- events.APP_PANEL_OPENED,
150
- { panelId: 'regularPanel', props: {} }
151
- )
152
- })
153
-
154
- it('does not close any panels when opening modal', async () => {
155
- run(
156
- {
157
- type: 'OPEN_PANEL',
158
- payload: { panelId: 'modalPanel', props: {} }
159
- },
160
- {
161
- openPanels: { regularPanel: {}, exclusivePanel: {} },
162
- breakpoint: 'md'
163
- },
164
- {
165
- modalPanel: { md: { modal: true } },
166
- regularPanel: { md: { exclusive: false, modal: false } },
167
- exclusivePanel: { md: { exclusive: true, modal: false } }
168
- }
169
- )
170
-
171
- await flushMicrotasks()
172
-
173
- expect(eventBus.emit).toHaveBeenCalledTimes(1)
174
- expect(eventBus.emit).toHaveBeenCalledWith(
175
- events.APP_PANEL_OPENED,
176
- { panelId: 'modalPanel', props: {} }
177
- )
178
- })
179
- })
180
-
181
- describe('ADD_PANEL', () => {
182
- it('emits APP_PANEL_OPENED with slot when panel opens by default', async () => {
183
- run(
184
- { type: 'ADD_PANEL', payload: { id: 'newPanel', config: {} } },
185
- { breakpoint: 'desktop' }
186
- )
187
-
188
- await flushMicrotasks()
189
-
190
- expect(eventBus.emit).toHaveBeenCalledWith(
191
- events.APP_PANEL_OPENED,
192
- { panelId: 'newPanel', slot: 'inset' }
193
- )
194
- })
195
-
196
- it('emits APP_PANEL_OPENED with visibleGeometry when provided in config', async () => {
197
- const visibleGeometry = { type: 'Feature', geometry: { type: 'Point', coordinates: [1, 2] }, properties: {} }
198
- run(
199
- { type: 'ADD_PANEL', payload: { id: 'geoPanel', config: { visibleGeometry } } },
200
- { breakpoint: 'desktop' }
201
- )
202
-
203
- await flushMicrotasks()
204
-
205
- expect(eventBus.emit).toHaveBeenCalledWith(
206
- events.APP_PANEL_OPENED,
207
- { panelId: 'geoPanel', slot: 'inset', visibleGeometry }
208
- )
209
- })
210
-
211
- it('does not emit APP_PANEL_OPENED when breakpoint config sets open: false', async () => {
212
- run(
213
- { type: 'ADD_PANEL', payload: { id: 'hiddenPanel', config: { desktop: { open: false } } } },
214
- { breakpoint: 'desktop' }
215
- )
216
-
217
- await flushMicrotasks()
218
-
219
- expect(eventBus.emit).not.toHaveBeenCalled()
220
- })
221
-
222
- it('emits APP_PANEL_OPENED with slot for mobile breakpoint', async () => {
223
- run(
224
- { type: 'ADD_PANEL', payload: { id: 'mobilePanel', config: {} } },
225
- { breakpoint: 'mobile' }
226
- )
227
-
228
- await flushMicrotasks()
229
-
230
- expect(eventBus.emit).toHaveBeenCalledWith(
231
- events.APP_PANEL_OPENED,
232
- { panelId: 'mobilePanel', slot: 'bottom' }
233
- )
234
- })
13
+ const INVALID_SLOT = 'invalid-slot'
14
+ const LOG_PREFIX = '[interactive-map]'
15
+
16
+ beforeEach(() => {
17
+ jest.clearAllMocks()
18
+ })
19
+
20
+ describe('CLOSE_PANEL', () => {
21
+ it('emits panel closed event', async () => {
22
+ run(
23
+ { type: 'CLOSE_PANEL', payload: 'testPanel' },
24
+ { openPanels: {} }
25
+ )
26
+
27
+ await flushMicrotasks()
28
+
29
+ expect(eventBus.emit).toHaveBeenCalledWith(
30
+ events.APP_PANEL_CLOSED,
31
+ { panelId: 'testPanel' }
32
+ )
33
+ })
34
+ })
35
+
36
+ describe('CLOSE_ALL_PANELS', () => {
37
+ it('emits closed event for each panel', async () => {
38
+ run(
39
+ { type: 'CLOSE_ALL_PANELS' },
40
+ { openPanels: { panel1: {}, panel2: {} } }
41
+ )
42
+
43
+ await flushMicrotasks()
44
+
45
+ expect(eventBus.emit).toHaveBeenCalledTimes(2)
46
+ expect(eventBus.emit).toHaveBeenCalledWith(
47
+ events.APP_PANEL_CLOSED,
48
+ { panelId: 'panel1' }
49
+ )
50
+ expect(eventBus.emit).toHaveBeenCalledWith(
51
+ events.APP_PANEL_CLOSED,
52
+ { panelId: 'panel2' }
53
+ )
54
+ })
55
+ })
56
+
57
+ describe('OPEN_PANEL — regular panel', () => {
58
+ it('emits panel opened event', async () => {
59
+ run(
60
+ {
61
+ type: 'OPEN_PANEL',
62
+ payload: { panelId: 'newPanel', props: { foo: 'bar' } }
63
+ },
64
+ { openPanels: {}, breakpoint: 'md' },
65
+ { newPanel: { md: { exclusive: false, modal: false } } }
66
+ )
67
+
68
+ await flushMicrotasks()
69
+
70
+ expect(eventBus.emit).toHaveBeenCalledWith(
71
+ events.APP_PANEL_OPENED,
72
+ { panelId: 'newPanel', props: { foo: 'bar' } }
73
+ )
74
+ })
75
+ })
76
+
77
+ const CLOSED_PLUS_OPENED = 3
78
+
79
+ describe('OPEN_PANEL — opening exclusive closes non-exclusive', () => {
80
+ it('closes all non-exclusive panels', async () => {
81
+ run(
82
+ { type: 'OPEN_PANEL', payload: { panelId: 'exclusivePanel', props: {} } },
83
+ { openPanels: { regularPanel1: {}, regularPanel2: {}, exclusivePanel2: {} }, breakpoint: 'md' },
84
+ {
85
+ exclusivePanel: { md: { exclusive: true, modal: false } },
86
+ regularPanel1: { md: { exclusive: false, modal: false } },
87
+ regularPanel2: { md: { exclusive: false, modal: false } },
88
+ exclusivePanel2: { md: { exclusive: true, modal: false } }
89
+ }
90
+ )
91
+ await flushMicrotasks()
92
+ expect(eventBus.emit).toHaveBeenCalledTimes(CLOSED_PLUS_OPENED)
93
+ expect(eventBus.emit).toHaveBeenCalledWith(events.APP_PANEL_CLOSED, { panelId: 'regularPanel1' })
94
+ expect(eventBus.emit).toHaveBeenCalledWith(events.APP_PANEL_CLOSED, { panelId: 'regularPanel2' })
95
+ expect(eventBus.emit).toHaveBeenCalledWith(events.APP_PANEL_OPENED, { panelId: 'exclusivePanel', props: {} })
96
+ })
97
+ })
98
+
99
+ describe('OPEN_PANEL — opening regular closes exclusive non-modal', () => {
100
+ it('closes all exclusive non-modal panels', async () => {
101
+ run(
102
+ { type: 'OPEN_PANEL', payload: { panelId: 'regularPanel', props: {} } },
103
+ { openPanels: { exclusivePanel1: {}, exclusivePanel2: {}, exclusiveModalPanel: {}, regularPanel2: {} }, breakpoint: 'md' },
104
+ {
105
+ regularPanel: { md: { exclusive: false, modal: false } },
106
+ exclusivePanel1: { md: { exclusive: true, modal: false } },
107
+ exclusivePanel2: { md: { exclusive: true, modal: false } },
108
+ exclusiveModalPanel: { md: { exclusive: true, modal: true } },
109
+ regularPanel2: { md: { exclusive: false, modal: false } }
110
+ }
111
+ )
112
+ await flushMicrotasks()
113
+ expect(eventBus.emit).toHaveBeenCalledTimes(CLOSED_PLUS_OPENED)
114
+ expect(eventBus.emit).toHaveBeenCalledWith(events.APP_PANEL_CLOSED, { panelId: 'exclusivePanel1' })
115
+ expect(eventBus.emit).toHaveBeenCalledWith(events.APP_PANEL_CLOSED, { panelId: 'exclusivePanel2' })
116
+ expect(eventBus.emit).toHaveBeenCalledWith(events.APP_PANEL_OPENED, { panelId: 'regularPanel', props: {} })
117
+ })
118
+ })
119
+
120
+ describe('OPEN_PANEL — modal panel', () => {
121
+ it('does not close any panels when opening modal', async () => {
122
+ run(
123
+ {
124
+ type: 'OPEN_PANEL',
125
+ payload: { panelId: 'modalPanel', props: {} }
126
+ },
127
+ {
128
+ openPanels: { regularPanel: {}, exclusivePanel: {} },
129
+ breakpoint: 'md'
130
+ },
131
+ {
132
+ modalPanel: { md: { modal: true } },
133
+ regularPanel: { md: { exclusive: false, modal: false } },
134
+ exclusivePanel: { md: { exclusive: true, modal: false } }
135
+ }
136
+ )
137
+
138
+ await flushMicrotasks()
139
+
140
+ expect(eventBus.emit).toHaveBeenCalledTimes(1)
141
+ expect(eventBus.emit).toHaveBeenCalledWith(
142
+ events.APP_PANEL_OPENED,
143
+ { panelId: 'modalPanel', props: {} }
144
+ )
145
+ })
146
+ })
147
+
148
+ describe('ADD_BUTTON', () => {
149
+ beforeEach(() => jest.spyOn(console, 'warn').mockImplementation(() => {}))
150
+ afterEach(() => jest.restoreAllMocks())
151
+
152
+ it('warns when button has an invalid slot', () => {
153
+ run(
154
+ { type: 'ADD_BUTTON', payload: { id: 'myBtn', config: { desktop: { slot: INVALID_SLOT } } } },
155
+ { breakpoint: 'desktop' }
156
+ )
157
+ expect(console.warn).toHaveBeenCalledWith(LOG_PREFIX, expect.stringContaining(INVALID_SLOT))
158
+ })
159
+
160
+ it('does not warn when button has a valid slot', () => {
161
+ run(
162
+ { type: 'ADD_BUTTON', payload: { id: 'myBtn', config: { mobile: { slot: 'top-left' }, tablet: { slot: 'top-left' }, desktop: { slot: 'top-left' } } } },
163
+ { breakpoint: 'desktop' }
164
+ )
165
+ expect(console.warn).not.toHaveBeenCalled()
166
+ })
167
+ })
168
+
169
+ describe('ADD_CONTROL', () => {
170
+ beforeEach(() => jest.spyOn(console, 'warn').mockImplementation(() => {}))
171
+ afterEach(() => jest.restoreAllMocks())
172
+
173
+ it('warns when control has an invalid slot', () => {
174
+ run(
175
+ { type: 'ADD_CONTROL', payload: { id: 'myCtrl', config: { desktop: { slot: INVALID_SLOT } } } },
176
+ { breakpoint: 'desktop' }
177
+ )
178
+ expect(console.warn).toHaveBeenCalledWith(LOG_PREFIX, expect.stringContaining(INVALID_SLOT))
179
+ })
180
+
181
+ it('does not warn when control has a valid slot', () => {
182
+ run(
183
+ { type: 'ADD_CONTROL', payload: { id: 'myCtrl', config: { mobile: { slot: 'bottom' }, tablet: { slot: 'top-left' }, desktop: { slot: 'top-left' } } } },
184
+ { breakpoint: 'desktop' }
185
+ )
186
+ expect(console.warn).not.toHaveBeenCalled()
187
+ })
188
+ })
189
+
190
+ describe('ADD_PANEL', () => {
191
+ it('emits APP_PANEL_OPENED with slot when panel opens by default', async () => {
192
+ run(
193
+ { type: 'ADD_PANEL', payload: { id: 'newPanel', config: {} } },
194
+ { breakpoint: 'desktop' }
195
+ )
196
+
197
+ await flushMicrotasks()
198
+
199
+ expect(eventBus.emit).toHaveBeenCalledWith(
200
+ events.APP_PANEL_OPENED,
201
+ { panelId: 'newPanel', slot: 'left-top' }
202
+ )
203
+ })
204
+
205
+ it('emits APP_PANEL_OPENED with visibleGeometry when provided in config', async () => {
206
+ const visibleGeometry = { type: 'Feature', geometry: { type: 'Point', coordinates: [1, 2] }, properties: {} }
207
+ run(
208
+ { type: 'ADD_PANEL', payload: { id: 'geoPanel', config: { visibleGeometry } } },
209
+ { breakpoint: 'desktop' }
210
+ )
211
+
212
+ await flushMicrotasks()
213
+
214
+ expect(eventBus.emit).toHaveBeenCalledWith(
215
+ events.APP_PANEL_OPENED,
216
+ { panelId: 'geoPanel', slot: 'left-top', visibleGeometry }
217
+ )
218
+ })
219
+
220
+ it('does not emit APP_PANEL_OPENED when breakpoint config sets open: false', async () => {
221
+ run(
222
+ { type: 'ADD_PANEL', payload: { id: 'hiddenPanel', config: { desktop: { open: false } } } },
223
+ { breakpoint: 'desktop' }
224
+ )
225
+
226
+ await flushMicrotasks()
227
+
228
+ expect(eventBus.emit).not.toHaveBeenCalled()
229
+ })
230
+
231
+ it('emits APP_PANEL_OPENED with slot for mobile breakpoint', async () => {
232
+ run(
233
+ { type: 'ADD_PANEL', payload: { id: 'mobilePanel', config: {} } },
234
+ { breakpoint: 'mobile' }
235
+ )
236
+
237
+ await flushMicrotasks()
238
+
239
+ expect(eventBus.emit).toHaveBeenCalledWith(
240
+ events.APP_PANEL_OPENED,
241
+ { panelId: 'mobilePanel', slot: 'bottom' }
242
+ )
243
+ })
244
+
245
+ it('warns when panel has an invalid slot', () => {
246
+ jest.spyOn(console, 'warn').mockImplementation(() => {})
247
+ run(
248
+ { type: 'ADD_PANEL', payload: { id: 'badPanel', config: { desktop: { slot: INVALID_SLOT, open: false } } } },
249
+ { breakpoint: 'desktop' }
250
+ )
251
+ expect(console.warn).toHaveBeenCalledWith(LOG_PREFIX, expect.stringContaining(INVALID_SLOT))
252
+ jest.restoreAllMocks()
253
+ })
254
+
255
+ it('does not warn for a panel with a button-adjacent slot', () => {
256
+ jest.spyOn(console, 'warn').mockImplementation(() => {})
257
+ run(
258
+ { type: 'ADD_PANEL', payload: { id: 'btnPanel', config: { desktop: { slot: 'left-top-button', open: false } } } },
259
+ { breakpoint: 'desktop' }
260
+ )
261
+ expect(console.warn).not.toHaveBeenCalled()
262
+ jest.restoreAllMocks()
235
263
  })
236
264
  })
@@ -120,14 +120,14 @@ export const defaultPanelConfig = {
120
120
  showLabel: true
121
121
  },
122
122
  tablet: {
123
- slot: 'inset',
123
+ slot: 'left-top',
124
124
  open: true,
125
125
  dismissible: true,
126
126
  modal: false,
127
127
  showLabel: true
128
128
  },
129
129
  desktop: {
130
- slot: 'inset',
130
+ slot: 'left-top',
131
131
  open: true,
132
132
  dismissible: true,
133
133
  modal: false,
@@ -144,10 +144,10 @@ export const defaultControlConfig = {
144
144
  slot: 'bottom'
145
145
  },
146
146
  tablet: {
147
- slot: 'inset'
147
+ slot: 'top-left'
148
148
  },
149
149
  desktop: {
150
- slot: 'inset'
150
+ slot: 'top-left'
151
151
  }
152
152
  }
153
153