@defra/interactive-map 0.0.40-alpha → 0.0.42-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 (374) 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/button-definition.md +7 -8
  7. package/docs/api/control-definition.md +17 -1
  8. package/docs/api/panel-definition.md +5 -0
  9. package/docs/api/slots.md +50 -6
  10. package/docs/assets/images/slot-map.svg +61 -262
  11. package/docs/examples/add-polygons.mdx +8 -2
  12. package/docs/examples/add-symbols.mdx +8 -2
  13. package/docs/examples/draw-tools.mdx +3 -3
  14. package/docs/plugins/datasets.md +23 -15
  15. package/docs/plugins/draw.md +79 -16
  16. package/docs/plugins/map-key.md +147 -0
  17. package/docs/plugins/search.md +1 -1
  18. package/docs/plugins.md +5 -1
  19. package/govuk-prototype-kit.config.json +2 -0
  20. package/jest.setup.js +5 -0
  21. package/package.json +9 -1
  22. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  23. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  24. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  25. package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
  26. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
  27. package/plugins/beta/map-styles/dist/umd/index.js +1 -1
  28. package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -1
  29. package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -1
  30. package/plugins/beta/use-location/src/manifest.js +1 -1
  31. package/plugins/datasets/dist/css/index.css +1 -1
  32. package/plugins/datasets/dist/esm/esriLayerAdapter.js +1 -1
  33. package/plugins/datasets/dist/esm/im-datasets-ml-adapter.js +1 -1
  34. package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
  35. package/plugins/datasets/dist/umd/im-datasets-esri-adapter.js +1 -1
  36. package/plugins/datasets/dist/umd/im-datasets-ml-adapter.js +1 -1
  37. package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
  38. package/plugins/datasets/dist/umd/index.js +1 -1
  39. package/plugins/datasets/src/adapters/esri/esriLayerAdapter.test.js +175 -0
  40. package/plugins/datasets/src/adapters/esri/registry/esriDataset.test.js +55 -0
  41. package/plugins/datasets/src/adapters/loadLayerAdapter.test.js +61 -0
  42. package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +14 -0
  43. package/plugins/datasets/src/api/setOpacity.test.js +9 -0
  44. package/plugins/datasets/src/components/LayersMenu/LayersMenu.jsx +10 -25
  45. package/plugins/datasets/src/components/LayersMenu/LayersMenu.test.jsx +0 -42
  46. package/plugins/datasets/src/datasets.scss +0 -1
  47. package/plugins/datasets/src/fetch/createDynamicSource.js +135 -129
  48. package/plugins/datasets/src/fetch/createDynamicSource.test.js +318 -0
  49. package/plugins/datasets/src/fetch/fetchGeoJSON.test.js +83 -0
  50. package/plugins/datasets/src/index.test.js +36 -0
  51. package/plugins/datasets/src/initialise/DatasetsInit.jsx +17 -7
  52. package/plugins/datasets/src/initialise/DatasetsInit.test.jsx +262 -0
  53. package/plugins/datasets/src/initialise/initialiseDatasets.js +16 -4
  54. package/plugins/datasets/src/initialise/initialiseDatasets.test.js +42 -0
  55. package/plugins/datasets/src/manifest.js +0 -38
  56. package/plugins/datasets/src/manifest.test.js +49 -0
  57. package/plugins/datasets/src/reducers/datasetsToMenu.test.js +54 -1
  58. package/plugins/datasets/src/reducers/pluginState.js +3 -16
  59. package/plugins/datasets/src/reducers/pluginState.test.js +23 -0
  60. package/plugins/datasets/src/registry/dataset.js +13 -1
  61. package/plugins/datasets/src/registry/dataset.test.js +61 -0
  62. package/plugins/datasets/src/registry/datasetRegistry.js +12 -6
  63. package/plugins/datasets/src/registry/datasetRegistry.test.js +16 -4
  64. package/plugins/datasets/src/registry/isVisibleWhen.js +6 -3
  65. package/plugins/datasets/src/registry/isVisibleWhen.test.js +11 -1
  66. package/plugins/datasets/src/utils/bbox.test.js +125 -0
  67. package/plugins/draw/dist/esm/im-draw-ml-adapter.js +1 -1
  68. package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -1
  69. package/plugins/draw/dist/esm/im-draw-plugin.js +1 -1
  70. package/plugins/draw/dist/esm/index.js +1 -1
  71. package/plugins/draw/dist/umd/im-draw-ml-adapter.js +2 -1
  72. package/plugins/draw/dist/umd/im-draw-ml-adapter.js.LICENSE.txt +1 -0
  73. package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
  74. package/plugins/draw/dist/umd/im-draw-plugin.js +1 -1
  75. package/plugins/draw/dist/umd/index.js +1 -1
  76. package/plugins/draw/src/DrawInit.jsx +10 -2
  77. package/plugins/draw/src/DrawInit.test.jsx +51 -10
  78. package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +91 -11
  79. package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.test.js +246 -4
  80. package/plugins/draw/src/adapters/maplibre/mapboxDraw.js +109 -14
  81. package/plugins/draw/src/adapters/maplibre/mapboxDraw.test.js +174 -11
  82. package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.test.js +12 -0
  83. package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.js +9 -1
  84. package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.test.js +24 -3
  85. package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.js +194 -0
  86. package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.test.js +457 -0
  87. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/__helpers__/harness.js +102 -0
  88. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.js +82 -0
  89. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.test.js +83 -0
  90. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.js +55 -0
  91. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.test.js +62 -0
  92. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.js +138 -0
  93. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.test.js +197 -0
  94. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.js +92 -0
  95. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.test.js +101 -0
  96. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.js +46 -0
  97. package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.test.js +42 -0
  98. package/plugins/draw/src/adapters/maplibre/modes/editPointMode.js +151 -0
  99. package/plugins/draw/src/adapters/maplibre/modes/editPointMode.test.js +156 -0
  100. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.js +4 -27
  101. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.test.js +24 -39
  102. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.js +4 -5
  103. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.js +20 -23
  104. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.test.js +25 -5
  105. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.js +6 -36
  106. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.test.js +7 -49
  107. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.js +3 -53
  108. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.test.js +8 -33
  109. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.js +29 -72
  110. package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.test.js +10 -3
  111. package/plugins/draw/src/adapters/maplibre/pointSymbolImages.js +158 -0
  112. package/plugins/draw/src/adapters/maplibre/pointSymbolImages.test.js +314 -0
  113. package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.js +6 -1
  114. package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.test.js +21 -0
  115. package/plugins/draw/src/adapters/maplibre/snap/snapInstance.js +6 -2
  116. package/plugins/draw/src/adapters/maplibre/snap/snapInstance.test.js +12 -0
  117. package/plugins/draw/src/adapters/maplibre/styles.js +38 -11
  118. package/plugins/draw/src/adapters/maplibre/styles.test.js +39 -0
  119. package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.js +79 -0
  120. package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.test.js +135 -0
  121. package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.js +42 -0
  122. package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.test.js +85 -0
  123. package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.js +34 -0
  124. package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.test.js +78 -0
  125. package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.js +16 -3
  126. package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.test.js +21 -3
  127. package/plugins/draw/src/adapters/maplibre/utils/snapMovement.js +59 -0
  128. package/plugins/draw/src/adapters/maplibre/utils/snapMovement.test.js +73 -0
  129. package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.js +33 -0
  130. package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.test.js +70 -0
  131. package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.js +27 -2
  132. package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.test.js +93 -0
  133. package/plugins/draw/src/adapters/openlayers/__helpers__/harness.js +9 -0
  134. package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.js +19 -1
  135. package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.test.js +34 -1
  136. package/plugins/draw/src/adapters/openlayers/core/styles.js +73 -1
  137. package/plugins/draw/src/adapters/openlayers/core/styles.test.js +122 -3
  138. package/plugins/draw/src/adapters/openlayers/draw/DrawMode.test.js +12 -0
  139. package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +8 -2
  140. package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +31 -0
  141. package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +9 -5
  142. package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +33 -0
  143. package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.js +5 -3
  144. package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.test.js +11 -0
  145. package/plugins/draw/src/adapters/openlayers/edit/nudge.js +10 -3
  146. package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +24 -1
  147. package/plugins/draw/src/adapters/openlayers/edit/touchHandler.js +18 -13
  148. package/plugins/draw/src/adapters/openlayers/edit/touchHandler.test.js +46 -1
  149. package/plugins/draw/src/adapters/openlayers/olDraw.js +11 -4
  150. package/plugins/draw/src/adapters/openlayers/olDraw.test.js +48 -6
  151. package/plugins/draw/src/adapters/openlayers/point/drawPointMode.js +156 -0
  152. package/plugins/draw/src/adapters/openlayers/point/drawPointMode.test.js +246 -0
  153. package/plugins/draw/src/adapters/openlayers/point/editPointMode.js +267 -0
  154. package/plugins/draw/src/adapters/openlayers/point/editPointMode.test.js +327 -0
  155. package/plugins/draw/src/adapters/openlayers/point/pointDragInteraction.js +71 -0
  156. package/plugins/draw/src/adapters/openlayers/point/pointOps.js +34 -0
  157. package/plugins/draw/src/adapters/openlayers/point/pointOps.test.js +32 -0
  158. package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.js +82 -0
  159. package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.test.js +107 -0
  160. package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.js +83 -0
  161. package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.test.js +214 -0
  162. package/plugins/draw/src/adapters/openlayers/snap/snapIndicator.js +3 -1
  163. package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.js +6 -9
  164. package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.test.js +15 -4
  165. package/plugins/draw/src/adapters/openlayers/utils/olCoords.js +1 -1
  166. package/plugins/draw/src/api/addFeature.js +16 -1
  167. package/plugins/draw/src/api/addFeature.test.js +62 -5
  168. package/plugins/draw/src/api/createNewShape.js +41 -0
  169. package/plugins/draw/src/api/createNewShape.test.js +63 -0
  170. package/plugins/draw/src/api/editFeature.js +7 -4
  171. package/plugins/draw/src/api/editFeature.test.js +16 -0
  172. package/plugins/draw/src/api/newLine.js +2 -40
  173. package/plugins/draw/src/api/newPoint.js +42 -0
  174. package/plugins/draw/src/api/newPoint.test.js +117 -0
  175. package/plugins/draw/src/api/newPolygon.js +2 -40
  176. package/plugins/draw/src/api/setStyle.js +43 -0
  177. package/plugins/draw/src/api/setStyle.test.js +75 -0
  178. package/plugins/draw/src/events.js +17 -10
  179. package/plugins/draw/src/events.test.js +14 -0
  180. package/plugins/draw/src/manifest.js +18 -6
  181. package/plugins/draw/src/manifest.test.js +32 -1
  182. package/plugins/draw/src/reducer.test.js +7 -0
  183. package/plugins/draw/src/utils/stripInternalProperties.js +19 -0
  184. package/plugins/draw/src/utils/stripInternalProperties.test.js +50 -0
  185. package/plugins/draw/src/utils/symbolKeys.js +5 -0
  186. package/plugins/draw/src/validation/liveDrawChecks.test.js +54 -0
  187. package/plugins/draw/src/validation/liveStroke.test.js +64 -1
  188. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  189. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  190. package/plugins/interact/dist/umd/index.js +1 -1
  191. package/plugins/interact/src/hooks/useHighlightSync.js +33 -12
  192. package/plugins/interact/src/hooks/useHighlightSync.test.js +96 -25
  193. package/plugins/interact/src/manifest.js +1 -1
  194. package/plugins/interact/src/utils/featureQueries.js +18 -0
  195. package/plugins/interact/src/utils/featureQueries.test.js +15 -0
  196. package/plugins/interact/src/utils/spatial.js +9 -3
  197. package/plugins/interact/src/utils/spatial.test.js +12 -1
  198. package/plugins/map-key/dist/css/index.css +68 -0
  199. package/plugins/map-key/dist/esm/im-map-key-plugin.js +1 -0
  200. package/plugins/map-key/dist/esm/index.js +1 -0
  201. package/plugins/map-key/dist/umd/im-map-key-plugin.js +2 -0
  202. package/plugins/map-key/dist/umd/im-map-key-plugin.js.LICENSE.txt +1 -0
  203. package/plugins/map-key/dist/umd/index.js +2 -0
  204. package/plugins/map-key/dist/umd/index.js.LICENSE.txt +1 -0
  205. package/plugins/map-key/package.json +28 -0
  206. package/plugins/map-key/src/components/Key/EmptyKey.jsx +7 -0
  207. package/plugins/{datasets → map-key}/src/components/Key/EmptyKey.test.jsx +2 -2
  208. package/plugins/map-key/src/components/Key/Key.jsx +43 -0
  209. package/plugins/{datasets → map-key}/src/components/Key/Key.module.scss +21 -14
  210. package/plugins/map-key/src/components/Key/Key.test.jsx +73 -0
  211. package/plugins/map-key/src/components/Key/KeyGroupItem.jsx +16 -0
  212. package/plugins/map-key/src/components/Key/KeyGroupItem.test.jsx +53 -0
  213. package/plugins/map-key/src/components/Key/KeyItem.jsx +26 -0
  214. package/plugins/map-key/src/components/Key/KeyItem.test.jsx +67 -0
  215. package/plugins/map-key/src/components/Key/KeySvg.jsx +41 -0
  216. package/plugins/{datasets → map-key}/src/components/Key/KeySvg.test.jsx +14 -12
  217. package/plugins/map-key/src/components/Key/KeySvgLine.jsx +22 -0
  218. package/plugins/{datasets → map-key}/src/components/Key/KeySvgLine.test.jsx +1 -1
  219. package/plugins/{datasets → map-key}/src/components/Key/KeySvgPattern.jsx +7 -2
  220. package/plugins/{datasets → map-key}/src/components/Key/KeySvgPattern.test.jsx +8 -2
  221. package/plugins/map-key/src/components/Key/KeySvgRect.jsx +26 -0
  222. package/plugins/{datasets → map-key}/src/components/Key/KeySvgRect.test.jsx +1 -1
  223. package/plugins/{datasets → map-key}/src/components/Key/KeySvgSymbol.jsx +8 -3
  224. package/plugins/{datasets → map-key}/src/components/Key/KeySvgSymbol.test.jsx +17 -6
  225. package/plugins/map-key/src/components/Key/MapKey.jsx +53 -0
  226. package/plugins/map-key/src/components/Key/MapKey.test.jsx +147 -0
  227. package/plugins/{datasets → map-key}/src/components/Key/svgProperties.js +2 -2
  228. package/plugins/{datasets → map-key}/src/components/Key/svgProperties.test.js +2 -2
  229. package/plugins/map-key/src/index.js +13 -0
  230. package/plugins/map-key/src/index.test.js +31 -0
  231. package/plugins/map-key/src/initialise/MapKeyInit.jsx +15 -0
  232. package/plugins/map-key/src/initialise/MapKeyInit.test.jsx +39 -0
  233. package/plugins/map-key/src/manifest.js +30 -0
  234. package/plugins/map-key/src/manifest.test.js +63 -0
  235. package/plugins/map-key/src/mapKey.scss +4 -0
  236. package/plugins/map-key/src/registry/getDatasetRegistry.js +6 -0
  237. package/plugins/map-key/src/registry/getDatasetRegistry.test.js +23 -0
  238. package/plugins/map-key/src/registry/index.js +3 -0
  239. package/plugins/map-key/src/registry/index.test.js +15 -0
  240. package/plugins/menu/dist/css/index.css +80 -0
  241. package/plugins/menu/dist/esm/im-menu-plugin.js +1 -0
  242. package/plugins/menu/dist/esm/index.js +1 -0
  243. package/plugins/menu/dist/umd/im-menu-plugin.js +1 -0
  244. package/plugins/menu/dist/umd/index.js +2 -0
  245. package/plugins/menu/dist/umd/index.js.LICENSE.txt +1 -0
  246. package/plugins/menu/package.json +28 -0
  247. package/plugins/menu/src/components/Menu/CheckboxGroupWrapper.jsx +21 -0
  248. package/plugins/menu/src/components/Menu/GroupLegend.jsx +17 -0
  249. package/plugins/menu/src/components/Menu/GroupLegend.test.jsx +68 -0
  250. package/plugins/menu/src/components/Menu/Menu.jsx +19 -0
  251. package/plugins/menu/src/components/Menu/Menu.module.scss +90 -0
  252. package/plugins/menu/src/components/Menu/Menu.test.jsx +161 -0
  253. package/plugins/menu/src/components/Menu/MenuCheckbox.jsx +35 -0
  254. package/plugins/menu/src/components/Menu/MenuCheckbox.test.jsx +93 -0
  255. package/plugins/menu/src/components/Menu/MenuGroup.jsx +11 -0
  256. package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.jsx → menu/src/components/Menu/MenuRadio.jsx} +6 -4
  257. package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.test.jsx → menu/src/components/Menu/MenuRadio.test.jsx} +18 -18
  258. package/plugins/menu/src/components/Menu/RadioGroupWrapper.jsx +32 -0
  259. package/plugins/{datasets/src/components/LayersMenu/LayersRadioGroupWrapper.test.jsx → menu/src/components/Menu/RadioGroupWrapper.test.jsx} +21 -21
  260. package/plugins/menu/src/components/Menu/useIdPrefix.jsx +4 -0
  261. package/plugins/menu/src/index.js +13 -0
  262. package/plugins/menu/src/index.test.js +31 -0
  263. package/plugins/menu/src/initialise/MenuInit.jsx +24 -0
  264. package/plugins/menu/src/initialise/MenuInit.test.jsx +50 -0
  265. package/plugins/menu/src/initialise/createMenu.js +10 -0
  266. package/plugins/menu/src/initialise/createMenu.test.js +37 -0
  267. package/plugins/menu/src/manifest.js +35 -0
  268. package/plugins/menu/src/manifest.test.js +63 -0
  269. package/plugins/menu/src/menu.scss +4 -0
  270. package/plugins/menu/src/reducers/menuStateReducer.test.js +38 -0
  271. package/plugins/menu/src/reducers/pluginState.js +35 -0
  272. package/plugins/menu/src/reducers/pluginState.test.js +57 -0
  273. package/plugins/menu/src/registry/getDatasetRegistry.js +9 -0
  274. package/plugins/menu/src/registry/getDatasetRegistry.test.js +23 -0
  275. package/plugins/menu/src/registry/index.js +3 -0
  276. package/plugins/menu/src/registry/index.test.js +15 -0
  277. package/plugins/menu/src/registry/isVisibleWhen.js +43 -0
  278. package/plugins/menu/src/registry/isVisibleWhen.test.js +48 -0
  279. package/plugins/search/dist/css/index.css +1 -1
  280. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  281. package/plugins/search/dist/esm/index.js +1 -1
  282. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  283. package/plugins/search/dist/umd/index.js +1 -1
  284. package/plugins/search/src/Search.jsx +2 -3
  285. package/plugins/search/src/components/Form/Form.module.scss +9 -20
  286. package/plugins/search/src/index.js +0 -1
  287. package/plugins/search/src/index.test.js +2 -4
  288. package/plugins/search/src/manifest.js +1 -1
  289. package/plugins/search/src/search.scss +17 -16
  290. package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
  291. package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
  292. package/providers/beta/openlayers/dist/umd/index.js +1 -1
  293. package/providers/beta/openlayers/src/openlayersProvider.js +4 -1
  294. package/providers/beta/openlayers/src/openlayersProvider.test.js +18 -0
  295. package/providers/beta/openlayers/src/utils/highlightFeatures.js +52 -5
  296. package/providers/beta/openlayers/src/utils/highlightFeatures.test.js +176 -0
  297. package/providers/beta/openlayers/src/utils/symbolImages.js +40 -0
  298. package/providers/beta/openlayers/src/utils/symbolImages.test.js +58 -0
  299. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  300. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  301. package/providers/maplibre/dist/umd/index.js +1 -1
  302. package/providers/maplibre/src/utils/drawLayerBuckets.js +14 -0
  303. package/providers/maplibre/src/utils/drawLayerBuckets.test.js +18 -0
  304. package/providers/maplibre/src/utils/highlightFeatures.js +80 -55
  305. package/providers/maplibre/src/utils/highlightFeatures.test.js +156 -17
  306. package/providers/maplibre/src/utils/hoverCursor.js +30 -14
  307. package/providers/maplibre/src/utils/hoverCursor.test.js +35 -0
  308. package/providers/maplibre/src/utils/patternImages.js +1 -1
  309. package/providers/maplibre/src/utils/symbolImages.js +41 -3
  310. package/providers/maplibre/src/utils/symbolImages.test.js +40 -6
  311. package/rollup.esm.mjs +11 -1
  312. package/src/App/components/Actions/Actions.test.jsx +47 -0
  313. package/src/App/components/Hints/Hints.test.jsx +13 -0
  314. package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +25 -23
  315. package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +22 -5
  316. package/src/App/components/MoveControls/MoveControls.module.scss +41 -10
  317. package/src/App/components/Panel/Panel.jsx +65 -8
  318. package/src/App/components/Panel/Panel.module.scss +49 -15
  319. package/src/App/components/Panel/Panel.test.jsx +114 -1
  320. package/src/App/components/Tabs/Tabs.jsx +43 -17
  321. package/src/App/components/Tabs/Tabs.module.scss +32 -5
  322. package/src/App/components/Tabs/Tabs.test.jsx +40 -10
  323. package/src/App/components/Tooltip/Tooltip.test.jsx +12 -14
  324. package/src/App/hooks/useFeatureItems.js +16 -7
  325. package/src/App/hooks/useFeatureItems.test.js +57 -0
  326. package/src/App/hooks/useLayoutMeasurements.js +95 -84
  327. package/src/App/hooks/useLayoutMeasurements.test.js +61 -1
  328. package/src/App/layout/Layout.jsx +9 -10
  329. package/src/App/layout/Layout.test.jsx +2 -0
  330. package/src/App/layout/layout.module.scss +31 -17
  331. package/src/App/registry/pluginRegistry.js +1 -1
  332. package/src/App/registry/pluginRegistry.test.js +12 -0
  333. package/src/App/renderer/HtmlElementHost.jsx +18 -5
  334. package/src/App/renderer/HtmlElementHost.test.jsx +54 -0
  335. package/src/App/renderer/groupByKey.js +28 -0
  336. package/src/App/renderer/groupByKey.test.js +39 -0
  337. package/src/App/renderer/groupIntoTabs.js +42 -0
  338. package/src/App/renderer/groupIntoTabs.test.js +95 -0
  339. package/src/App/renderer/mapButtons.js +97 -129
  340. package/src/App/renderer/mapButtons.test.js +67 -83
  341. package/src/App/renderer/mapControls.js +4 -1
  342. package/src/App/renderer/mapControls.test.js +32 -0
  343. package/src/App/renderer/mapPanels.js +53 -11
  344. package/src/App/renderer/mapPanels.test.js +95 -2
  345. package/src/App/renderer/orderItems.js +28 -0
  346. package/src/App/renderer/orderItems.test.js +50 -0
  347. package/src/App/renderer/slotAggregator.js +2 -18
  348. package/src/App/renderer/slotHelpers.js +3 -1
  349. package/src/App/renderer/slotHelpers.test.js +5 -0
  350. package/src/App/renderer/slots.js +3 -0
  351. package/src/App/store/AppProvider.jsx +1 -0
  352. package/src/App/store/appDispatchMiddleware.js +1 -1
  353. package/src/App/store/appDispatchMiddleware.test.js +8 -0
  354. package/src/config/appConfig.js +2 -2
  355. package/src/config/events.js +2 -0
  356. package/src/scss/settings/_colors.scss +1 -0
  357. package/src/scss/settings/_dimensions.scss +2 -3
  358. package/src/scss/settings/_transition.scss +1 -1
  359. package/src/services/eventBus.js +44 -0
  360. package/src/services/eventBus.test.js +83 -0
  361. package/src/services/symbolRegistry.js +1 -1
  362. package/src/types.js +31 -3
  363. package/webpack.umd.mjs +2 -0
  364. package/plugins/datasets/src/components/Key/EmptyKey.jsx +0 -7
  365. package/plugins/datasets/src/components/Key/Key.jsx +0 -50
  366. package/plugins/datasets/src/components/Key/KeyGroupItem.jsx +0 -18
  367. package/plugins/datasets/src/components/Key/KeyItem.jsx +0 -23
  368. package/plugins/datasets/src/components/Key/KeySvg.jsx +0 -22
  369. package/plugins/datasets/src/components/Key/KeySvgLine.jsx +0 -19
  370. package/plugins/datasets/src/components/Key/KeySvgRect.jsx +0 -22
  371. package/plugins/datasets/src/components/LayersMenu/LayersRadioGroupWrapper.jsx +0 -41
  372. /package/plugins/{datasets → menu}/src/reducers/menuStateReducer.js +0 -0
  373. /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.js +0 -0
  374. /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.test.js +0 -0
@@ -1,21 +1,28 @@
1
+
1
2
  // When groups are present, every direct child gets a border-top
2
- .im-c-datasets-key > *:first-child {
3
+ .im-c-map-key > *:first-child {
3
4
  padding-top: 5px;
4
5
 
5
- .im-c-datasets-key__group-heading {
6
+ .im-c-map-key__group-heading {
6
7
  padding-top: 0;
7
8
  }
8
9
  }
9
10
 
10
- .im-c-datasets-key--has-groups > *:not(:first-child) {
11
+ .im-c-map-key--has-groups > *:not(:first-child) {
11
12
  border-top: 1px solid var(--button-hover-color);
12
13
  }
13
14
 
14
- .im-c-datasets-key__group:not(:last-child) {
15
+ .im-c-map-key__group:not(:last-child) {
15
16
  padding-bottom: 5px;
16
17
  }
17
18
 
18
- .im-c-datasets-key__group-heading {
19
+ .im-c-map-key__empty-message {
20
+ margin: 0;
21
+ font-size: 1rem;
22
+ color: var(--foreground-color);
23
+ }
24
+
25
+ .im-c-map-key__group-heading {
19
26
  padding-top: 15px;
20
27
  padding-bottom: 10px;
21
28
  margin: 0;
@@ -24,7 +31,7 @@
24
31
  color: var(--foreground-color);
25
32
  }
26
33
 
27
- .im-c-datasets-key__item {
34
+ .im-c-map-key__item {
28
35
  display: flex;
29
36
  align-items: start;
30
37
  padding-top: 10px;
@@ -32,38 +39,38 @@
32
39
  font-size: 1rem;
33
40
 
34
41
  // First item inside a group — heading already provides top spacing
35
- .im-c-datasets-key__group &:first-child {
42
+ .im-c-map-key__group &:first-child {
36
43
  padding-top: 0;
37
44
  }
38
45
  }
39
46
 
40
- .im-c-datasets-key--has-groups > .im-c-datasets-key__item:not(:first-child) {
47
+ .im-c-map-key--has-groups > .im-c-map-key__item:not(:first-child) {
41
48
  padding-top: 15px;
42
49
  }
43
50
 
44
- .im-c-datasets-key--has-groups > .im-c-datasets-key__item {
51
+ .im-c-map-key--has-groups > .im-c-map-key__item {
45
52
  padding-bottom: 15px;
46
53
  }
47
54
 
48
- .im-c-datasets-key__group .im-c-datasets-key__item {
55
+ .im-c-map-key__group .im-c-map-key__item {
49
56
  padding-top: 10px;
50
57
  padding-bottom: 10px;
51
58
  }
52
59
 
53
60
  // Last item in all scenarios — flat or inside the last group
54
- .im-c-datasets-key > .im-c-datasets-key__item:last-child,
55
- .im-c-datasets-key__group:last-child .im-c-datasets-key__item:last-child {
61
+ .im-c-map-key > .im-c-map-key__item:last-child,
62
+ .im-c-map-key__group:last-child .im-c-map-key__item:last-child {
56
63
  padding-bottom: 5px;
57
64
  }
58
65
 
59
66
  // Symbol
60
67
 
61
- .am-c-datasets-key-symbol {
68
+ .am-c-map-key-symbol {
62
69
  position: relative;
63
70
  flex-shrink: 0;
64
71
  margin: 0 13px 0 3px;
65
72
  }
66
73
 
67
- .am-c-datasets-key-symbol--point {
74
+ .am-c-map-key-symbol--point {
68
75
  margin: -12px 1px -12px -9px;
69
76
  }
@@ -0,0 +1,73 @@
1
+ import { render, screen } from '@testing-library/react'
2
+ import { Key } from './Key'
3
+
4
+ jest.mock('./EmptyKey.jsx', () => ({
5
+ EmptyKey: ({ text }) => <div data-testid='empty-key'>{text}</div>
6
+ }))
7
+
8
+ jest.mock('./KeyItem.jsx', () => ({
9
+ KeyItem: () => <div data-testid='key-item' />
10
+ }))
11
+
12
+ jest.mock('./KeyGroupItem.jsx', () => ({
13
+ KeyGroupItem: ({ label }) => <div data-testid='key-group-item'>{label}</div>
14
+ }))
15
+
16
+ const mapStyle = { id: 'default' }
17
+
18
+ describe('Key', () => {
19
+ it('renders EmptyKey when keyGroups is undefined', () => {
20
+ render(<Key mapStyle={mapStyle} />)
21
+ expect(screen.getByTestId('empty-key')).toBeTruthy()
22
+ })
23
+
24
+ it('renders EmptyKey when keyGroups is an empty array', () => {
25
+ render(<Key mapStyle={mapStyle} keyGroups={[]} />)
26
+ expect(screen.getByTestId('empty-key')).toBeTruthy()
27
+ })
28
+
29
+ it('passes noKeyItemText to EmptyKey', () => {
30
+ render(<Key mapStyle={mapStyle} noKeyItemText='No items' />)
31
+ expect(screen.getByText('No items')).toBeTruthy()
32
+ })
33
+
34
+ it('renders the wrapper div with the base class', () => {
35
+ const { container } = render(<Key mapStyle={mapStyle} keyGroups={[{ id: '1', keyDefinition: { id: '1' } }]} />)
36
+ expect(container.querySelector('.im-c-map-key')).toBeTruthy()
37
+ })
38
+
39
+ it('adds the groups class when hasGroups is true', () => {
40
+ const { container } = render(<Key mapStyle={mapStyle} keyGroups={[{ id: '1', keyDefinition: { id: '1' } }]} hasGroups />)
41
+ expect(container.querySelector('.im-c-map-key--has-groups')).toBeTruthy()
42
+ })
43
+
44
+ it('does not add the groups class when hasGroups is false', () => {
45
+ const { container } = render(<Key mapStyle={mapStyle} keyGroups={[{ id: '1', keyDefinition: { id: '1' } }]} hasGroups={false} />)
46
+ expect(container.querySelector('.im-c-map-key--has-groups')).toBeNull()
47
+ })
48
+
49
+ it('renders KeyItem for a non-group item', () => {
50
+ render(<Key mapStyle={mapStyle} keyGroups={[{ id: '1', keyDefinition: { id: '1' } }]} />)
51
+ expect(screen.getByTestId('key-item')).toBeTruthy()
52
+ })
53
+
54
+ it('renders KeyGroupItem for a group item', () => {
55
+ render(<Key mapStyle={mapStyle} keyGroups={[{ id: '1', type: 'group', groupLabel: 'Group A', keyDefinitions: [] }]} />)
56
+ expect(screen.getByTestId('key-group-item')).toBeTruthy()
57
+ })
58
+
59
+ it('passes groupLabel to KeyGroupItem', () => {
60
+ render(<Key mapStyle={mapStyle} keyGroups={[{ id: '1', type: 'group', groupLabel: 'My Group', keyDefinitions: [] }]} />)
61
+ expect(screen.getByText('My Group')).toBeTruthy()
62
+ })
63
+
64
+ it('renders mixed group and non-group items', () => {
65
+ const keyGroups = [
66
+ { id: '1', keyDefinition: { id: '1' } },
67
+ { id: '2', type: 'group', groupLabel: 'Group A', keyDefinitions: [] }
68
+ ]
69
+ render(<Key mapStyle={mapStyle} keyGroups={keyGroups} />)
70
+ expect(screen.getAllByTestId('key-item')).toHaveLength(1)
71
+ expect(screen.getAllByTestId('key-group-item')).toHaveLength(1)
72
+ })
73
+ })
@@ -0,0 +1,16 @@
1
+ import { KeyItem } from './KeyItem.jsx'
2
+
3
+ export const KeyGroupItem = ({ headingId, label, keyDefinitions, mapStyle }) => {
4
+ return (
5
+ <section className='im-c-map-key__group' aria-labelledby={headingId}>
6
+ <h3 id={headingId} className='im-c-map-key__group-heading'>{label}</h3>
7
+ {keyDefinitions.map(keyDefinition =>
8
+ <KeyItem
9
+ key={`${keyDefinition.id}`}
10
+ keyDefinition={keyDefinition}
11
+ mapStyle={mapStyle}
12
+ />
13
+ )}
14
+ </section>
15
+ )
16
+ }
@@ -0,0 +1,53 @@
1
+ import { render, screen } from '@testing-library/react'
2
+ import { KeyGroupItem } from './KeyGroupItem'
3
+
4
+ jest.mock('./KeyItem.jsx', () => ({
5
+ KeyItem: ({ keyDefinition }) => <div data-testid='key-item'>{keyDefinition.id}</div>
6
+ }))
7
+
8
+ const mapStyle = { id: 'default' }
9
+
10
+ const baseProps = {
11
+ headingId: 'key-heading-1',
12
+ label: 'My Group',
13
+ keyDefinitions: [],
14
+ mapStyle
15
+ }
16
+
17
+ describe('KeyGroupItem', () => {
18
+ it('renders a section with the group class', () => {
19
+ const { container } = render(<KeyGroupItem {...baseProps} />)
20
+ expect(container.querySelector('.im-c-map-key__group')).toBeTruthy()
21
+ })
22
+
23
+ it('sets aria-labelledby to headingId on the section', () => {
24
+ const { container } = render(<KeyGroupItem {...baseProps} />)
25
+ expect(container.querySelector('section').getAttribute('aria-labelledby')).toBe('key-heading-1')
26
+ })
27
+
28
+ it('renders the heading with the correct id', () => {
29
+ render(<KeyGroupItem {...baseProps} />)
30
+ expect(document.getElementById('key-heading-1')).toBeTruthy()
31
+ })
32
+
33
+ it('renders the heading with the group-heading class', () => {
34
+ const { container } = render(<KeyGroupItem {...baseProps} />)
35
+ expect(container.querySelector('.im-c-map-key__group-heading')).toBeTruthy()
36
+ })
37
+
38
+ it('renders the label text in the heading', () => {
39
+ render(<KeyGroupItem {...baseProps} />)
40
+ expect(screen.getByText('My Group')).toBeTruthy()
41
+ })
42
+
43
+ it('renders a KeyItem for each keyDefinition', () => {
44
+ const keyDefinitions = [{ id: 'a' }, { id: 'b' }, { id: 'c' }]
45
+ render(<KeyGroupItem {...baseProps} keyDefinitions={keyDefinitions} />)
46
+ expect(screen.getAllByTestId('key-item')).toHaveLength(3)
47
+ })
48
+
49
+ it('renders no KeyItems when keyDefinitions is empty', () => {
50
+ render(<KeyGroupItem {...baseProps} keyDefinitions={[]} />)
51
+ expect(screen.queryAllByTestId('key-item')).toHaveLength(0)
52
+ })
53
+ })
@@ -0,0 +1,26 @@
1
+ import { getValueForStyle } from '../../../../../src/utils/getValueForStyle.js'
2
+ import { KeySvg } from './KeySvg.jsx'
3
+
4
+ export const KeyItem = ({ keyDefinition, mapStyle }) => {
5
+ // Pure derivations of props — computed directly on every render rather than staged through
6
+ // useState/useEffect, so there's no post-mount frame where the label/description are blank
7
+ // (every panel reopen mounts this fresh, so that frame was visible as a flicker).
8
+ const label = keyDefinition.label
9
+ const symbolDescription = getValueForStyle(keyDefinition.symbolDescription, mapStyle.id)
10
+
11
+ return (
12
+ <dl className='im-c-map-key__item'>
13
+ <dt className='im-c-map-key__item-symbol'>
14
+ <KeySvg keyDefinition={keyDefinition} mapStyle={mapStyle} />
15
+ </dt>
16
+ <dd className='im-c-map-key__item-label'>
17
+ {label}
18
+ {symbolDescription && (
19
+ <span className='govuk-visually-hidden'>
20
+ ({symbolDescription})
21
+ </span>
22
+ )}
23
+ </dd>
24
+ </dl>
25
+ )
26
+ }
@@ -0,0 +1,67 @@
1
+ import { render, screen } from '@testing-library/react'
2
+ import { KeyItem } from './KeyItem'
3
+ import { getValueForStyle } from '../../../../../src/utils/getValueForStyle.js'
4
+
5
+ jest.mock('../../../../../src/utils/getValueForStyle.js', () => ({
6
+ getValueForStyle: jest.fn()
7
+ }))
8
+
9
+ jest.mock('./KeySvg.jsx', () => ({
10
+ KeySvg: () => <svg data-testid='key-svg' />
11
+ }))
12
+
13
+ const mapStyle = { id: 'default' }
14
+
15
+ const baseKeyDefinition = {
16
+ id: '1',
17
+ label: 'My Label',
18
+ symbolDescription: null
19
+ }
20
+
21
+ beforeEach(() => {
22
+ getValueForStyle.mockReturnValue(null)
23
+ })
24
+
25
+ describe('KeyItem', () => {
26
+ it('renders a dl with the item class', () => {
27
+ const { container } = render(<KeyItem keyDefinition={baseKeyDefinition} mapStyle={mapStyle} />)
28
+ expect(container.querySelector('.im-c-map-key__item')).toBeTruthy()
29
+ })
30
+
31
+ it('renders the symbol dt with the correct class', () => {
32
+ const { container } = render(<KeyItem keyDefinition={baseKeyDefinition} mapStyle={mapStyle} />)
33
+ expect(container.querySelector('.im-c-map-key__item-symbol')).toBeTruthy()
34
+ })
35
+
36
+ it('renders KeySvg inside the symbol dt', () => {
37
+ render(<KeyItem keyDefinition={baseKeyDefinition} mapStyle={mapStyle} />)
38
+ expect(screen.getByTestId('key-svg')).toBeTruthy()
39
+ })
40
+
41
+ it('renders the label dd with the correct class', () => {
42
+ const { container } = render(<KeyItem keyDefinition={baseKeyDefinition} mapStyle={mapStyle} />)
43
+ expect(container.querySelector('.im-c-map-key__item-label')).toBeTruthy()
44
+ })
45
+
46
+ it('renders the label text', () => {
47
+ render(<KeyItem keyDefinition={baseKeyDefinition} mapStyle={mapStyle} />)
48
+ expect(screen.getByText('My Label')).toBeTruthy()
49
+ })
50
+
51
+ it('renders the symbol description when getValueForStyle returns a value', () => {
52
+ getValueForStyle.mockReturnValue('A marker symbol')
53
+ render(<KeyItem keyDefinition={{ ...baseKeyDefinition, symbolDescription: 'A marker symbol' }} mapStyle={mapStyle} />)
54
+ expect(screen.getByText('(A marker symbol)')).toBeTruthy()
55
+ })
56
+
57
+ it('does not render the symbol description span when getValueForStyle returns null', () => {
58
+ render(<KeyItem keyDefinition={baseKeyDefinition} mapStyle={mapStyle} />)
59
+ expect(screen.queryByRole('presentation')).toBeNull()
60
+ expect(document.querySelector('.govuk-visually-hidden')).toBeNull()
61
+ })
62
+
63
+ it('calls getValueForStyle with the symbolDescription and mapStyle id', () => {
64
+ render(<KeyItem keyDefinition={{ ...baseKeyDefinition, symbolDescription: 'desc' }} mapStyle={mapStyle} />)
65
+ expect(getValueForStyle).toHaveBeenCalledWith('desc', 'default')
66
+ })
67
+ })
@@ -0,0 +1,41 @@
1
+ import { KeySvgPattern } from './KeySvgPattern.jsx'
2
+ import { KeySvgSymbol } from './KeySvgSymbol.jsx'
3
+ import { KeySvgLine } from './KeySvgLine.jsx'
4
+ import { KeySvgRect } from './KeySvgRect.jsx'
5
+ import { symbolRegistry } from '../../registry/index.js'
6
+
7
+ // Pure derivation of keyDefinition — computed directly during render (see KeyItem.jsx for why:
8
+ // staging this through useState/useEffect meant every fresh mount painted a blank symbol first).
9
+ const getSymbolShape = (keyDefinition) => {
10
+ if (!keyDefinition) {
11
+ return { symbolShape: null, symbolDef: null }
12
+ }
13
+ const { hasSymbol, hasPattern, style } = keyDefinition
14
+ if (hasSymbol) {
15
+ const symbolDef = symbolRegistry.getSymbolDef(style)
16
+ return { symbolShape: symbolDef ? 'symbol' : 'rect', symbolDef }
17
+ }
18
+ if (hasPattern) {
19
+ return { symbolShape: 'pattern', symbolDef: null }
20
+ }
21
+ if (style.keySymbolShape === 'line') {
22
+ return { symbolShape: 'line', symbolDef: null }
23
+ }
24
+ return { symbolShape: 'rect', symbolDef: null }
25
+ }
26
+
27
+ export const KeySvg = ({ keyDefinition, mapStyle }) => {
28
+ const { symbolShape, symbolDef } = getSymbolShape(keyDefinition)
29
+
30
+ if (!symbolShape) {
31
+ return null
32
+ } else if (symbolShape === 'symbol') {
33
+ return <KeySvgSymbol mapStyle={mapStyle} keyDefinition={keyDefinition} symbolDef={symbolDef} />
34
+ } else if (symbolShape === 'pattern') {
35
+ return <KeySvgPattern mapStyle={mapStyle} keyDefinition={keyDefinition} />
36
+ } else if (symbolShape === 'line') {
37
+ return <KeySvgLine mapStyle={mapStyle} keyDefinition={keyDefinition} />
38
+ } else {
39
+ return <KeySvgRect mapStyle={mapStyle} keyDefinition={keyDefinition} />
40
+ }
41
+ }
@@ -2,7 +2,6 @@ import { render } from '@testing-library/react'
2
2
  import { KeySvg } from './KeySvg'
3
3
 
4
4
  import { symbolRegistry } from '../../../../../src/services/symbolRegistry.js'
5
- import { patternRegistry } from '../../../../../src/services/patternRegistry.js'
6
5
 
7
6
  const getSymbolDef = jest.spyOn(symbolRegistry, 'getSymbolDef')
8
7
 
@@ -22,17 +21,15 @@ jest.mock('./KeySvgRect.jsx', () => ({
22
21
  KeySvgRect: () => <svg data-testid='key-svg-rect' />
23
22
  }))
24
23
 
25
- const baseRegistryDataset = {
24
+ const baseKeyDefinition = {
26
25
  hasSymbol: false,
27
26
  hasPattern: false,
28
27
  style: {}
29
28
  }
30
29
 
31
30
  const baseProps = {
32
- symbolRegistry,
33
- patternRegistry,
34
31
  mapStyle: { id: 'default' },
35
- registryDataset: baseRegistryDataset
32
+ keyDefinition: baseKeyDefinition
36
33
  }
37
34
 
38
35
  beforeEach(() => {
@@ -42,17 +39,17 @@ beforeEach(() => {
42
39
  describe('KeySvg', () => {
43
40
  it('renders KeySvgSymbol when a symbolDef is resolved', () => {
44
41
  getSymbolDef.mockReturnValue({ id: 'marker' })
45
- const { getByTestId } = render(<KeySvg {...baseProps} registryDataset={{ ...baseRegistryDataset, hasSymbol: true }} />)
42
+ const { getByTestId } = render(<KeySvg {...baseProps} keyDefinition={{ ...baseKeyDefinition, hasSymbol: true }} />)
46
43
  expect(getByTestId('key-svg-symbol')).toBeTruthy()
47
44
  })
48
45
 
49
46
  it('renders KeySvgPattern when hasPattern is true and no symbol', () => {
50
- const { getByTestId } = render(<KeySvg {...baseProps} registryDataset={{ ...baseRegistryDataset, hasPattern: true }} />)
47
+ const { getByTestId } = render(<KeySvg {...baseProps} keyDefinition={{ ...baseKeyDefinition, hasPattern: true }} />)
51
48
  expect(getByTestId('key-svg-pattern')).toBeTruthy()
52
49
  })
53
50
 
54
51
  it('renders KeySvgLine when keySymbolShape is line and no symbol or pattern', () => {
55
- const { getByTestId } = render(<KeySvg {...baseProps} registryDataset={{ ...baseRegistryDataset, style: { keySymbolShape: 'line' } }} />)
52
+ const { getByTestId } = render(<KeySvg {...baseProps} keyDefinition={{ ...baseKeyDefinition, style: { keySymbolShape: 'line' } }} />)
56
53
  expect(getByTestId('key-svg-line')).toBeTruthy()
57
54
  })
58
55
 
@@ -63,25 +60,30 @@ describe('KeySvg', () => {
63
60
 
64
61
  it('prefers symbol over pattern when both are present', () => {
65
62
  getSymbolDef.mockReturnValue({ id: 'marker' })
66
- const { getByTestId, queryByTestId } = render(<KeySvg {...baseProps} registryDataset={{ ...baseRegistryDataset, hasSymbol: true, hasPattern: true }} />)
63
+ const { getByTestId, queryByTestId } = render(<KeySvg {...baseProps} keyDefinition={{ ...baseKeyDefinition, hasSymbol: true, hasPattern: true }} />)
67
64
  expect(getByTestId('key-svg-symbol')).toBeTruthy()
68
65
  expect(queryByTestId('key-svg-pattern')).toBeNull()
69
66
  })
70
67
 
71
68
  it('prefers pattern over line when both conditions are met', () => {
72
- const { getByTestId, queryByTestId } = render(<KeySvg {...baseProps} registryDataset={{ ...baseRegistryDataset, hasPattern: true, style: { keySymbolShape: 'line' } }} />)
69
+ const { getByTestId, queryByTestId } = render(<KeySvg {...baseProps} keyDefinition={{ ...baseKeyDefinition, hasPattern: true, style: { keySymbolShape: 'line' } }} />)
73
70
  expect(getByTestId('key-svg-pattern')).toBeTruthy()
74
71
  expect(queryByTestId('key-svg-line')).toBeNull()
75
72
  })
76
73
 
77
74
  it('renders KeySvgRect when keySymbolShape is not line and no symbol or pattern', () => {
78
- const { getByTestId } = render(<KeySvg {...baseProps} registryDataset={{ ...baseRegistryDataset, style: { keySymbolShape: 'polygon' } }} />)
75
+ const { getByTestId } = render(<KeySvg {...baseProps} keyDefinition={{ ...baseKeyDefinition, style: { keySymbolShape: 'polygon' } }} />)
79
76
  expect(getByTestId('key-svg-rect')).toBeTruthy()
80
77
  })
81
78
 
82
79
  it('does not render KeySvgSymbol when hasSymbol is true but getSymbolDef returns null', () => {
83
80
  getSymbolDef.mockReturnValue(null)
84
- const { getByTestId } = render(<KeySvg {...baseProps} registryDataset={{ ...baseRegistryDataset, hasSymbol: true }} />)
81
+ const { getByTestId } = render(<KeySvg {...baseProps} keyDefinition={{ ...baseKeyDefinition, hasSymbol: true }} />)
85
82
  expect(getByTestId('key-svg-rect')).toBeTruthy()
86
83
  })
84
+
85
+ it('should return null if keyDefinition is undefined', () => {
86
+ const { container } = render(<KeySvg {...baseProps} keyDefinition={null} />)
87
+ expect(container.firstChild).toBeNull()
88
+ })
87
89
  })
@@ -0,0 +1,22 @@
1
+ import { svgProps, SVG_SIZE, SVG_CENTER } from './svgProperties.js'
2
+ import { getValueForStyle } from '../../../../../src/utils/getValueForStyle.js'
3
+
4
+ export const KeySvgLine = ({ mapStyle, keyDefinition }) => {
5
+ const { style } = keyDefinition
6
+ const strokeWidth = style.strokeWidth
7
+ const stroke = getValueForStyle(style.stroke, mapStyle.id)
8
+
9
+ return (
10
+ <svg {...svgProps}>
11
+ <line
12
+ x1={strokeWidth / 2}
13
+ y1={SVG_CENTER}
14
+ x2={SVG_SIZE - strokeWidth / 2}
15
+ y2={SVG_CENTER}
16
+ stroke={stroke}
17
+ strokeWidth={strokeWidth}
18
+ strokeLinecap='round'
19
+ />
20
+ </svg>
21
+ )
22
+ }
@@ -8,7 +8,7 @@ jest.mock('../../../../../src/utils/getValueForStyle', () => ({
8
8
  }))
9
9
 
10
10
  const defaultProps = {
11
- registryDataset: {
11
+ keyDefinition: {
12
12
  style: {
13
13
  stroke: '#ff0000',
14
14
  strokeWidth: 2
@@ -1,9 +1,14 @@
1
1
  import { svgProps } from './svgProperties.js'
2
+ import { patternRegistry } from '../../registry/index.js'
2
3
  const PATTERN_INSET = 2
3
4
 
4
- export const KeySvgPattern = ({ patternRegistry, registryDataset, mapStyle }) => {
5
- const { style } = registryDataset
5
+ export const KeySvgPattern = ({ keyDefinition, mapStyle }) => {
6
+ const { style } = keyDefinition
6
7
  const paths = patternRegistry.getKeyPatternPaths(style, mapStyle.id)
8
+
9
+ if (!paths) {
10
+ return null
11
+ }
7
12
  return (
8
13
  <svg {...svgProps}>
9
14
  <g dangerouslySetInnerHTML={{ __html: paths.border }} />
@@ -4,7 +4,7 @@ import { patternRegistry } from '../../../../../src/services/patternRegistry.js'
4
4
 
5
5
  const getKeyPatternPaths = jest.spyOn(patternRegistry, 'getKeyPatternPaths')
6
6
  const defaultProps = {
7
- registryDataset: {
7
+ keyDefinition: {
8
8
  style: {
9
9
  fillPattern: 'dots'
10
10
  }
@@ -25,7 +25,7 @@ describe('KeySvgPattern', () => {
25
25
 
26
26
  it('calls getKeyPatternPaths with props, the mapStyle id, and the patternRegistry', () => {
27
27
  render(<KeySvgPattern {...defaultProps} />)
28
- expect(getKeyPatternPaths).toHaveBeenCalledWith(defaultProps.registryDataset.style, 'default')
28
+ expect(getKeyPatternPaths).toHaveBeenCalledWith(defaultProps.keyDefinition.style, 'default')
29
29
  })
30
30
 
31
31
  it('renders two g elements for border and content', () => {
@@ -52,4 +52,10 @@ describe('KeySvgPattern', () => {
52
52
  const { container } = render(<KeySvgPattern {...defaultProps} />)
53
53
  expect(container.querySelector('#content-el')).toBeTruthy()
54
54
  })
55
+
56
+ it('renders nothing when getKeyPatternPaths returns falsy', () => {
57
+ getKeyPatternPaths.mockReturnValue(null)
58
+ const { container } = render(<KeySvgPattern {...defaultProps} />)
59
+ expect(container.firstChild).toBeNull()
60
+ })
55
61
  })
@@ -0,0 +1,26 @@
1
+ import { getValueForStyle } from '../../../../../src/utils/getValueForStyle.js'
2
+ import { svgProps, SVG_SIZE } from './svgProperties.js'
3
+
4
+ export const KeySvgRect = ({ mapStyle, keyDefinition }) => {
5
+ const { style } = keyDefinition
6
+ const strokeWidth = style.strokeWidth
7
+ const fill = getValueForStyle(style.fill, mapStyle.id)
8
+ const stroke = getValueForStyle(style.stroke, mapStyle.id)
9
+
10
+ return (
11
+ <svg {...svgProps}>
12
+ <rect
13
+ x={strokeWidth / 2}
14
+ y={strokeWidth / 2}
15
+ width={SVG_SIZE - strokeWidth}
16
+ height={SVG_SIZE - strokeWidth}
17
+ rx={strokeWidth}
18
+ ry={strokeWidth}
19
+ fill={fill}
20
+ stroke={stroke}
21
+ strokeWidth={strokeWidth}
22
+ strokeLinejoin='round'
23
+ />
24
+ </svg>
25
+ )
26
+ }
@@ -8,7 +8,7 @@ jest.mock('../../../../../src/utils/getValueForStyle', () => ({
8
8
  }))
9
9
 
10
10
  const defaultProps = {
11
- registryDataset: {
11
+ keyDefinition: {
12
12
  style: {
13
13
  fill: '#0000ff',
14
14
  stroke: '#ff0000',
@@ -1,13 +1,18 @@
1
1
  import { getSymbolStyleColors, getSymbolViewBox } from '../../../../../src/utils/symbolUtils.js'
2
2
  import { svgSymbolProps } from './svgProperties.js'
3
+ import { symbolRegistry } from '../../registry/index.js'
3
4
 
4
- export const KeySvgSymbol = ({ symbolRegistry, registryDataset, mapStyle, symbolDef }) => {
5
- const { style } = registryDataset
5
+ export const KeySvgSymbol = ({ keyDefinition, mapStyle, symbolDef }) => {
6
+ const { style } = keyDefinition
6
7
  const mapColorScheme = mapStyle?.appColorScheme ?? 'light'
7
8
  const keyMapStyle = { ...mapStyle, mapColorScheme }
8
-
9
9
  const resolvedSvg = symbolRegistry.resolve(symbolDef, getSymbolStyleColors(style), keyMapStyle)
10
10
  const viewBox = getSymbolViewBox(style, symbolDef)
11
+
12
+ if (!(resolvedSvg && viewBox)) {
13
+ return null
14
+ }
15
+
11
16
  return (
12
17
  <svg {...svgSymbolProps} viewBox={viewBox}>
13
18
  <g dangerouslySetInnerHTML={{ __html: resolvedSvg }} />
@@ -1,23 +1,22 @@
1
1
  import { render } from '@testing-library/react'
2
2
  import { KeySvgSymbol } from './KeySvgSymbol'
3
-
4
3
  import { getSymbolStyleColors, getSymbolViewBox } from '../../../../../src/utils/symbolUtils.js'
4
+ import { symbolRegistry } from '../../registry/index.js'
5
5
 
6
6
  jest.mock('../../../../../src/utils/symbolUtils.js', () => ({
7
7
  getSymbolStyleColors: jest.fn(() => ({ foreground: '#000', background: '#fff' })),
8
8
  getSymbolViewBox: jest.fn(() => '0 0 38 38')
9
9
  }))
10
10
 
11
- const mockResolve = jest.fn(() => '<path d="M0 0"/>')
11
+ const mockResolve = jest.spyOn(symbolRegistry, 'resolve')
12
12
 
13
13
  const defaultProps = {
14
14
  symbolDef: { id: 'marker' },
15
- registryDataset: {
15
+ keyDefinition: {
16
16
  style: {
17
17
  stroke: '#000000'
18
18
  }
19
19
  },
20
- symbolRegistry: { resolve: mockResolve },
21
20
  mapStyle: { id: 'default' }
22
21
  }
23
22
 
@@ -45,12 +44,12 @@ describe('KeySvgSymbol', () => {
45
44
 
46
45
  it('calls getSymbolStyleColors with the dataset props', () => {
47
46
  render(<KeySvgSymbol {...defaultProps} />)
48
- expect(getSymbolStyleColors).toHaveBeenCalledWith(defaultProps.registryDataset.style)
47
+ expect(getSymbolStyleColors).toHaveBeenCalledWith(defaultProps.keyDefinition.style)
49
48
  })
50
49
 
51
50
  it('calls getSymbolViewBox with the dataset props and symbolDef', () => {
52
51
  render(<KeySvgSymbol {...defaultProps} />)
53
- expect(getSymbolViewBox).toHaveBeenCalledWith(defaultProps.registryDataset.style, defaultProps.symbolDef)
52
+ expect(getSymbolViewBox).toHaveBeenCalledWith(defaultProps.keyDefinition.style, defaultProps.symbolDef)
54
53
  })
55
54
 
56
55
  it('sets the viewBox from getSymbolViewBox', () => {
@@ -77,4 +76,16 @@ describe('KeySvgSymbol', () => {
77
76
  const callArg = mockResolve.mock.calls[0][2]
78
77
  expect(callArg.mapColorScheme).toBe('dark')
79
78
  })
79
+
80
+ it('renders nothing when resolve returns falsy', () => {
81
+ mockResolve.mockReturnValue(null)
82
+ const { container } = render(<KeySvgSymbol {...defaultProps} />)
83
+ expect(container.firstChild).toBeNull()
84
+ })
85
+
86
+ it('renders nothing when getSymbolViewBox returns falsy', () => {
87
+ getSymbolViewBox.mockReturnValue(null)
88
+ const { container } = render(<KeySvgSymbol {...defaultProps} />)
89
+ expect(container.firstChild).toBeNull()
90
+ })
80
91
  })