@defra/interactive-map 0.0.40-alpha → 0.0.41-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 (351) hide show
  1. package/dist/css/index.css +1 -1
  2. package/dist/esm/im-core.js +1 -1
  3. package/dist/esm/im-shell.js +1 -1
  4. package/dist/umd/im-core.js +1 -1
  5. package/dist/umd/index.js +1 -1
  6. package/docs/api/control-definition.md +9 -1
  7. package/docs/api/slots.md +26 -5
  8. package/docs/assets/images/slot-map.svg +61 -262
  9. package/docs/examples/draw-tools.mdx +3 -3
  10. package/docs/plugins/draw.md +79 -16
  11. package/docs/plugins/search.md +1 -1
  12. package/govuk-prototype-kit.config.json +2 -0
  13. package/jest.setup.js +5 -0
  14. package/package.json +9 -1
  15. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  16. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  17. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  18. package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
  19. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
  20. package/plugins/beta/map-styles/dist/umd/index.js +1 -1
  21. package/plugins/datasets/dist/css/index.css +1 -1
  22. package/plugins/datasets/dist/esm/esriLayerAdapter.js +1 -1
  23. package/plugins/datasets/dist/esm/im-datasets-ml-adapter.js +1 -1
  24. package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
  25. package/plugins/datasets/dist/umd/im-datasets-esri-adapter.js +1 -1
  26. package/plugins/datasets/dist/umd/im-datasets-ml-adapter.js +1 -1
  27. package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
  28. package/plugins/datasets/dist/umd/index.js +1 -1
  29. package/plugins/datasets/src/adapters/esri/esriLayerAdapter.test.js +175 -0
  30. package/plugins/datasets/src/adapters/esri/registry/esriDataset.test.js +55 -0
  31. package/plugins/datasets/src/adapters/loadLayerAdapter.test.js +61 -0
  32. package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +14 -0
  33. package/plugins/datasets/src/api/setOpacity.test.js +9 -0
  34. package/plugins/datasets/src/components/LayersMenu/LayersMenu.jsx +10 -25
  35. package/plugins/datasets/src/components/LayersMenu/LayersMenu.test.jsx +0 -42
  36. package/plugins/datasets/src/datasets.scss +0 -1
  37. package/plugins/datasets/src/fetch/createDynamicSource.js +135 -129
  38. package/plugins/datasets/src/fetch/createDynamicSource.test.js +318 -0
  39. package/plugins/datasets/src/fetch/fetchGeoJSON.test.js +83 -0
  40. package/plugins/datasets/src/index.test.js +36 -0
  41. package/plugins/datasets/src/initialise/DatasetsInit.jsx +17 -7
  42. package/plugins/datasets/src/initialise/DatasetsInit.test.jsx +262 -0
  43. package/plugins/datasets/src/initialise/initialiseDatasets.js +16 -4
  44. package/plugins/datasets/src/initialise/initialiseDatasets.test.js +42 -0
  45. package/plugins/datasets/src/manifest.js +0 -38
  46. package/plugins/datasets/src/manifest.test.js +49 -0
  47. package/plugins/datasets/src/reducers/datasetsToMenu.test.js +54 -1
  48. package/plugins/datasets/src/reducers/pluginState.js +3 -16
  49. package/plugins/datasets/src/reducers/pluginState.test.js +23 -0
  50. package/plugins/datasets/src/registry/dataset.js +13 -1
  51. package/plugins/datasets/src/registry/dataset.test.js +61 -0
  52. package/plugins/datasets/src/registry/datasetRegistry.js +12 -6
  53. package/plugins/datasets/src/registry/datasetRegistry.test.js +16 -4
  54. package/plugins/datasets/src/registry/isVisibleWhen.js +6 -3
  55. package/plugins/datasets/src/registry/isVisibleWhen.test.js +11 -1
  56. package/plugins/datasets/src/utils/bbox.test.js +125 -0
  57. package/plugins/draw/dist/esm/im-draw-ml-adapter.js +1 -1
  58. package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -1
  59. package/plugins/draw/dist/esm/im-draw-plugin.js +1 -1
  60. package/plugins/draw/dist/esm/index.js +1 -1
  61. package/plugins/draw/dist/umd/im-draw-ml-adapter.js +2 -1
  62. package/plugins/draw/dist/umd/im-draw-ml-adapter.js.LICENSE.txt +1 -0
  63. package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
  64. package/plugins/draw/dist/umd/im-draw-plugin.js +1 -1
  65. package/plugins/draw/dist/umd/index.js +1 -1
  66. package/plugins/draw/src/DrawInit.jsx +10 -2
  67. package/plugins/draw/src/DrawInit.test.jsx +51 -10
  68. package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +91 -11
  69. package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.test.js +246 -4
  70. package/plugins/draw/src/adapters/maplibre/mapboxDraw.js +109 -14
  71. package/plugins/draw/src/adapters/maplibre/mapboxDraw.test.js +174 -11
  72. package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.test.js +12 -0
  73. package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.js +9 -1
  74. package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.test.js +24 -3
  75. package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.js +194 -0
  76. package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.test.js +457 -0
  77. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/__helpers__/harness.js +102 -0
  78. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.js +82 -0
  79. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.test.js +83 -0
  80. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.js +55 -0
  81. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.test.js +62 -0
  82. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.js +138 -0
  83. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.test.js +197 -0
  84. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.js +92 -0
  85. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.test.js +101 -0
  86. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.js +46 -0
  87. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.test.js +42 -0
  88. package/plugins/draw/src/adapters/maplibre/modes/editPointMode.js +151 -0
  89. package/plugins/draw/src/adapters/maplibre/modes/editPointMode.test.js +156 -0
  90. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.js +4 -27
  91. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.test.js +24 -39
  92. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.js +4 -5
  93. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.js +20 -23
  94. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.test.js +25 -5
  95. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.js +6 -36
  96. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.test.js +7 -49
  97. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.js +3 -53
  98. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.test.js +8 -33
  99. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.js +29 -72
  100. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.test.js +10 -3
  101. package/plugins/draw/src/adapters/maplibre/pointSymbolImages.js +158 -0
  102. package/plugins/draw/src/adapters/maplibre/pointSymbolImages.test.js +314 -0
  103. package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.js +6 -1
  104. package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.test.js +21 -0
  105. package/plugins/draw/src/adapters/maplibre/snap/snapInstance.js +6 -2
  106. package/plugins/draw/src/adapters/maplibre/snap/snapInstance.test.js +12 -0
  107. package/plugins/draw/src/adapters/maplibre/styles.js +38 -11
  108. package/plugins/draw/src/adapters/maplibre/styles.test.js +39 -0
  109. package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.js +79 -0
  110. package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.test.js +135 -0
  111. package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.js +42 -0
  112. package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.test.js +85 -0
  113. package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.js +34 -0
  114. package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.test.js +78 -0
  115. package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.js +16 -3
  116. package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.test.js +21 -3
  117. package/plugins/draw/src/adapters/maplibre/utils/snapMovement.js +59 -0
  118. package/plugins/draw/src/adapters/maplibre/utils/snapMovement.test.js +73 -0
  119. package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.js +33 -0
  120. package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.test.js +70 -0
  121. package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.js +27 -2
  122. package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.test.js +93 -0
  123. package/plugins/draw/src/adapters/openlayers/__helpers__/harness.js +9 -0
  124. package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.js +19 -1
  125. package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.test.js +34 -1
  126. package/plugins/draw/src/adapters/openlayers/core/styles.js +73 -1
  127. package/plugins/draw/src/adapters/openlayers/core/styles.test.js +122 -3
  128. package/plugins/draw/src/adapters/openlayers/draw/DrawMode.test.js +12 -0
  129. package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +8 -2
  130. package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +31 -0
  131. package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +9 -5
  132. package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +33 -0
  133. package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.js +5 -3
  134. package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.test.js +11 -0
  135. package/plugins/draw/src/adapters/openlayers/edit/nudge.js +10 -3
  136. package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +24 -1
  137. package/plugins/draw/src/adapters/openlayers/edit/touchHandler.js +18 -13
  138. package/plugins/draw/src/adapters/openlayers/edit/touchHandler.test.js +46 -1
  139. package/plugins/draw/src/adapters/openlayers/olDraw.js +11 -4
  140. package/plugins/draw/src/adapters/openlayers/olDraw.test.js +48 -6
  141. package/plugins/draw/src/adapters/openlayers/point/drawPointMode.js +156 -0
  142. package/plugins/draw/src/adapters/openlayers/point/drawPointMode.test.js +246 -0
  143. package/plugins/draw/src/adapters/openlayers/point/editPointMode.js +267 -0
  144. package/plugins/draw/src/adapters/openlayers/point/editPointMode.test.js +327 -0
  145. package/plugins/draw/src/adapters/openlayers/point/pointDragInteraction.js +71 -0
  146. package/plugins/draw/src/adapters/openlayers/point/pointOps.js +34 -0
  147. package/plugins/draw/src/adapters/openlayers/point/pointOps.test.js +32 -0
  148. package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.js +82 -0
  149. package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.test.js +107 -0
  150. package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.js +83 -0
  151. package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.test.js +214 -0
  152. package/plugins/draw/src/adapters/openlayers/snap/snapIndicator.js +3 -1
  153. package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.js +6 -9
  154. package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.test.js +15 -4
  155. package/plugins/draw/src/adapters/openlayers/utils/olCoords.js +1 -1
  156. package/plugins/draw/src/api/addFeature.js +16 -1
  157. package/plugins/draw/src/api/addFeature.test.js +62 -5
  158. package/plugins/draw/src/api/editFeature.js +7 -4
  159. package/plugins/draw/src/api/editFeature.test.js +16 -0
  160. package/plugins/draw/src/api/newPoint.js +42 -0
  161. package/plugins/draw/src/api/newPoint.test.js +117 -0
  162. package/plugins/draw/src/api/setStyle.js +43 -0
  163. package/plugins/draw/src/api/setStyle.test.js +75 -0
  164. package/plugins/draw/src/events.js +17 -10
  165. package/plugins/draw/src/events.test.js +14 -0
  166. package/plugins/draw/src/manifest.js +18 -6
  167. package/plugins/draw/src/manifest.test.js +32 -1
  168. package/plugins/draw/src/reducer.test.js +7 -0
  169. package/plugins/draw/src/utils/stripInternalProperties.js +19 -0
  170. package/plugins/draw/src/utils/stripInternalProperties.test.js +50 -0
  171. package/plugins/draw/src/utils/symbolKeys.js +5 -0
  172. package/plugins/draw/src/validation/liveDrawChecks.test.js +54 -0
  173. package/plugins/draw/src/validation/liveStroke.test.js +64 -1
  174. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  175. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  176. package/plugins/interact/dist/umd/index.js +1 -1
  177. package/plugins/interact/src/hooks/useHighlightSync.js +33 -12
  178. package/plugins/interact/src/hooks/useHighlightSync.test.js +96 -25
  179. package/plugins/interact/src/utils/featureQueries.js +18 -0
  180. package/plugins/interact/src/utils/featureQueries.test.js +15 -0
  181. package/plugins/interact/src/utils/spatial.js +9 -3
  182. package/plugins/interact/src/utils/spatial.test.js +12 -1
  183. package/plugins/map-key/dist/css/index.css +62 -0
  184. package/plugins/map-key/dist/esm/im-map-key-plugin.js +1 -0
  185. package/plugins/map-key/dist/esm/index.js +1 -0
  186. package/plugins/map-key/dist/umd/im-map-key-plugin.js +2 -0
  187. package/plugins/map-key/dist/umd/im-map-key-plugin.js.LICENSE.txt +1 -0
  188. package/plugins/map-key/dist/umd/index.js +2 -0
  189. package/plugins/map-key/dist/umd/index.js.LICENSE.txt +1 -0
  190. package/plugins/map-key/package.json +28 -0
  191. package/plugins/map-key/src/components/Key/EmptyKey.jsx +7 -0
  192. package/plugins/{datasets → map-key}/src/components/Key/EmptyKey.test.jsx +2 -2
  193. package/plugins/map-key/src/components/Key/Key.jsx +41 -0
  194. package/plugins/{datasets → map-key}/src/components/Key/Key.module.scss +15 -14
  195. package/plugins/map-key/src/components/Key/Key.test.jsx +73 -0
  196. package/plugins/map-key/src/components/Key/KeyGroupItem.jsx +16 -0
  197. package/plugins/map-key/src/components/Key/KeyGroupItem.test.jsx +53 -0
  198. package/plugins/map-key/src/components/Key/KeyItem.jsx +29 -0
  199. package/plugins/map-key/src/components/Key/KeyItem.test.jsx +67 -0
  200. package/plugins/map-key/src/components/Key/KeySvg.jsx +42 -0
  201. package/plugins/{datasets → map-key}/src/components/Key/KeySvg.test.jsx +14 -12
  202. package/plugins/map-key/src/components/Key/KeySvgLine.jsx +28 -0
  203. package/plugins/{datasets → map-key}/src/components/Key/KeySvgLine.test.jsx +1 -1
  204. package/plugins/map-key/src/components/Key/KeySvgPattern.jsx +23 -0
  205. package/plugins/{datasets → map-key}/src/components/Key/KeySvgPattern.test.jsx +2 -2
  206. package/plugins/map-key/src/components/Key/KeySvgRect.jsx +33 -0
  207. package/plugins/{datasets → map-key}/src/components/Key/KeySvgRect.test.jsx +1 -1
  208. package/plugins/map-key/src/components/Key/KeySvgSymbol.jsx +27 -0
  209. package/plugins/{datasets → map-key}/src/components/Key/KeySvgSymbol.test.jsx +5 -6
  210. package/plugins/map-key/src/components/Key/MapKey.jsx +50 -0
  211. package/plugins/map-key/src/components/Key/MapKey.test.jsx +130 -0
  212. package/plugins/{datasets → map-key}/src/components/Key/svgProperties.js +2 -2
  213. package/plugins/{datasets → map-key}/src/components/Key/svgProperties.test.js +2 -2
  214. package/plugins/map-key/src/index.js +13 -0
  215. package/plugins/map-key/src/index.test.js +31 -0
  216. package/plugins/map-key/src/initialise/MapKeyInit.jsx +15 -0
  217. package/plugins/map-key/src/initialise/MapKeyInit.test.jsx +39 -0
  218. package/plugins/map-key/src/manifest.js +30 -0
  219. package/plugins/map-key/src/manifest.test.js +63 -0
  220. package/plugins/map-key/src/mapKey.scss +4 -0
  221. package/plugins/map-key/src/registry/getDatasetRegistry.js +6 -0
  222. package/plugins/map-key/src/registry/getDatasetRegistry.test.js +23 -0
  223. package/plugins/map-key/src/registry/index.js +3 -0
  224. package/plugins/map-key/src/registry/index.test.js +15 -0
  225. package/plugins/menu/dist/css/index.css +80 -0
  226. package/plugins/menu/dist/esm/im-menu-plugin.js +1 -0
  227. package/plugins/menu/dist/esm/index.js +1 -0
  228. package/plugins/menu/dist/umd/im-menu-plugin.js +1 -0
  229. package/plugins/menu/dist/umd/index.js +2 -0
  230. package/plugins/menu/dist/umd/index.js.LICENSE.txt +1 -0
  231. package/plugins/menu/package.json +28 -0
  232. package/plugins/menu/src/components/Menu/CheckboxGroupWrapper.jsx +21 -0
  233. package/plugins/menu/src/components/Menu/GroupLegend.jsx +17 -0
  234. package/plugins/menu/src/components/Menu/GroupLegend.test.jsx +68 -0
  235. package/plugins/menu/src/components/Menu/Menu.jsx +19 -0
  236. package/plugins/menu/src/components/Menu/Menu.module.scss +90 -0
  237. package/plugins/menu/src/components/Menu/Menu.test.jsx +161 -0
  238. package/plugins/menu/src/components/Menu/MenuCheckbox.jsx +35 -0
  239. package/plugins/menu/src/components/Menu/MenuCheckbox.test.jsx +93 -0
  240. package/plugins/menu/src/components/Menu/MenuGroup.jsx +11 -0
  241. package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.jsx → menu/src/components/Menu/MenuRadio.jsx} +6 -4
  242. package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.test.jsx → menu/src/components/Menu/MenuRadio.test.jsx} +18 -18
  243. package/plugins/menu/src/components/Menu/RadioGroupWrapper.jsx +32 -0
  244. package/plugins/{datasets/src/components/LayersMenu/LayersRadioGroupWrapper.test.jsx → menu/src/components/Menu/RadioGroupWrapper.test.jsx} +21 -21
  245. package/plugins/menu/src/components/Menu/useIdPrefix.jsx +4 -0
  246. package/plugins/menu/src/index.js +13 -0
  247. package/plugins/menu/src/index.test.js +31 -0
  248. package/plugins/menu/src/initialise/MenuInit.jsx +24 -0
  249. package/plugins/menu/src/initialise/MenuInit.test.jsx +50 -0
  250. package/plugins/menu/src/initialise/createMenu.js +10 -0
  251. package/plugins/menu/src/initialise/createMenu.test.js +37 -0
  252. package/plugins/menu/src/manifest.js +35 -0
  253. package/plugins/menu/src/manifest.test.js +63 -0
  254. package/plugins/menu/src/menu.scss +4 -0
  255. package/plugins/menu/src/reducers/menuStateReducer.test.js +38 -0
  256. package/plugins/menu/src/reducers/pluginState.js +35 -0
  257. package/plugins/menu/src/reducers/pluginState.test.js +57 -0
  258. package/plugins/menu/src/registry/getDatasetRegistry.js +9 -0
  259. package/plugins/menu/src/registry/getDatasetRegistry.test.js +23 -0
  260. package/plugins/menu/src/registry/index.js +3 -0
  261. package/plugins/menu/src/registry/index.test.js +15 -0
  262. package/plugins/menu/src/registry/isVisibleWhen.js +43 -0
  263. package/plugins/menu/src/registry/isVisibleWhen.test.js +48 -0
  264. package/plugins/search/dist/css/index.css +1 -1
  265. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  266. package/plugins/search/dist/esm/index.js +1 -1
  267. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  268. package/plugins/search/dist/umd/index.js +1 -1
  269. package/plugins/search/src/Search.jsx +2 -3
  270. package/plugins/search/src/components/Form/Form.module.scss +9 -20
  271. package/plugins/search/src/index.js +0 -1
  272. package/plugins/search/src/index.test.js +2 -4
  273. package/plugins/search/src/manifest.js +1 -1
  274. package/plugins/search/src/search.scss +17 -16
  275. package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
  276. package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
  277. package/providers/beta/openlayers/dist/umd/index.js +1 -1
  278. package/providers/beta/openlayers/src/openlayersProvider.js +4 -1
  279. package/providers/beta/openlayers/src/openlayersProvider.test.js +18 -0
  280. package/providers/beta/openlayers/src/utils/highlightFeatures.js +52 -5
  281. package/providers/beta/openlayers/src/utils/highlightFeatures.test.js +176 -0
  282. package/providers/beta/openlayers/src/utils/symbolImages.js +40 -0
  283. package/providers/beta/openlayers/src/utils/symbolImages.test.js +58 -0
  284. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  285. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  286. package/providers/maplibre/dist/umd/index.js +1 -1
  287. package/providers/maplibre/src/utils/drawLayerBuckets.js +14 -0
  288. package/providers/maplibre/src/utils/drawLayerBuckets.test.js +18 -0
  289. package/providers/maplibre/src/utils/highlightFeatures.js +80 -55
  290. package/providers/maplibre/src/utils/highlightFeatures.test.js +156 -17
  291. package/providers/maplibre/src/utils/hoverCursor.js +30 -14
  292. package/providers/maplibre/src/utils/hoverCursor.test.js +35 -0
  293. package/providers/maplibre/src/utils/patternImages.js +1 -1
  294. package/providers/maplibre/src/utils/symbolImages.js +41 -3
  295. package/providers/maplibre/src/utils/symbolImages.test.js +40 -6
  296. package/rollup.esm.mjs +11 -1
  297. package/src/App/components/Actions/Actions.test.jsx +47 -0
  298. package/src/App/components/Hints/Hints.test.jsx +13 -0
  299. package/src/App/components/MoveControls/MoveControls.module.scss +41 -10
  300. package/src/App/components/Panel/Panel.jsx +18 -3
  301. package/src/App/components/Panel/Panel.module.scss +18 -8
  302. package/src/App/components/Panel/Panel.test.jsx +34 -0
  303. package/src/App/components/Tooltip/Tooltip.test.jsx +12 -14
  304. package/src/App/hooks/useFeatureItems.js +16 -7
  305. package/src/App/hooks/useFeatureItems.test.js +57 -0
  306. package/src/App/hooks/useLayoutMeasurements.js +95 -84
  307. package/src/App/hooks/useLayoutMeasurements.test.js +61 -1
  308. package/src/App/layout/Layout.jsx +9 -10
  309. package/src/App/layout/Layout.test.jsx +2 -0
  310. package/src/App/layout/layout.module.scss +31 -17
  311. package/src/App/registry/pluginRegistry.js +1 -1
  312. package/src/App/registry/pluginRegistry.test.js +12 -0
  313. package/src/App/renderer/HtmlElementHost.jsx +18 -5
  314. package/src/App/renderer/HtmlElementHost.test.jsx +54 -0
  315. package/src/App/renderer/mapButtons.js +28 -12
  316. package/src/App/renderer/mapButtons.test.js +17 -4
  317. package/src/App/renderer/mapControls.js +3 -1
  318. package/src/App/renderer/mapControls.test.js +16 -0
  319. package/src/App/renderer/mapPanels.js +44 -9
  320. package/src/App/renderer/mapPanels.test.js +51 -2
  321. package/src/App/renderer/orderItems.js +28 -0
  322. package/src/App/renderer/orderItems.test.js +50 -0
  323. package/src/App/renderer/slotAggregator.js +2 -18
  324. package/src/App/renderer/slotHelpers.js +3 -1
  325. package/src/App/renderer/slotHelpers.test.js +5 -0
  326. package/src/App/renderer/slots.js +3 -0
  327. package/src/App/store/AppProvider.jsx +1 -0
  328. package/src/App/store/appDispatchMiddleware.js +1 -1
  329. package/src/App/store/appDispatchMiddleware.test.js +8 -0
  330. package/src/config/events.js +2 -0
  331. package/src/scss/settings/_colors.scss +1 -0
  332. package/src/scss/settings/_dimensions.scss +2 -3
  333. package/src/scss/settings/_transition.scss +1 -1
  334. package/src/services/eventBus.js +44 -0
  335. package/src/services/eventBus.test.js +83 -0
  336. package/src/services/symbolRegistry.js +1 -1
  337. package/src/types.js +11 -1
  338. package/webpack.umd.mjs +2 -0
  339. package/plugins/datasets/src/components/Key/EmptyKey.jsx +0 -7
  340. package/plugins/datasets/src/components/Key/Key.jsx +0 -50
  341. package/plugins/datasets/src/components/Key/KeyGroupItem.jsx +0 -18
  342. package/plugins/datasets/src/components/Key/KeyItem.jsx +0 -23
  343. package/plugins/datasets/src/components/Key/KeySvg.jsx +0 -22
  344. package/plugins/datasets/src/components/Key/KeySvgLine.jsx +0 -19
  345. package/plugins/datasets/src/components/Key/KeySvgPattern.jsx +0 -13
  346. package/plugins/datasets/src/components/Key/KeySvgRect.jsx +0 -22
  347. package/plugins/datasets/src/components/Key/KeySvgSymbol.jsx +0 -16
  348. package/plugins/datasets/src/components/LayersMenu/LayersRadioGroupWrapper.jsx +0 -41
  349. /package/plugins/{datasets → menu}/src/reducers/menuStateReducer.js +0 -0
  350. /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.js +0 -0
  351. /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.test.js +0 -0
@@ -0,0 +1,31 @@
1
+ import createPlugin from './index.js'
2
+
3
+ jest.mock('./menu.scss', () => {})
4
+ jest.mock('./manifest.js', () => ({ manifest: { InitComponent: 'MenuInit', panels: [] } }))
5
+
6
+ describe('createPlugin', () => {
7
+ it('returns the menu id', () => {
8
+ expect(createPlugin().id).toBe('menu')
9
+ })
10
+
11
+ it('id cannot be overridden by options', () => {
12
+ expect(createPlugin({ id: 'other' }).id).toBe('menu')
13
+ })
14
+
15
+ it('returns the default noKeyItemText', () => {
16
+ expect(createPlugin().noKeyItemText).toBe('No features displayed')
17
+ })
18
+
19
+ it('allows noKeyItemText to be overridden', () => {
20
+ expect(createPlugin({ noKeyItemText: 'Nothing here' }).noKeyItemText).toBe('Nothing here')
21
+ })
22
+
23
+ it('merges additional options into the returned object', () => {
24
+ expect(createPlugin({ custom: true }).custom).toBe(true)
25
+ })
26
+
27
+ it('load() resolves to the manifest', async () => {
28
+ const result = await createPlugin().load()
29
+ expect(result).toEqual({ InitComponent: 'MenuInit', panels: [] })
30
+ })
31
+ })
@@ -0,0 +1,24 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { createMenu } from './createMenu.js'
3
+ import { attachPluginStateRef } from '../registry/isVisibleWhen.js'
4
+
5
+ // additional possible params here are: appState, mapProvider,
6
+ export function MenuInit ({ pluginConfig, pluginState, mapState, services }) {
7
+ const { dispatch } = pluginState
8
+ const { eventBus } = services
9
+
10
+ const pluginStateRef = useRef(pluginState)
11
+ pluginStateRef.current = pluginState
12
+
13
+ useEffect(() => {
14
+ if (!mapState.isMapReady) {
15
+ return () => {}
16
+ }
17
+ attachPluginStateRef(pluginStateRef)
18
+ const { menu } = pluginConfig
19
+ return createMenu({ menu, eventBus, dispatch, pluginStateRef })
20
+ }, [mapState.isMapReady])
21
+
22
+ // Notify any plugins, that are listening, to the menu's state change
23
+ useEffect(() => eventBus.emit('menu:changed', pluginState.menuState), [pluginState.menuState])
24
+ }
@@ -0,0 +1,50 @@
1
+ import { render } from '@testing-library/react'
2
+ import { MenuInit } from './MenuInit.jsx'
3
+ import { createMenu } from './createMenu.js'
4
+
5
+ jest.mock('./createMenu.js', () => ({
6
+ createMenu: jest.fn()
7
+ }))
8
+
9
+ const mockRemove = jest.fn()
10
+ const mockDispatch = jest.fn()
11
+ const eventBus = { on: jest.fn(), off: jest.fn(), emit: jest.fn(), emitWhenRequested: jest.fn() }
12
+ const services = { eventBus }
13
+ const pluginState = { dispatch: mockDispatch, menuState: null }
14
+ const pluginConfig = { menu: [] }
15
+
16
+ beforeEach(() => {
17
+ jest.clearAllMocks()
18
+ createMenu.mockReturnValue(mockRemove)
19
+ })
20
+
21
+ describe('MenuInit', () => {
22
+ it('does not call createMenu when map is not ready', () => {
23
+ render(<MenuInit mapState={{ isMapReady: false }} services={services} pluginState={pluginState} pluginConfig={pluginConfig} />)
24
+ expect(createMenu).not.toHaveBeenCalled()
25
+ })
26
+
27
+ it('calls createMenu with eventBus when map is ready', () => {
28
+ render(<MenuInit mapState={{ isMapReady: true }} services={services} pluginState={pluginState} pluginConfig={pluginConfig} />)
29
+ expect(createMenu).toHaveBeenCalledWith(expect.objectContaining({ eventBus }))
30
+ })
31
+
32
+ it('calls remove on unmount when map was ready', () => {
33
+ const { unmount } = render(<MenuInit mapState={{ isMapReady: true }} services={services} pluginState={pluginState} pluginConfig={pluginConfig} />)
34
+ unmount()
35
+ expect(mockRemove).toHaveBeenCalled()
36
+ })
37
+
38
+ it('does not call remove on unmount when map was not ready', () => {
39
+ const { unmount } = render(<MenuInit mapState={{ isMapReady: false }} services={services} pluginState={pluginState} pluginConfig={pluginConfig} />)
40
+ unmount()
41
+ expect(mockRemove).not.toHaveBeenCalled()
42
+ })
43
+
44
+ it('calls createMenu again when isMapReady changes to true', () => {
45
+ const { rerender } = render(<MenuInit mapState={{ isMapReady: false }} services={services} pluginState={pluginState} pluginConfig={pluginConfig} />)
46
+ expect(createMenu).not.toHaveBeenCalled()
47
+ rerender(<MenuInit mapState={{ isMapReady: true }} services={services} pluginState={pluginState} pluginConfig={pluginConfig} />)
48
+ expect(createMenu).toHaveBeenCalledTimes(1)
49
+ })
50
+ })
@@ -0,0 +1,10 @@
1
+ import { setDatasetRegistry } from '../registry/getDatasetRegistry.js'
2
+
3
+ export const createMenu = ({ menu, eventBus, dispatch, pluginStateRef }) => {
4
+ dispatch({ type: 'SET_MENU', payload: { menu } })
5
+ // Request a handle on the datasetsRegistry singleton, so that the menu plugin can access it when needed
6
+ eventBus.requestOnce('datasets:registry', setDatasetRegistry)
7
+ // add a listener for the menu:state event, which will be emitted whenever requested by other plugins,
8
+ // e.g. the datasets plugin, so they can access the menu state.
9
+ return eventBus.emitWhenRequested('menu:state', pluginStateRef)
10
+ }
@@ -0,0 +1,37 @@
1
+ import { createMenu } from './createMenu.js'
2
+ import { setDatasetRegistry } from '../registry/getDatasetRegistry.js'
3
+
4
+ jest.mock('../registry/getDatasetRegistry.js', () => ({
5
+ setDatasetRegistry: jest.fn()
6
+ }))
7
+
8
+ const makeEventBus = () => ({
9
+ on: jest.fn(),
10
+ off: jest.fn(),
11
+ emit: jest.fn(),
12
+ requestOnce: jest.fn(),
13
+ emitWhenRequested: jest.fn()
14
+ })
15
+
16
+ describe('createMenu', () => {
17
+ it('dispatches SET_MENU with the menu', () => {
18
+ const eventBus = makeEventBus()
19
+ const dispatch = jest.fn()
20
+ const menu = [{ id: 'layer1' }]
21
+ createMenu({ menu, eventBus, dispatch, pluginStateRef: {} })
22
+ expect(dispatch).toHaveBeenCalledWith({ type: 'SET_MENU', payload: { menu } })
23
+ })
24
+
25
+ it('calls requestOnce for the datasets registry', () => {
26
+ const eventBus = makeEventBus()
27
+ createMenu({ eventBus, dispatch: jest.fn(), pluginStateRef: {} })
28
+ expect(eventBus.requestOnce).toHaveBeenCalledWith('datasets:registry', setDatasetRegistry)
29
+ })
30
+
31
+ it('calls emitWhenRequested for menu:state', () => {
32
+ const eventBus = makeEventBus()
33
+ const pluginStateRef = { current: {} }
34
+ createMenu({ eventBus, dispatch: jest.fn(), pluginStateRef })
35
+ expect(eventBus.emitWhenRequested).toHaveBeenCalledWith('menu:state', pluginStateRef)
36
+ })
37
+ })
@@ -0,0 +1,35 @@
1
+ import { Menu } from './components/Menu/Menu.jsx'
2
+ import './registry/getDatasetRegistry.js'
3
+ import { MenuInit } from './initialise/MenuInit.jsx'
4
+ import { initialState, actions } from './reducers/pluginState.js'
5
+
6
+ export const manifest = {
7
+ InitComponent: MenuInit,
8
+ reducer: {
9
+ initialState,
10
+ actions
11
+ },
12
+ panels: [
13
+ {
14
+ id: 'menu',
15
+ label: 'Layers',
16
+ mobile: { slot: 'drawer', modal: true, dismissible: true },
17
+ tablet: { slot: 'left-top', dismissible: true, exclusive: true, width: '260px' },
18
+ desktop: { slot: 'left-top', modal: false, dismissible: true, exclusive: true, width: '280px' },
19
+ render: Menu
20
+ }],
21
+ buttons: [{
22
+ id: 'menuButton',
23
+ label: 'Layers',
24
+ panelId: 'menu',
25
+ iconId: 'layersMenu',
26
+ mobile: { slot: 'top-left', showLabel: true },
27
+ tablet: { slot: 'top-left', showLabel: true },
28
+ desktop: { slot: 'top-left', showLabel: true }
29
+ }],
30
+
31
+ icons: [{
32
+ id: 'layersMenu',
33
+ svgContent: '<path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"></path><path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"></path><path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"></path>'
34
+ }]
35
+ }
@@ -0,0 +1,63 @@
1
+ import { manifest } from './manifest.js'
2
+ import { Menu } from './components/Menu/Menu.jsx'
3
+ import { MenuInit } from './initialise/MenuInit.jsx'
4
+
5
+ jest.mock('./components/Menu/Menu.jsx', () => ({ Menu: jest.fn() }))
6
+ jest.mock('./initialise/MenuInit.jsx', () => ({ MenuInit: jest.fn() }))
7
+ jest.mock('./registry/getDatasetRegistry.js', () => ({}))
8
+
9
+ describe('manifest', () => {
10
+ it('sets InitComponent to MenuInit', () => {
11
+ expect(manifest.InitComponent).toBe(MenuInit)
12
+ })
13
+
14
+ describe('panels', () => {
15
+ it('defines one panel', () => {
16
+ expect(manifest.panels).toHaveLength(1)
17
+ })
18
+
19
+ it('panel has id menu', () => {
20
+ expect(manifest.panels[0].id).toBe('menu')
21
+ })
22
+
23
+ it('panel render is Menu', () => {
24
+ expect(manifest.panels[0].render).toBe(Menu)
25
+ })
26
+
27
+ it('panel defines mobile, tablet and desktop slots', () => {
28
+ const { mobile, tablet, desktop } = manifest.panels[0]
29
+ expect(mobile.slot).toBe('drawer')
30
+ expect(tablet.slot).toBe('left-top')
31
+ expect(desktop.slot).toBe('left-top')
32
+ })
33
+ })
34
+
35
+ describe('buttons', () => {
36
+ it('defines one button', () => {
37
+ expect(manifest.buttons).toHaveLength(1)
38
+ })
39
+
40
+ it('button has id menuButton and links to the menu panel', () => {
41
+ expect(manifest.buttons[0].id).toBe('menuButton')
42
+ expect(manifest.buttons[0].panelId).toBe('menu')
43
+ })
44
+
45
+ it('button references the layersMenu icon', () => {
46
+ expect(manifest.buttons[0].iconId).toBe('layersMenu')
47
+ })
48
+ })
49
+
50
+ describe('icons', () => {
51
+ it('defines one icon', () => {
52
+ expect(manifest.icons).toHaveLength(1)
53
+ })
54
+
55
+ it('icon id is layersMenu', () => {
56
+ expect(manifest.icons[0].id).toBe('layersMenu')
57
+ })
58
+
59
+ it('icon has svgContent', () => {
60
+ expect(manifest.icons[0].svgContent).toBeTruthy()
61
+ })
62
+ })
63
+ })
@@ -0,0 +1,4 @@
1
+ // ===================================================
2
+ // Component: Menu
3
+ // ===================================================
4
+ @use './components/Menu/Menu.module';
@@ -0,0 +1,38 @@
1
+ import { buildMenuState } from './menuStateReducer.js'
2
+
3
+ describe('buildMenuState', () => {
4
+ it('returns an empty object for an empty menu', () => {
5
+ expect(buildMenuState([])).toEqual({})
6
+ })
7
+
8
+ it('ignores non-radio menu groups', () => {
9
+ const menu = [{ id: 'layer', type: 'checkbox', value: 'a' }]
10
+ expect(buildMenuState(menu)).toEqual({})
11
+ })
12
+
13
+ it('uses the explicit value when set', () => {
14
+ const menu = [{ id: 'datasets', type: 'radio', value: 'floodZones', items: [{ value: 'other' }] }]
15
+ expect(buildMenuState(menu)).toEqual({ datasets: 'floodZones' })
16
+ })
17
+
18
+ it('falls back to the first item value when no value is set', () => {
19
+ const menu = [{ id: 'datasets', type: 'radio', items: [{ value: 'floodZones' }, { value: 'other' }] }]
20
+ expect(buildMenuState(menu)).toEqual({ datasets: 'floodZones' })
21
+ })
22
+
23
+ it('handles multiple radio groups', () => {
24
+ const menu = [
25
+ { id: 'datasets', type: 'radio', value: 'floodZones' },
26
+ { id: 'timeframe', type: 'radio', items: [{ value: 'presentDay' }] }
27
+ ]
28
+ expect(buildMenuState(menu)).toEqual({ datasets: 'floodZones', timeframe: 'presentDay' })
29
+ })
30
+
31
+ it('mixes radio and non-radio groups, only including radios', () => {
32
+ const menu = [
33
+ { id: 'layer', type: 'checkbox', value: 'a' },
34
+ { id: 'datasets', type: 'radio', value: 'floodZones' }
35
+ ]
36
+ expect(buildMenuState(menu)).toEqual({ datasets: 'floodZones' })
37
+ })
38
+ })
@@ -0,0 +1,35 @@
1
+ import { buildMenuState } from './menuStateReducer.js'
2
+
3
+ const initialState = {
4
+ actionsArray: [],
5
+ menu: [],
6
+ menuState: {}
7
+ }
8
+
9
+ const updateMenuState = (state, payload) => {
10
+ return {
11
+ ...state,
12
+ menuState: { ...state.menuState, ...payload }
13
+ }
14
+ }
15
+
16
+ const setMenu = (state, payload) => {
17
+ const { menu } = payload
18
+ // build the initial menuState for radios from the menu
19
+ const menuState = buildMenuState(menu)
20
+ return {
21
+ ...state,
22
+ menu,
23
+ menuState
24
+ }
25
+ }
26
+
27
+ const actions = {
28
+ SET_MENU: setMenu,
29
+ UPDATE_MENU_STATE: updateMenuState
30
+ }
31
+
32
+ export {
33
+ initialState,
34
+ actions
35
+ }
@@ -0,0 +1,57 @@
1
+ import { initialState, actions } from './pluginState.js'
2
+
3
+ describe('initialState', () => {
4
+ it('has the expected shape', () => {
5
+ expect(initialState).toEqual({
6
+ actionsArray: [],
7
+ menu: [],
8
+ menuState: {}
9
+ })
10
+ })
11
+ })
12
+
13
+ describe('actions.SET_MENU', () => {
14
+ it('sets the menu on state', () => {
15
+ const menu = [{ id: 'datasets', type: 'checkbox' }]
16
+ const result = actions.SET_MENU(initialState, { menu })
17
+ expect(result.menu).toBe(menu)
18
+ })
19
+
20
+ it('builds menuState from radio groups', () => {
21
+ const menu = [{ id: 'datasets', type: 'radio', value: 'floodZones' }]
22
+ const result = actions.SET_MENU(initialState, { menu })
23
+ expect(result.menuState).toEqual({ datasets: 'floodZones' })
24
+ })
25
+
26
+ it('produces empty menuState when there are no radio groups', () => {
27
+ const menu = [{ id: 'layer', type: 'checkbox', value: 'a' }]
28
+ const result = actions.SET_MENU(initialState, { menu })
29
+ expect(result.menuState).toEqual({})
30
+ })
31
+
32
+ it('does not mutate the existing state', () => {
33
+ const state = { ...initialState, actionsArray: [1] }
34
+ const result = actions.SET_MENU(state, { menu: [] })
35
+ expect(result.actionsArray).toEqual([1])
36
+ })
37
+ })
38
+
39
+ describe('actions.UPDATE_MENU_STATE', () => {
40
+ it('merges payload into menuState', () => {
41
+ const state = { ...initialState, menuState: { datasets: 'floodZones' } }
42
+ const result = actions.UPDATE_MENU_STATE(state, { timeframe: 'presentDay' })
43
+ expect(result.menuState).toEqual({ datasets: 'floodZones', timeframe: 'presentDay' })
44
+ })
45
+
46
+ it('overwrites an existing key', () => {
47
+ const state = { ...initialState, menuState: { datasets: 'floodZones' } }
48
+ const result = actions.UPDATE_MENU_STATE(state, { datasets: 'riverNetwork' })
49
+ expect(result.menuState).toEqual({ datasets: 'riverNetwork' })
50
+ })
51
+
52
+ it('does not mutate the existing state', () => {
53
+ const state = { ...initialState, actionsArray: [1] }
54
+ const result = actions.UPDATE_MENU_STATE(state, { datasets: 'floodZones' })
55
+ expect(result.actionsArray).toEqual([1])
56
+ })
57
+ })
@@ -0,0 +1,9 @@
1
+ // the local _datasetRegistry will only be initialised if the datasets plugin is in use,
2
+ // otherwise it will remain null.
3
+ let _datasetRegistry = null
4
+
5
+ export const getDatasetRegistry = () => _datasetRegistry
6
+
7
+ export const setDatasetRegistry = (datasetRegistry) => {
8
+ _datasetRegistry = datasetRegistry
9
+ }
@@ -0,0 +1,23 @@
1
+ import { getDatasetRegistry, setDatasetRegistry } from './getDatasetRegistry.js'
2
+
3
+ afterEach(() => {
4
+ setDatasetRegistry(null)
5
+ })
6
+
7
+ describe('getDatasetRegistry', () => {
8
+ it('returns null before any registry is set', () => {
9
+ expect(getDatasetRegistry()).toBeNull()
10
+ })
11
+
12
+ it('returns the registry after setDatasetRegistry is called', () => {
13
+ const registry = { keyItems: jest.fn() }
14
+ setDatasetRegistry(registry)
15
+ expect(getDatasetRegistry()).toBe(registry)
16
+ })
17
+
18
+ it('returns null after being reset to null', () => {
19
+ setDatasetRegistry({ keyItems: jest.fn() })
20
+ setDatasetRegistry(null)
21
+ expect(getDatasetRegistry()).toBeNull()
22
+ })
23
+ })
@@ -0,0 +1,3 @@
1
+ export { getDatasetRegistry } from './getDatasetRegistry.js'
2
+ export { patternRegistry } from '../../../../src/services/patternRegistry.js'
3
+ export { symbolRegistry } from '../../../../src/services/symbolRegistry.js'
@@ -0,0 +1,15 @@
1
+ import { getDatasetRegistry, patternRegistry, symbolRegistry } from './index.js'
2
+
3
+ describe('registry/index', () => {
4
+ it('exports getDatasetRegistry as a function', () => {
5
+ expect(typeof getDatasetRegistry).toBe('function')
6
+ })
7
+
8
+ it('exports patternRegistry', () => {
9
+ expect(patternRegistry).toBeDefined()
10
+ })
11
+
12
+ it('exports symbolRegistry', () => {
13
+ expect(symbolRegistry).toBeDefined()
14
+ })
15
+ })
@@ -0,0 +1,43 @@
1
+ let _pluginStateRef = {}
2
+ export const attachPluginStateRef = (pluginStateRef) => { _pluginStateRef = pluginStateRef }
3
+
4
+ const _isVisibleWhenMenuCheck = (menuVisibleWhen) => {
5
+ const menuState = _pluginStateRef?.current?.menuState || {}
6
+ for (const [key, valueArray] of Object.entries(menuVisibleWhen)) {
7
+ const menuValue = menuState[key]
8
+ if (!valueArray.includes(menuValue)) {
9
+ return false
10
+ }
11
+ }
12
+ return true
13
+ }
14
+
15
+ /**
16
+ * receives a visibleWhen boolean or object
17
+ * and returns a boolean indicating whether the dataset should be visible
18
+ * if visibleWhen is undefined, it returns true
19
+ * if visibleWhen is a boolean, it returns that boolean
20
+ * if visibleWhen is an object, it checks the properties of the object, against the relevant pluginState properties,
21
+ * and returns true if all properties are satisfied:
22
+ * @param {boolean|object} visibleWhen - the visibleWhen property of a dataset
23
+ * @returns {boolean} - true if the dataset should be visible, false otherwise
24
+ */
25
+ export const isVisibleWhen = (visibleWhen) => {
26
+ if (visibleWhen === undefined || visibleWhen === null) {
27
+ return true
28
+ }
29
+ if (typeof visibleWhen === 'boolean') {
30
+ return visibleWhen
31
+ }
32
+ if (typeof visibleWhen === 'object') {
33
+ // check each property of the visibleWhen object against the relevant pluginState property
34
+ for (const [visibleWhenKey, visibleWhenValue] of Object.entries(visibleWhen)) {
35
+ if (visibleWhenKey === 'menu' && !_isVisibleWhenMenuCheck(visibleWhenValue)) {
36
+ return false
37
+ }
38
+ }
39
+ return true
40
+ }
41
+ // Fallback to true if visibleWhen is incorrectly configured
42
+ return true
43
+ }
@@ -0,0 +1,48 @@
1
+ import { isVisibleWhen, attachPluginStateRef } from './isVisibleWhen.js'
2
+
3
+ const setMenuState = (menuState) => {
4
+ attachPluginStateRef({ current: { menuState } })
5
+ }
6
+
7
+ describe('isVisibleWhen', () => {
8
+ beforeEach(() => {
9
+ // reset menu state before each test
10
+ setMenuState({})
11
+ })
12
+
13
+ it('returns true if visibleWhen is undefined', () => {
14
+ expect(isVisibleWhen(undefined)).toBe(true)
15
+ })
16
+
17
+ it('returns true if visibleWhen is null', () => {
18
+ expect(isVisibleWhen(null)).toBe(true)
19
+ })
20
+
21
+ it('returns the boolean value if visibleWhen is a boolean', () => {
22
+ expect(isVisibleWhen(true)).toBe(true)
23
+ expect(isVisibleWhen(false)).toBe(false)
24
+ })
25
+
26
+ it('returns true if all properties of visibleWhen object are satisfied', () => {
27
+ setMenuState({ datasets: 'floodZones', timeframe: 'climateChange' })
28
+ const visibleWhen = { menu: { datasets: ['floodZones'], timeframe: ['climateChange'] } }
29
+ expect(isVisibleWhen(visibleWhen)).toBe(true)
30
+ })
31
+
32
+ it('returns false if any property of visibleWhen object is not satisfied', () => {
33
+ setMenuState({ datasets: 'floodZones', timeframe: 'climateChange' })
34
+ const visibleWhen = { menu: { datasets: ['floodZones'], timeframe: ['presentDay'] } }
35
+ expect(isVisibleWhen(visibleWhen)).toBe(false)
36
+ })
37
+
38
+ it('returns true if visibleWhen is incorrectly configured', () => {
39
+ const visibleWhen = 'incorrectly-configured'
40
+ expect(isVisibleWhen(visibleWhen)).toBe(true)
41
+ })
42
+
43
+ it('falls back to false if menu state is not attached', () => {
44
+ attachPluginStateRef(null)
45
+ const visibleWhen = { menu: { datasets: ['floodZones'], timeframe: ['climateChange'] } }
46
+ expect(isVisibleWhen(visibleWhen)).toBe(false)
47
+ })
48
+ })
@@ -1 +1 @@
1
- .im-c-search-form{display:none;padding:0;min-height:auto;border-radius:0;filter:var(--search-drop-shadow);-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.im-c-search-form:before{border-radius:0;box-shadow:none}.im-o-app--mobile .im-c-search-form{position:absolute;left:0;top:0;right:0;height:auto;overflow:visible}.im-o-app--tablet .im-c-search-form,.im-o-app--desktop .im-c-search-form{position:relative;overflow:visible;border-radius:var(--button-border-radius);box-shadow:none}.im-o-app--tablet .im-c-search-form:before,.im-o-app--desktop .im-c-search-form:before{border-radius:var(--button-border-radius)}.im-o-app--tablet .im-c-search-form .im-c-search-suggestions,.im-o-app--desktop .im-c-search-form .im-c-search-suggestions{position:absolute;top:calc(100% + 2px);left:0;right:0;z-index:1}.im-c-search__status{position:absolute;top:100%;left:0;right:0;padding:var(--divider-gap) calc(var(--divider-gap) + 2px);background-color:var(--background-color);border-top:1px solid var(--button-hover-color);border-bottom-left-radius:var(--button-border-radius);border-bottom-right-radius:var(--button-border-radius)}.im-o-app--mobile .im-c-search-form--default-expanded{position:relative;margin:calc(-1*var(--primary-gap)) calc(-1*var(--primary-gap)) 0}.im-o-app--mobile .im-c-search-form--default-expanded .im-c-search-suggestions{position:absolute;top:100%;width:100%;left:0}.im-c-search-close-button{position:relative}.im-c-search-close-button:before{box-shadow:none}.im-c-search-close-button[data-focus-visible=true]{z-index:1}.im-c-search-close-button:hover{z-index:1}.im-c-search-submit-button{position:relative}.im-c-search-submit-button:before{box-shadow:none}.im-c-search-submit-button[data-focus-visible=true]{z-index:1}.im-c-search-suggestions{max-height:calc(100vh - 66px);overflow-y:auto;scroll-padding:0;margin:0;padding:0;background-color:var(--background-color);list-style:none}.im-c-search-suggestions__item{margin:0;padding:calc(var(--divider-gap) + 2px) var(--primary-gap);position:relative;color:var(--secondary-text-color);border-top:1px solid var(--button-hover-color);cursor:pointer;user-select:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search-suggestions__item{color:Highlight}}.im-c-search-suggestions__item mark{font-weight:bold;color:var(--foreground-color);background-color:rgba(0,0,0,0);pointer-events:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search-suggestions__item mark{color:Highlight}}@media(hover: hover)and (pointer: fine){.im-c-search-suggestions__item:hover:not([aria-selected=true]){background-color:var(--button-hover-color)}}.im-c-search-suggestions__item[aria-selected=true]{background-color:var(--button-hover-color)}.im-c-search-suggestions__item[aria-selected=true] mark{color:var(--fixed-foreground-color)}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search-suggestions__item[aria-selected=true] mark{color:Highlight}}.im-c-search-suggestions__item:hover:not([aria-selected=true]) .im-c-search-suggestions__label{text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:2px;text-decoration-color:var(--foreground-color);text-decoration-skip-ink:none}.im-c-search-suggestions__item[aria-selected=true] .im-c-search-suggestions__label{outline:3px solid rgba(0,0,0,0);color:var(--focus-border-color);background-color:var(--focus-outline-color);box-shadow:0 -2px var(--focus-outline-color),0 4px var(--focus-border-color);text-decoration:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search-suggestions__item[aria-selected=true] .im-c-search-suggestions__label{outline-color:Highlight;color:Highlight}}.im-c-search-suggestions__label{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.im-o-app--mobile .im-c-search-form--default-expanded .im-c-search-suggestions{box-shadow:var(--panel-box-shadow);clip-path:inset(0px 0px -20px 0px)}.im-o-app--tablet .im-c-search-suggestions,.im-o-app--desktop .im-c-search-suggestions{border-bottom-left-radius:var(--button-border-radius);border-bottom-right-radius:var(--button-border-radius)}.im-o-app--tablet .im-c-search-suggestions__item,.im-o-app--desktop .im-c-search-suggestions__item{padding:var(--divider-gap) calc(var(--divider-gap) + 2px)}:root{--search-drop-shadow: drop-shadow(0 -2px 20px rgba(0,0,0,.1)) drop-shadow(0 0 12px rgba(0,0,0,.1))}.im-c-search--collapsed{display:none}.im-c-search__input-container{display:flex;position:relative;flex:1 1 auto;min-height:auto;height:var(--button-size)}.im-c-search__input-container:before{content:"";z-index:1;position:absolute;top:-2px;right:-2px;bottom:-2px;left:-2px;border-radius:var(--button-border-radius);border:2px solid var(--foreground-color);pointer-events:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container:before{border-color:ButtonText}}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container:hover:not(:has(.im-c-search-close-button:hover)):before{border-color:Highlight}}.im-c-search__input-container:after{position:absolute;content:"";pointer-events:none;z-index:99;top:-1px;right:-1px;bottom:-1px;left:-1px;border:var(--focus-border-width) solid var(--focus-border-color);outline:var(--focus-box-shadow-width) solid var(--focus-outline-color);border-radius:calc(var(--button-border-radius) - 2px);opacity:0}@media(prefers-reduced-motion: no-preference){.im-c-search__input-container:after{transition:opacity var(--duration) ease}}.im-o-app .im-c-search__input{width:100%;color:var(--foreground-color);background-color:rgba(0,0,0,0);font-size:1rem;border:0;padding:var(--divider-gap) calc(var(--divider-gap) + 2px);outline:0}.im-o-app .im-c-search__input:focus{outline:3px solid rgba(0,0,0,0);box-shadow:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-o-app .im-c-search__input:focus{outline:none}}.im-o-app .im-c-search__input::placeholder{color:var(--foreground-color)}.im-o-app .im-c-search__input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}.im-o-app .im-c-search__input::-moz-search-clear{display:none}.im-c-search__hint{display:none}.im-o-app--exclusive-control .im-o-app__top .im-c-button-wrapper,.im-o-app--exclusive-control .im-o-app__right .im-c-button-wrapper{opacity:0;z-index:-1}.im-o-app--exclusive-control .im-c-button-wrapper--search{position:absolute;pointer-events:none}.im-o-app--exclusive-control .im-o-app__left-top .im-c-panel,.im-o-app--exclusive-control .im-o-app__left-bottom .im-c-panel,.im-o-app--exclusive-control .im-o-app__right-top .im-c-panel,.im-o-app--exclusive-control .im-o-app__right-bottom .im-c-panel{opacity:0;z-index:-1}.im-o-app--mobile .im-c-search__input-container{margin:var(--primary-gap)}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container--keyboard-focus-within:before{border-color:Highlight}}.im-c-search__input-container--keyboard-focus-within:after{opacity:1}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container--keyboard-focus-within:after{opacity:0}}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container--keyboard-focus-within{outline:var(--focus-highlight-width) solid Highlight}}
1
+ .im-c-search-form{display:none;padding:0;min-height:auto;border-radius:0;filter:var(--search-drop-shadow)}.im-c-search-form:before{border-radius:0;box-shadow:none}.im-o-app--mobile .im-c-search-form{position:relative;margin:calc(-1*var(--primary-gap)) calc(-1*var(--primary-gap)) 0;overflow:visible}.im-o-app--tablet .im-c-search-form,.im-o-app--desktop .im-c-search-form{position:relative;overflow:visible;border-radius:var(--button-border-radius);box-shadow:none}.im-o-app--tablet .im-c-search-form:before,.im-o-app--desktop .im-c-search-form:before{border-radius:var(--button-border-radius)}.im-o-app--tablet .im-c-search-form .im-c-search-suggestions,.im-o-app--desktop .im-c-search-form .im-c-search-suggestions{position:absolute;top:calc(100% + 2px);left:0;right:0;z-index:1}.im-c-search__status{position:absolute;top:100%;left:0;right:0;padding:var(--divider-gap) calc(var(--divider-gap) + 2px);background-color:var(--background-color);border-top:1px solid var(--button-hover-color);border-bottom-left-radius:var(--button-border-radius);border-bottom-right-radius:var(--button-border-radius)}.im-o-app--mobile .im-c-search-form--default-expanded .im-c-search-suggestions{position:absolute;top:100%;width:100%;left:0}.im-c-search-close-button{position:relative}.im-c-search-close-button:before{box-shadow:none}.im-c-search-close-button[data-focus-visible=true]{z-index:1}.im-c-search-close-button:hover{z-index:1}.im-c-search-submit-button{position:relative}.im-c-search-submit-button:before{box-shadow:none}.im-c-search-submit-button[data-focus-visible=true]{z-index:1}.im-c-search-suggestions{max-height:calc(100vh - 66px);overflow-y:auto;scroll-padding:0;margin:0;padding:0;background-color:var(--background-color);list-style:none}.im-c-search-suggestions__item{margin:0;padding:calc(var(--divider-gap) + 2px) var(--primary-gap);position:relative;color:var(--secondary-text-color);border-top:1px solid var(--button-hover-color);cursor:pointer;user-select:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search-suggestions__item{color:Highlight}}.im-c-search-suggestions__item mark{font-weight:bold;color:var(--foreground-color);background-color:rgba(0,0,0,0);pointer-events:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search-suggestions__item mark{color:Highlight}}@media(hover: hover)and (pointer: fine){.im-c-search-suggestions__item:hover:not([aria-selected=true]){background-color:var(--button-hover-color)}}.im-c-search-suggestions__item[aria-selected=true]{background-color:var(--button-hover-color)}.im-c-search-suggestions__item[aria-selected=true] mark{color:var(--fixed-foreground-color)}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search-suggestions__item[aria-selected=true] mark{color:Highlight}}.im-c-search-suggestions__item:hover:not([aria-selected=true]) .im-c-search-suggestions__label{text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:2px;text-decoration-color:var(--foreground-color);text-decoration-skip-ink:none}.im-c-search-suggestions__item[aria-selected=true] .im-c-search-suggestions__label{outline:3px solid rgba(0,0,0,0);color:var(--focus-border-color);background-color:var(--focus-outline-color);box-shadow:0 -2px var(--focus-outline-color),0 4px var(--focus-border-color);text-decoration:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search-suggestions__item[aria-selected=true] .im-c-search-suggestions__label{outline-color:Highlight;color:Highlight}}.im-c-search-suggestions__label{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.im-o-app--mobile .im-c-search-form--default-expanded .im-c-search-suggestions{box-shadow:var(--panel-box-shadow);clip-path:inset(0px 0px -20px 0px)}.im-o-app--tablet .im-c-search-suggestions,.im-o-app--desktop .im-c-search-suggestions{border-bottom-left-radius:var(--button-border-radius);border-bottom-right-radius:var(--button-border-radius)}.im-o-app--tablet .im-c-search-suggestions__item,.im-o-app--desktop .im-c-search-suggestions__item{padding:var(--divider-gap) calc(var(--divider-gap) + 2px)}:root{--search-drop-shadow: drop-shadow(0 -2px 20px rgba(0,0,0,.1)) drop-shadow(0 0 12px rgba(0,0,0,.1))}.im-c-search{display:flow-root}.im-c-search--collapsed{display:none}.im-o-app__header:has(>.im-c-search--collapsed:only-child){margin-bottom:0}.im-c-search__input-container{display:flex;position:relative;flex:1 1 auto;min-height:auto;height:var(--button-size)}.im-c-search__input-container:before{content:"";z-index:1;position:absolute;top:-2px;right:-2px;bottom:-2px;left:-2px;border-radius:var(--button-border-radius);border:2px solid var(--foreground-color);pointer-events:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container:before{border-color:ButtonText}}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container:hover:not(:has(.im-c-search-close-button:hover)):before{border-color:Highlight}}.im-c-search__input-container:after{position:absolute;content:"";pointer-events:none;z-index:99;top:-1px;right:-1px;bottom:-1px;left:-1px;border:var(--focus-border-width) solid var(--focus-border-color);outline:var(--focus-box-shadow-width) solid var(--focus-outline-color);border-radius:calc(var(--button-border-radius) - 2px);opacity:0}@media(prefers-reduced-motion: no-preference){.im-c-search__input-container:after{transition:opacity var(--duration) ease}}.im-o-app .im-c-search__input{width:100%;color:var(--foreground-color);background-color:rgba(0,0,0,0);font-size:1rem;border:0;padding:var(--divider-gap) calc(var(--divider-gap) + 2px);outline:0}.im-o-app .im-c-search__input:focus{outline:3px solid rgba(0,0,0,0);box-shadow:none}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-o-app .im-c-search__input:focus{outline:none}}.im-o-app .im-c-search__input::placeholder{color:var(--foreground-color)}.im-o-app .im-c-search__input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}.im-o-app .im-c-search__input::-moz-search-clear{display:none}.im-c-search__hint{display:none}.im-o-app--exclusive-control .im-o-app__top .im-c-button-wrapper,.im-o-app--exclusive-control .im-o-app__right .im-c-button-wrapper{opacity:0;z-index:-1}.im-o-app--exclusive-control .im-c-button-wrapper--search{position:absolute;pointer-events:none}.im-o-app--exclusive-control .im-o-app__left-top .im-c-panel,.im-o-app--exclusive-control .im-o-app__left-bottom .im-c-panel,.im-o-app--exclusive-control .im-o-app__right-top .im-c-panel,.im-o-app--exclusive-control .im-o-app__right-bottom .im-c-panel{opacity:0;z-index:-1}.im-o-app--exclusive-control .im-o-app__banner{opacity:0;z-index:-1}.im-o-app--mobile .im-c-search__input-container{margin:var(--primary-gap)}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container--keyboard-focus-within:before{border-color:Highlight}}.im-c-search__input-container--keyboard-focus-within:after{opacity:1}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container--keyboard-focus-within:after{opacity:0}}@media(-ms-high-contrast: active),screen and (forced-colors: active){.im-c-search__input-container--keyboard-focus-within{outline:var(--focus-highlight-width) solid Highlight}}