@defra/interactive-map 0.0.30-alpha → 0.0.32-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 (247) hide show
  1. package/README.md +2 -2
  2. package/assets/images/bright-ofm-thumb.jpg +0 -0
  3. package/assets/images/dark-ofm-thumb.jpg +0 -0
  4. package/assets/images/road-ofm-thumb.jpg +0 -0
  5. package/assets/templates/add-polygons.njk +143 -0
  6. package/assets/templates/add-symbols.njk +119 -0
  7. package/assets/templates/{map.njk → basic-map.njk} +9 -17
  8. package/assets/templates/button-map.njk +56 -0
  9. package/assets/templates/draw-tools.njk +161 -0
  10. package/assets/templates/marker-panel.njk +91 -0
  11. package/assets/templates/place-marker.njk +104 -0
  12. package/assets/templates/search-control.njk +98 -0
  13. package/assets/templates/select-feature.njk +101 -0
  14. package/assets/templates/style-switcher.njk +87 -0
  15. package/assets/templates/toggle-marker-label.njk +74 -0
  16. package/dist/css/index.css +1 -1
  17. package/dist/esm/im-core.js +1 -1
  18. package/dist/esm/im-shell.js +1 -1
  19. package/dist/umd/im-core.js +1 -1
  20. package/dist/umd/index.js +1 -1
  21. package/docs/api/context.md +4 -4
  22. package/docs/api.md +166 -37
  23. package/docs/assets/css/docusaurus.scss +36 -4
  24. package/docs/assets/images/add-polygons.jpg +0 -0
  25. package/docs/assets/images/add-symbols.jpg +0 -0
  26. package/docs/assets/images/basic-map.jpg +0 -0
  27. package/docs/assets/images/button-first.jpg +0 -0
  28. package/docs/assets/images/deuteranopia-ozs-map-thumb.jpg +0 -0
  29. package/docs/assets/images/draw.jpg +0 -0
  30. package/docs/assets/images/marker-panel.jpg +0 -0
  31. package/docs/assets/images/night-ozs-map-thumb.jpg +0 -0
  32. package/docs/assets/images/outdoor-ozs-map-thumb.jpg +0 -0
  33. package/docs/assets/images/place-marker.jpg +0 -0
  34. package/docs/assets/images/screenshot.jpg +0 -0
  35. package/docs/assets/images/search.jpg +0 -0
  36. package/docs/assets/images/select-feature.jpg +0 -0
  37. package/docs/assets/images/style-switcher.jpg +0 -0
  38. package/docs/assets/images/toggle-marker-label.jpg +0 -0
  39. package/docs/assets/images/tritanopia-ozs-map-thumb.jpg +0 -0
  40. package/docs/examples/add-marker-with-panel.mdx +3 -3
  41. package/docs/examples/add-polygons.mdx +225 -0
  42. package/docs/examples/add-symbols.mdx +177 -0
  43. package/docs/examples/basic-map.mdx +2 -2
  44. package/docs/examples/draw-tools.mdx +271 -0
  45. package/docs/examples/index.mdx +95 -11
  46. package/docs/examples/place-marker.mdx +151 -0
  47. package/docs/examples/search-control.mdx +71 -0
  48. package/docs/examples/select-feature.mdx +146 -0
  49. package/docs/examples/style-switcher.mdx +135 -0
  50. package/docs/examples/toggle-marker-label.mdx +3 -3
  51. package/docs/plugins/datasets.md +157 -122
  52. package/docs/plugins/interact.md +1 -4
  53. package/docusaurus.config.cjs +9 -2
  54. package/govuk-prototype-kit.config.json +52 -2
  55. package/package.json +1 -1
  56. package/plugins/beta/datasets/dist/css/index.css +5 -2
  57. package/plugins/beta/datasets/dist/esm/im-datasets-ml-adapter.js +1 -0
  58. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  59. package/plugins/beta/datasets/dist/esm/index.js +1 -1
  60. package/plugins/beta/datasets/dist/umd/im-datasets-ml-adapter.js +2 -0
  61. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
  62. package/plugins/beta/datasets/dist/umd/index.js +2 -0
  63. package/plugins/beta/datasets/src/adapters/loadLayerAdapter.js +33 -0
  64. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +44 -120
  65. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +145 -245
  66. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +516 -0
  67. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +209 -0
  68. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.test.js +483 -0
  69. package/plugins/beta/datasets/src/api/addDataset.js +2 -5
  70. package/plugins/beta/datasets/src/api/addDataset.test.js +16 -0
  71. package/plugins/beta/datasets/src/api/getOpacity.js +14 -14
  72. package/plugins/beta/datasets/src/api/getOpacity.test.js +72 -0
  73. package/plugins/beta/datasets/src/api/getStyle.js +8 -9
  74. package/plugins/beta/datasets/src/api/getStyle.test.js +42 -0
  75. package/plugins/beta/datasets/src/api/removeDataset.js +7 -7
  76. package/plugins/beta/datasets/src/api/removeDataset.test.js +45 -0
  77. package/plugins/beta/datasets/src/api/setData.js +9 -3
  78. package/plugins/beta/datasets/src/api/setData.test.js +62 -0
  79. package/plugins/beta/datasets/src/api/setDatasetVisibility.js +5 -30
  80. package/plugins/beta/datasets/src/api/setDatasetVisibility.test.js +31 -0
  81. package/plugins/beta/datasets/src/api/setFeatureVisibility.js +9 -18
  82. package/plugins/beta/datasets/src/api/setFeatureVisibility.test.js +74 -0
  83. package/plugins/beta/datasets/src/api/setGlobals.js +20 -0
  84. package/plugins/beta/datasets/src/api/setGlobals.test.js +63 -0
  85. package/plugins/beta/datasets/src/api/setOpacity.js +8 -27
  86. package/plugins/beta/datasets/src/api/setOpacity.test.js +31 -0
  87. package/plugins/beta/datasets/src/api/setStyle.js +5 -21
  88. package/plugins/beta/datasets/src/api/setStyle.test.js +53 -0
  89. package/plugins/beta/datasets/src/components/Key/Key.jsx +51 -0
  90. package/plugins/beta/datasets/src/components/Key/KeyGroupItem.jsx +18 -0
  91. package/plugins/beta/datasets/src/components/Key/KeyItem.jsx +20 -0
  92. package/plugins/beta/datasets/src/components/Key/KeySvg.jsx +22 -0
  93. package/plugins/beta/datasets/src/components/Key/KeySvg.test.jsx +87 -0
  94. package/plugins/beta/datasets/src/components/Key/KeySvgLine.jsx +19 -0
  95. package/plugins/beta/datasets/src/components/Key/KeySvgLine.test.jsx +71 -0
  96. package/plugins/beta/datasets/src/components/{KeySvgPattern.jsx → Key/KeySvgPattern.jsx} +3 -4
  97. package/plugins/beta/datasets/src/components/Key/KeySvgPattern.test.jsx +55 -0
  98. package/plugins/beta/datasets/src/components/Key/KeySvgRect.jsx +22 -0
  99. package/plugins/beta/datasets/src/components/Key/KeySvgRect.test.jsx +85 -0
  100. package/plugins/beta/datasets/src/components/{KeySvgSymbol.jsx → Key/KeySvgSymbol.jsx} +5 -5
  101. package/plugins/beta/datasets/src/components/Key/KeySvgSymbol.test.jsx +80 -0
  102. package/plugins/beta/datasets/src/components/Key/svgProperties.test.js +61 -0
  103. package/plugins/beta/datasets/src/{panels → components/LayersMenu}/Layers.module.scss +15 -11
  104. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenu.jsx +33 -0
  105. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuCheckbox.jsx +31 -0
  106. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuGroupWrapper.jsx +19 -0
  107. package/plugins/beta/datasets/src/datasets.scss +2 -2
  108. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +8 -8
  109. package/plugins/beta/datasets/src/{DatasetsInit.jsx → initialise/DatasetsInit.jsx} +19 -21
  110. package/plugins/beta/datasets/src/{defaults.js → initialise/defaults.js} +17 -2
  111. package/plugins/beta/datasets/src/initialise/defaults.test.js +127 -0
  112. package/plugins/beta/datasets/src/{datasets.js → initialise/initialiseDatasets.js} +30 -26
  113. package/plugins/beta/datasets/src/initialise/initialiseDatasets.test.js +273 -0
  114. package/plugins/beta/datasets/src/manifest.js +8 -6
  115. package/plugins/beta/datasets/src/reducers/__data__/demoDatasets.js +253 -0
  116. package/plugins/beta/datasets/src/reducers/datasetsToMenu.js +83 -0
  117. package/plugins/beta/datasets/src/reducers/datasetsToMenu.test.js +84 -0
  118. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.js +41 -0
  119. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.test.js +117 -0
  120. package/plugins/beta/datasets/src/reducers/pluginState.js +206 -0
  121. package/plugins/beta/datasets/src/reducers/pluginState.test.js +386 -0
  122. package/plugins/beta/datasets/src/registry/__mocks__/datasetRegistry.js +29 -0
  123. package/plugins/beta/datasets/src/registry/createDataset.js +5 -0
  124. package/plugins/beta/datasets/src/registry/dataset.js +176 -0
  125. package/plugins/beta/datasets/src/registry/dataset.test.js +497 -0
  126. package/plugins/beta/datasets/src/registry/datasetDefinitionCache.js +35 -0
  127. package/plugins/beta/datasets/src/registry/datasetRegistry.js +116 -0
  128. package/plugins/beta/datasets/src/registry/datasetRegistry.test.js +335 -0
  129. package/plugins/beta/datasets/src/registry/dynamicGeoJson.js +17 -0
  130. package/plugins/beta/datasets/src/registry/globalDataset.js +36 -0
  131. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  132. package/plugins/beta/draw-es/src/api/addFeature.js +12 -3
  133. package/plugins/beta/draw-es/src/events.js +8 -3
  134. package/plugins/beta/draw-es/src/events.test.js +2 -0
  135. package/plugins/beta/draw-es/src/graphic.js +26 -2
  136. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  137. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  138. package/plugins/beta/draw-ml/src/DrawInit.jsx +8 -0
  139. package/plugins/beta/draw-ml/src/api/newLine.js +1 -0
  140. package/plugins/beta/draw-ml/src/api/newPolygon.js +1 -0
  141. package/plugins/beta/draw-ml/src/events.js +13 -1
  142. package/plugins/beta/draw-ml/src/index.js +1 -1
  143. package/plugins/beta/draw-ml/src/manifest.js +3 -0
  144. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +21 -12
  145. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +4 -0
  146. package/plugins/beta/draw-ol/dist/esm/EditMode.js +1 -1
  147. package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
  148. package/plugins/beta/draw-ol/src/edit/keyboardHandler.js +4 -2
  149. package/plugins/beta/map-styles/src/mapStyles.scss +41 -41
  150. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  151. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  152. package/plugins/interact/dist/umd/index.js +1 -1
  153. package/plugins/interact/src/InteractInit.jsx +3 -5
  154. package/plugins/interact/src/InteractInit.test.js +1 -3
  155. package/plugins/interact/src/events.js +2 -29
  156. package/plugins/interact/src/events.test.js +4 -102
  157. package/plugins/interact/src/hooks/useAttachEvents.js +3 -4
  158. package/plugins/interact/src/hooks/useAttachEvents.test.js +1 -3
  159. package/plugins/interact/src/hooks/useCrossHairVisibility.js +4 -10
  160. package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +5 -10
  161. package/plugins/interact/src/hooks/useHighlightSync.js +1 -10
  162. package/plugins/interact/src/hooks/useHighlightSync.test.js +2 -21
  163. package/plugins/interact/src/hooks/useInteractionHandlers.js +15 -12
  164. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +28 -29
  165. package/plugins/interact/src/manifest.js +0 -35
  166. package/plugins/interact/src/manifest.test.js +3 -43
  167. package/plugins/interact/src/reducer.js +4 -22
  168. package/plugins/interact/src/reducer.test.js +11 -39
  169. package/plugins/search/dist/css/index.css +5 -2
  170. package/plugins/search/src/components/Form/Form.module.scss +5 -2
  171. package/plugins/search/src/search.scss +4 -4
  172. package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
  173. package/providers/beta/esri/src/esriProvider.js +4 -0
  174. package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
  175. package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
  176. package/providers/beta/openlayers/src/openlayersProvider.js +4 -0
  177. package/providers/beta/openlayers/src/openlayersProvider.test.js +5 -0
  178. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  179. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  180. package/providers/maplibre/src/maplibreProvider.js +6 -4
  181. package/providers/maplibre/src/maplibreProvider.test.js +5 -0
  182. package/providers/maplibre/src/utils/highlightFeatures.js +74 -25
  183. package/providers/maplibre/src/utils/highlightFeatures.test.js +132 -0
  184. package/providers/maplibre/src/utils/patternImages.js +2 -4
  185. package/providers/maplibre/src/utils/symbolImages.js +1 -4
  186. package/rollup.esm.mjs +2 -8
  187. package/src/App/components/CrossHair/CrossHair.module.scss +8 -8
  188. package/src/App/components/MapButton/MapButton.jsx +16 -1
  189. package/src/App/components/MapButton/MapButton.module.scss +12 -1
  190. package/src/App/components/MapButton/MapButton.test.jsx +40 -1
  191. package/src/App/hooks/useContinueEnabledEvaluator.js +31 -0
  192. package/src/App/hooks/useContinueEnabledEvaluator.test.js +118 -0
  193. package/src/App/hooks/useEvaluateProp.js +15 -15
  194. package/src/App/hooks/useEvaluateProp.test.js +80 -76
  195. package/src/App/hooks/useLayoutMeasurements.js +11 -5
  196. package/src/App/hooks/useResizeObserver.js +15 -2
  197. package/src/App/hooks/useResizeObserver.test.js +1 -0
  198. package/src/App/renderer/PluginInits.jsx +2 -0
  199. package/src/App/renderer/mapButtons.js +21 -1
  200. package/src/App/renderer/mapButtons.test.js +46 -1
  201. package/src/App/store/appReducer.js +1 -1
  202. package/src/App/store/appReducer.test.js +37 -0
  203. package/src/InteractiveMap/InteractiveMap.js +14 -4
  204. package/src/InteractiveMap/InteractiveMap.test.js +11 -3
  205. package/src/InteractiveMap/behaviourController.js +3 -1
  206. package/src/InteractiveMap/behaviourController.test.js +6 -3
  207. package/src/InteractiveMap/domStateManager.js +2 -0
  208. package/src/config/appConfig.js +25 -0
  209. package/src/config/appConfig.test.js +50 -0
  210. package/src/config/defaults.js +1 -0
  211. package/src/config/events.js +21 -3
  212. package/src/scss/settings/_dimensions.scss +1 -0
  213. package/src/services/patternRegistry.js +8 -4
  214. package/src/types.js +7 -0
  215. package/src/utils/patternUtils.js +1 -2
  216. package/src/utils/patternUtils.test.js +9 -0
  217. package/webpack.dev.mjs +10 -0
  218. package/webpack.umd.mjs +1 -1
  219. package/docs/assets/images/screens-blue.jpg +0 -0
  220. package/docs/assets/images/screens-white.jpg +0 -0
  221. package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +0 -1
  222. package/plugins/beta/datasets/dist/adapters/maplibre/esm/index.js +0 -1
  223. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +0 -2
  224. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +0 -2
  225. package/plugins/beta/datasets/dist/umd/maplibre/index.js +0 -2
  226. package/plugins/beta/datasets/src/adapters/maplibre/index.js +0 -18
  227. package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +0 -75
  228. package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +0 -27
  229. package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +0 -31
  230. package/plugins/beta/datasets/src/components/KeySvg.jsx +0 -24
  231. package/plugins/beta/datasets/src/components/KeySvgLine.jsx +0 -19
  232. package/plugins/beta/datasets/src/components/KeySvgRect.jsx +0 -22
  233. package/plugins/beta/datasets/src/index.maplibre.umd.js +0 -23
  234. package/plugins/beta/datasets/src/panels/Key.jsx +0 -62
  235. package/plugins/beta/datasets/src/panels/Layers.jsx +0 -141
  236. package/plugins/beta/datasets/src/reducer.js +0 -219
  237. package/plugins/beta/datasets/src/reducers/keyReducer.js +0 -34
  238. package/plugins/beta/datasets/src/utils/filters.js +0 -34
  239. package/plugins/beta/datasets/src/utils/mergeSublayer.js +0 -86
  240. package/plugins/beta/draw-ml/dist/css/index.css +0 -21
  241. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-maplibre-adapter.js.LICENSE.txt → im-datasets-ml-adapter.js.LICENSE.txt} +0 -0
  242. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-plugin.js.LICENSE.txt → im-datasets-plugin.js.LICENSE.txt} +0 -0
  243. /package/plugins/beta/datasets/dist/umd/{maplibre/index.js.LICENSE.txt → index.js.LICENSE.txt} +0 -0
  244. /package/plugins/beta/datasets/src/components/{EmptyKey.jsx → Key/EmptyKey.jsx} +0 -0
  245. /package/plugins/beta/datasets/src/components/{EmptyKey.test.jsx → Key/EmptyKey.test.jsx} +0 -0
  246. /package/plugins/beta/datasets/src/{panels → components/Key}/Key.module.scss +0 -0
  247. /package/plugins/beta/datasets/src/components/{svgProperties.js → Key/svgProperties.js} +0 -0
@@ -0,0 +1,42 @@
1
+ import { getStyle } from './getStyle.js'
2
+ import { datasetRegistry } from '../registry/datasetRegistry.js'
3
+ import { logger } from '../../../../../src/services/logger.js'
4
+
5
+ jest.mock('../registry/datasetRegistry.js', () => ({
6
+ datasetRegistry: { getDataset: jest.fn() }
7
+ }))
8
+
9
+ jest.mock('../../../../../src/services/logger.js', () => ({
10
+ logger: { warn: jest.fn() }
11
+ }))
12
+
13
+ describe('getStyle', () => {
14
+ beforeEach(() => {
15
+ jest.clearAllMocks()
16
+ })
17
+
18
+ it('returns the style for a known dataset', () => {
19
+ const style = { fill: 'blue', stroke: 'red' }
20
+ datasetRegistry.getDataset.mockReturnValue({ style })
21
+ expect(getStyle({}, { datasetId: 'roads' })).toBe(style)
22
+ })
23
+
24
+ it('returns style for the combined sublayer id', () => {
25
+ const style = { fill: 'green' }
26
+ datasetRegistry.getDataset.mockReturnValue({ style })
27
+ expect(getStyle({}, { datasetId: 'parent', sublayerId: 'child' })).toBe(style)
28
+ expect(datasetRegistry.getDataset).toHaveBeenCalledWith('parent-child')
29
+ })
30
+
31
+ it('returns null and warns when a sublayer dataset is not found', () => {
32
+ datasetRegistry.getDataset.mockReturnValue(undefined)
33
+ expect(getStyle({}, { datasetId: 'parent', sublayerId: 'missing' })).toBeNull()
34
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('parent-missing'))
35
+ })
36
+
37
+ it('uses undefined datasetId when options argument is omitted', () => {
38
+ datasetRegistry.getDataset.mockReturnValue(undefined)
39
+ expect(getStyle({})).toBeNull()
40
+ expect(logger.warn).toHaveBeenCalledWith('getStyle: Dataset with id undefined not found')
41
+ })
42
+ })
@@ -1,9 +1,9 @@
1
- export const removeDataset = ({ pluginState }, datasetId) => {
2
- const dataset = pluginState.datasets?.find(d => d.id === datasetId)
3
- if (!dataset) {
4
- return
5
- }
1
+ import { layerAdapter } from '../adapters/loadLayerAdapter.js'
6
2
 
7
- pluginState.layerAdapter?.removeDataset(dataset, pluginState.datasets)
8
- pluginState.dispatch({ type: 'REMOVE_DATASET', payload: { id: datasetId } })
3
+ export const removeDataset = ({ pluginState: { dispatch } }, datasetId) => {
4
+ // Here we need to remove the dataset from the adapter before removing it from state,
5
+ // otherwise the datasetDefinition will have been removed from the registry by the
6
+ // time the adapter tries to remove the dataset, which causes an error
7
+ layerAdapter.removeDataset(datasetId)
8
+ dispatch({ type: 'REMOVE_DATASET', payload: { id: datasetId } })
9
9
  }
@@ -0,0 +1,45 @@
1
+ import { removeDataset } from './removeDataset.js'
2
+ import { layerAdapter } from '../adapters/loadLayerAdapter.js'
3
+
4
+ jest.mock('../adapters/loadLayerAdapter.js', () => ({
5
+ layerAdapter: {
6
+ removeDataset: jest.fn()
7
+ }
8
+ }))
9
+
10
+ describe('removeDataset', () => {
11
+ const dispatch = jest.fn()
12
+ it('calls layerAdapter.removeDataset with the datasetId', () => {
13
+ removeDataset({ pluginState: { layerAdapter, dispatch } }, 'my-dataset')
14
+
15
+ expect(layerAdapter.removeDataset).toHaveBeenCalledWith('my-dataset')
16
+ })
17
+
18
+ it('dispatches REMOVE_DATASET with the datasetId', () => {
19
+ removeDataset({ pluginState: { layerAdapter, dispatch } }, 'my-dataset')
20
+
21
+ expect(dispatch).toHaveBeenCalledWith({
22
+ type: 'REMOVE_DATASET',
23
+ payload: { id: 'my-dataset' }
24
+ })
25
+ })
26
+
27
+ it('calls layerAdapter.removeDataset before dispatching', () => {
28
+ const callOrder = []
29
+ dispatch.mockImplementationOnce(() => callOrder.push('dispatch'))
30
+ layerAdapter.removeDataset.mockImplementationOnce(() => callOrder.push('adapter'))
31
+
32
+ removeDataset({ pluginState: { layerAdapter, dispatch } }, 'my-dataset')
33
+
34
+ expect(callOrder).toEqual(['adapter', 'dispatch'])
35
+ })
36
+
37
+ it('still dispatches REMOVE_DATASET when layerAdapter is undefined', () => {
38
+ removeDataset({ pluginState: { layerAdapter: undefined, dispatch } }, 'my-dataset')
39
+
40
+ expect(dispatch).toHaveBeenCalledWith({
41
+ type: 'REMOVE_DATASET',
42
+ payload: { id: 'my-dataset' }
43
+ })
44
+ })
45
+ })
@@ -1,10 +1,16 @@
1
1
  import { logger } from '../../../../../src/services/logger.js'
2
+ import { datasetRegistry } from '../registry/datasetRegistry.js'
3
+ import { layerAdapter } from '../adapters/loadLayerAdapter.js'
2
4
 
3
5
  export const setData = ({ pluginState }, geojson, { datasetId }) => {
4
- const dataset = pluginState.datasets?.find(d => d.id === datasetId)
5
- if (dataset?.tiles) {
6
+ const registryDataset = datasetRegistry.getDataset(datasetId)
7
+ if (!registryDataset) {
8
+ logger.warn(`setData: Dataset with id ${datasetId} not found`)
9
+ return
10
+ }
11
+ if (registryDataset?.tiles) {
6
12
  logger.warn(`setData called on vector tile dataset "${datasetId}" — has no effect`)
7
13
  return
8
14
  }
9
- pluginState.layerAdapter?.setData(datasetId, geojson)
15
+ layerAdapter.setData(datasetId, geojson)
10
16
  }
@@ -0,0 +1,62 @@
1
+ import { setData } from './setData.js'
2
+ import { datasetRegistry } from '../registry/datasetRegistry.js'
3
+ import { logger } from '../../../../../src/services/logger.js'
4
+ import { layerAdapter } from '../adapters/loadLayerAdapter.js'
5
+
6
+ jest.mock('../adapters/loadLayerAdapter.js', () => ({
7
+ layerAdapter: {
8
+ setData: jest.fn()
9
+ }
10
+ }))
11
+
12
+ jest.mock('../registry/datasetRegistry.js', () => ({
13
+ datasetRegistry: { getDataset: jest.fn() }
14
+ }))
15
+
16
+ jest.mock('../../../../../src/services/logger.js', () => ({
17
+ logger: { warn: jest.fn() }
18
+ }))
19
+
20
+ describe('setData', () => {
21
+ const geojson = { type: 'FeatureCollection', features: [] }
22
+ beforeEach(() => {
23
+ jest.clearAllMocks()
24
+ })
25
+
26
+ it('calls layerAdapter.setData with datasetId and geojson', () => {
27
+ datasetRegistry.getDataset.mockReturnValue({ id: 'my-dataset' })
28
+
29
+ setData({ pluginState: { layerAdapter } }, geojson, { datasetId: 'my-dataset' })
30
+
31
+ expect(layerAdapter.setData).toHaveBeenCalledWith('my-dataset', geojson)
32
+ })
33
+
34
+ it('does nothing when the dataset is not found in the registry', () => {
35
+ datasetRegistry.getDataset.mockReturnValue(undefined)
36
+
37
+ setData({ pluginState: { layerAdapter } }, geojson, { datasetId: 'missing' })
38
+
39
+ expect(layerAdapter.setData).not.toHaveBeenCalled()
40
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('missing'))
41
+ })
42
+
43
+ it('warns and does nothing when dataset is a vector tile dataset', () => {
44
+ datasetRegistry.getDataset.mockReturnValue({
45
+ id: 'tiles-dataset',
46
+ tiles: ['https://example.com/{z}/{x}/{y}']
47
+ })
48
+
49
+ setData({ pluginState: { layerAdapter } }, geojson, { datasetId: 'tiles-dataset' })
50
+
51
+ expect(layerAdapter.setData).not.toHaveBeenCalled()
52
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('tiles-dataset'))
53
+ })
54
+
55
+ it("doesn't throw when layerAdapter is undefined", () => {
56
+ datasetRegistry.getDataset.mockReturnValue({ id: 'my-dataset' })
57
+ // Should not throw even if layerAdapter is undefined
58
+ expect(() =>
59
+ setData({ pluginState: { layerAdapter: undefined } }, geojson, { datasetId: 'my-dataset' })
60
+ ).not.toThrow()
61
+ })
62
+ })
@@ -1,37 +1,12 @@
1
- export const setDatasetVisibility = ({ pluginState }, visible, options = {}) => {
2
- const { datasetId, sublayerId } = options
3
-
4
- if (sublayerId) {
5
- const visibility = visible ? 'visible' : 'hidden'
6
- pluginState.layerAdapter?.[visible ? 'showSublayer' : 'hideSublayer'](datasetId, sublayerId)
7
- pluginState.dispatch({ type: 'SET_SUBLAYER_VISIBILITY', payload: { datasetId, sublayerId, visibility } })
8
- return
9
- }
1
+ export const setDatasetVisibility = ({ pluginState: { dispatch } }, visible, ids = {}) => {
2
+ const { datasetId: datasetToUpdate, sublayerId } = ids
3
+ const datasetId = sublayerId ? `${datasetToUpdate}-${sublayerId}` : datasetToUpdate
10
4
 
11
5
  if (datasetId) {
12
- pluginState.layerAdapter?.[visible ? 'showDataset' : 'hideDataset'](datasetId)
13
- pluginState.dispatch({ type: 'SET_DATASET_VISIBILITY', payload: { id: datasetId, visibility: visible ? 'visible' : 'hidden' } })
14
- if (visible) {
15
- const dataset = pluginState.datasets?.find(d => d.id === datasetId)
16
- Object.entries(dataset?.sublayerVisibility || {}).forEach(([subId, vis]) => {
17
- if (vis === 'hidden') {
18
- pluginState.layerAdapter?.hideSublayer(datasetId, subId)
19
- }
20
- })
21
- }
6
+ dispatch({ type: 'SET_DATASET_VISIBILITY', payload: { datasetId, visible } })
22
7
  return
23
8
  }
24
9
 
25
10
  // Global
26
- pluginState.dispatch({ type: 'SET_GLOBAL_VISIBILITY', payload: { visibility: visible ? 'visible' : 'hidden' } })
27
- pluginState.datasets?.forEach(dataset => {
28
- pluginState.layerAdapter?.[visible ? 'showDataset' : 'hideDataset'](dataset.id)
29
- if (visible) {
30
- Object.entries(dataset.sublayerVisibility || {}).forEach(([subId, vis]) => {
31
- if (vis === 'hidden') {
32
- pluginState.layerAdapter?.hideSublayer(dataset.id, subId)
33
- }
34
- })
35
- }
36
- })
11
+ dispatch({ type: 'SET_GLOBAL_VISIBILITY', payload: { visible } })
37
12
  }
@@ -0,0 +1,31 @@
1
+ import { setDatasetVisibility } from './setDatasetVisibility.js'
2
+
3
+ describe('setDatasetVisibility', () => {
4
+ const dispatch = jest.fn()
5
+ it('dispatches SET_DATASET_VISIBILITY with visible false', () => {
6
+ setDatasetVisibility({ pluginState: { dispatch } }, false, { datasetId: 'my-dataset' })
7
+
8
+ expect(dispatch).toHaveBeenCalledWith({
9
+ type: 'SET_DATASET_VISIBILITY',
10
+ payload: { datasetId: 'my-dataset', visible: false }
11
+ })
12
+ })
13
+
14
+ it('combines datasetId and sublayerId for the dispatched id', () => {
15
+ setDatasetVisibility({ pluginState: { dispatch } }, true, { datasetId: 'parent', sublayerId: 'child' })
16
+
17
+ expect(dispatch).toHaveBeenCalledWith({
18
+ type: 'SET_DATASET_VISIBILITY',
19
+ payload: { datasetId: 'parent-child', visible: true }
20
+ })
21
+ })
22
+
23
+ it('dispatches SET_GLOBAL_VISIBILITY when ids defaults to empty object', () => {
24
+ setDatasetVisibility({ pluginState: { dispatch } }, false)
25
+
26
+ expect(dispatch).toHaveBeenCalledWith({
27
+ type: 'SET_GLOBAL_VISIBILITY',
28
+ payload: { visible: false }
29
+ })
30
+ })
31
+ })
@@ -1,22 +1,13 @@
1
- export const setFeatureVisibility = ({ pluginState }, visible, featureIds, { datasetId, idProperty = null } = {}) => {
2
- const dataset = pluginState.datasets?.find(d => d.id === datasetId)
3
- if (!dataset) {
4
- return
5
- }
1
+ import { logger } from '../../../../../src/services/logger.js'
2
+ import { datasetRegistry } from '../registry/datasetRegistry.js'
6
3
 
7
- if (visible) {
8
- const existingHidden = pluginState.hiddenFeatures[datasetId]
9
- if (!existingHidden) {
10
- return
4
+ export const setFeatureVisibility = ({ pluginState: { dispatch } }, visible, featureIds, { datasetId = null } = {}) => {
5
+ if (datasetId) {
6
+ const dataset = datasetRegistry.getDataset(datasetId)
7
+ if (dataset && !dataset.idProperty && !dataset.generateIds) {
8
+ logger.warn(`setFeatureVisibility: Dataset "${datasetId}" has no idProperty or generateIds configured. Features may not be matched correctly. Add idProperty to match by a feature property, or add generateIds: true to use auto-generated IDs.`)
11
9
  }
12
- const remainingHiddenIds = existingHidden.ids.filter(id => !featureIds.includes(id))
13
- pluginState.dispatch({ type: 'SHOW_FEATURES', payload: { layerId: datasetId, featureIds } })
14
- pluginState.layerAdapter?.showFeatures(dataset, idProperty, remainingHiddenIds)
15
- } else {
16
- const existingHidden = pluginState.hiddenFeatures[datasetId]
17
- const existingIds = existingHidden?.ids || []
18
- const allHiddenIds = [...new Set([...existingIds, ...featureIds])]
19
- pluginState.dispatch({ type: 'HIDE_FEATURES', payload: { layerId: datasetId, idProperty, featureIds } })
20
- pluginState.layerAdapter?.hideFeatures(dataset, idProperty, allHiddenIds)
21
10
  }
11
+ const type = visible ? 'SHOW_FEATURES' : 'HIDE_FEATURES'
12
+ dispatch({ type, payload: { datasetId, featureIds } })
22
13
  }
@@ -0,0 +1,74 @@
1
+ import { setFeatureVisibility } from './setFeatureVisibility.js'
2
+ import { datasetRegistry } from '../registry/datasetRegistry.js'
3
+ import { logger } from '../../../../../src/services/logger.js'
4
+
5
+ jest.mock('../registry/datasetRegistry.js', () => ({
6
+ datasetRegistry: { getDataset: jest.fn() }
7
+ }))
8
+
9
+ jest.mock('../../../../../src/services/logger.js', () => ({
10
+ logger: { warn: jest.fn() }
11
+ }))
12
+
13
+ describe('setFeatureVisibility', () => {
14
+ const dispatch = jest.fn()
15
+ const featureIds = [1, 2, 3]
16
+ const datasetId = 'my-dataset'
17
+
18
+ beforeEach(() => {
19
+ jest.clearAllMocks()
20
+ datasetRegistry.getDataset.mockReturnValue({ idProperty: 'id', generateIds: false })
21
+ })
22
+
23
+ it('dispatches SHOW_FEATURES when visible is true', () => {
24
+ setFeatureVisibility({ pluginState: { dispatch } }, true, featureIds, { datasetId })
25
+
26
+ expect(dispatch).toHaveBeenCalledWith({
27
+ type: 'SHOW_FEATURES',
28
+ payload: { datasetId, featureIds }
29
+ })
30
+ })
31
+
32
+ it('dispatches HIDE_FEATURES when visible is false', () => {
33
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds, { datasetId })
34
+
35
+ expect(dispatch).toHaveBeenCalledWith({
36
+ type: 'HIDE_FEATURES',
37
+ payload: { datasetId, featureIds }
38
+ })
39
+ })
40
+
41
+ it('uses null datasetId when options are not provided', () => {
42
+ setFeatureVisibility({ pluginState: { dispatch } }, true, featureIds)
43
+
44
+ expect(dispatch).toHaveBeenCalledWith({
45
+ type: 'SHOW_FEATURES',
46
+ payload: { datasetId: null, featureIds }
47
+ })
48
+ })
49
+
50
+ it('does not warn when dataset has idProperty set', () => {
51
+ datasetRegistry.getDataset.mockReturnValue({ idProperty: 'parcel_id', generateIds: false })
52
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds, { datasetId })
53
+ expect(logger.warn).not.toHaveBeenCalled()
54
+ })
55
+
56
+ it('does not warn when dataset has generateIds set', () => {
57
+ datasetRegistry.getDataset.mockReturnValue({ idProperty: null, generateIds: true })
58
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds, { datasetId })
59
+ expect(logger.warn).not.toHaveBeenCalled()
60
+ })
61
+
62
+ it('warns when dataset has neither idProperty nor generateIds', () => {
63
+ datasetRegistry.getDataset.mockReturnValue({ idProperty: null, generateIds: false })
64
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds, { datasetId })
65
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining(`"${datasetId}"`))
66
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('idProperty'))
67
+ })
68
+
69
+ it('does not warn or look up registry when datasetId is not provided', () => {
70
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds)
71
+ expect(datasetRegistry.getDataset).not.toHaveBeenCalled()
72
+ expect(logger.warn).not.toHaveBeenCalled()
73
+ })
74
+ })
@@ -0,0 +1,20 @@
1
+ import { logger } from '../../../../../src/services/logger.js'
2
+ export const setGlobals = ({ pluginState: { dispatch } }, values) => {
3
+ const { opacityMode } = values
4
+ const payload = {}
5
+ let valuesSet = false
6
+ if (opacityMode) {
7
+ if (!['dataset', 'global', 'multiply'].includes(opacityMode)) {
8
+ logger.warn(`Ignoring invalid opacityMode: ${opacityMode}. Must be one of 'dataset', 'global' or 'multiply'.`)
9
+ } else {
10
+ valuesSet = true
11
+ payload.opacityMode = opacityMode
12
+ }
13
+ }
14
+
15
+ if (valuesSet) {
16
+ dispatch({ type: 'SET_GLOBAL_STATE', payload })
17
+ } else {
18
+ logger.warn('setGlobals: requires a valid value for opacityMode to be set')
19
+ }
20
+ }
@@ -0,0 +1,63 @@
1
+ import { setGlobals } from './setGlobals.js'
2
+ import { logger } from '../../../../../src/services/logger.js'
3
+
4
+ jest.mock('../../../../../src/services/logger.js', () => ({
5
+ logger: { warn: jest.fn() }
6
+ }))
7
+
8
+ describe('setGlobals', () => {
9
+ const dispatch = jest.fn()
10
+
11
+ beforeEach(() => {
12
+ jest.clearAllMocks()
13
+ })
14
+
15
+ it('dispatches SET_GLOBAL_STATE with opacityMode when valid value is provided', () => {
16
+ setGlobals({ pluginState: { dispatch } }, { opacityMode: 'dataset' })
17
+
18
+ expect(dispatch).toHaveBeenCalledWith({
19
+ type: 'SET_GLOBAL_STATE',
20
+ payload: { opacityMode: 'dataset' }
21
+ })
22
+ })
23
+
24
+ it.each(['dataset', 'global', 'multiply'])('accepts valid opacityMode: %s', (opacityMode) => {
25
+ setGlobals({ pluginState: { dispatch } }, { opacityMode })
26
+
27
+ expect(dispatch).toHaveBeenCalledWith({
28
+ type: 'SET_GLOBAL_STATE',
29
+ payload: { opacityMode }
30
+ })
31
+ expect(logger.warn).not.toHaveBeenCalled()
32
+ })
33
+
34
+ it('warns and does not dispatch when opacityMode is invalid', () => {
35
+ setGlobals({ pluginState: { dispatch } }, { opacityMode: 'invalid' })
36
+
37
+ expect(logger.warn).toHaveBeenCalledWith(
38
+ "Ignoring invalid opacityMode: invalid. Must be one of 'dataset', 'global' or 'multiply'."
39
+ )
40
+ expect(dispatch).not.toHaveBeenCalled()
41
+ })
42
+
43
+ it('warns and does not dispatch when no valid values are provided', () => {
44
+ setGlobals({ pluginState: { dispatch } }, {})
45
+
46
+ expect(logger.warn).toHaveBeenCalledWith(
47
+ 'setGlobals: requires a valid value for opacityMode to be set'
48
+ )
49
+ expect(dispatch).not.toHaveBeenCalled()
50
+ })
51
+
52
+ it('warns about invalid opacityMode and then warns no valid values set', () => {
53
+ setGlobals({ pluginState: { dispatch } }, { opacityMode: 'bad' })
54
+
55
+ expect(logger.warn).toHaveBeenCalledWith(
56
+ "Ignoring invalid opacityMode: bad. Must be one of 'dataset', 'global' or 'multiply'."
57
+ )
58
+ expect(logger.warn).toHaveBeenCalledWith(
59
+ 'setGlobals: requires a valid value for opacityMode to be set'
60
+ )
61
+ expect(dispatch).not.toHaveBeenCalled()
62
+ })
63
+ })
@@ -1,29 +1,10 @@
1
- export const setOpacity = ({ pluginState }, opacity, options) => {
2
- const { datasetId, sublayerId } = options || {}
3
-
4
- if (sublayerId) {
5
- const dataset = pluginState.datasets?.find(d => d.id === datasetId)
6
- if (!dataset) {
7
- return
8
- }
9
- pluginState.dispatch({ type: 'SET_SUBLAYER_OPACITY', payload: { datasetId, sublayerId, opacity } })
10
- pluginState.layerAdapter?.setSublayerOpacity(datasetId, sublayerId, opacity)
11
- return
1
+ export const setOpacity = ({ pluginState: { dispatch } }, opacity, options = {}) => {
2
+ const { datasetId, sublayerId } = options
3
+ const fullId = sublayerId ? `${datasetId}-${sublayerId}` : datasetId
4
+ if (fullId) {
5
+ dispatch({ type: 'SET_OPACITY', payload: { datasetId: fullId, opacity } })
6
+ } else {
7
+ // Global update
8
+ dispatch({ type: 'SET_GLOBAL_OPACITY', payload: { opacity } })
12
9
  }
13
-
14
- if (datasetId) {
15
- const dataset = pluginState.datasets?.find(d => d.id === datasetId)
16
- if (!dataset) {
17
- return
18
- }
19
- pluginState.dispatch({ type: 'SET_OPACITY', payload: { datasetId, opacity } })
20
- pluginState.layerAdapter?.setOpacity(datasetId, opacity)
21
- return
22
- }
23
-
24
- // Global
25
- pluginState.dispatch({ type: 'SET_GLOBAL_OPACITY', payload: { opacity } })
26
- pluginState.datasets?.forEach(d => {
27
- pluginState.layerAdapter?.setOpacity(d.id, opacity)
28
- })
29
10
  }
@@ -0,0 +1,31 @@
1
+ import { setOpacity } from './setOpacity.js'
2
+
3
+ describe('setOpacity', () => {
4
+ const dispatch = jest.fn()
5
+ it('dispatches SET_OPACITY when datasetId is provided', () => {
6
+ setOpacity({ pluginState: { dispatch } }, 0.5, { datasetId: 'my-dataset' })
7
+
8
+ expect(dispatch).toHaveBeenCalledWith({
9
+ type: 'SET_OPACITY',
10
+ payload: { datasetId: 'my-dataset', opacity: 0.5 }
11
+ })
12
+ })
13
+
14
+ it('combines datasetId and sublayerId for the dispatched datasetId', () => {
15
+ setOpacity({ pluginState: { dispatch } }, 0.4, { datasetId: 'parent', sublayerId: 'child' })
16
+
17
+ expect(dispatch).toHaveBeenCalledWith({
18
+ type: 'SET_OPACITY',
19
+ payload: { datasetId: 'parent-child', opacity: 0.4 }
20
+ })
21
+ })
22
+
23
+ it('dispatches SET_GLOBAL_OPACITY when no datasetId is provided', () => {
24
+ setOpacity({ pluginState: { dispatch } }, 0.8, {})
25
+
26
+ expect(dispatch).toHaveBeenCalledWith({
27
+ type: 'SET_GLOBAL_OPACITY',
28
+ payload: { opacity: 0.8 }
29
+ })
30
+ })
31
+ })
@@ -1,22 +1,6 @@
1
- export const setStyle = ({ pluginState, mapState }, style, { datasetId, sublayerId } = {}) => {
2
- const dataset = pluginState.datasets?.find(d => d.id === datasetId)
3
- if (!dataset) {
4
- return
5
- }
6
-
7
- if (sublayerId) {
8
- pluginState.dispatch({ type: 'SET_SUBLAYER_STYLE', payload: { datasetId, sublayerId, styleChanges: style } })
9
- const updatedSublayerDataset = {
10
- ...dataset,
11
- sublayers: dataset.sublayers?.map(sublayer =>
12
- sublayer.id === sublayerId ? { ...sublayer, style: { ...sublayer.style, ...style } } : sublayer
13
- )
14
- }
15
- pluginState.layerAdapter?.setSublayerStyle(updatedSublayerDataset, sublayerId, mapState.mapStyle)
16
- return
17
- }
18
-
19
- pluginState.dispatch({ type: 'SET_DATASET_STYLE', payload: { datasetId, styleChanges: style } })
20
- const updatedDataset = { ...dataset, ...style }
21
- pluginState.layerAdapter?.setStyle(updatedDataset, mapState.mapStyle)
1
+ export const setStyle = ({ pluginState: { dispatch }, mapState }, styleChanges, { datasetId, sublayerId } = {}) => {
2
+ datasetId = sublayerId ? `${datasetId}-${sublayerId}` : datasetId
3
+ const mapStyle = mapState.mapStyle
4
+ const payload = { datasetId, styleChanges, mapStyle }
5
+ dispatch({ type: 'SET_DATASET_STYLE', payload })
22
6
  }
@@ -0,0 +1,53 @@
1
+ import { setStyle } from './setStyle.js'
2
+
3
+ describe('setStyle', () => {
4
+ it('dispatches SET_DATASET_STYLE with datasetId, styleChanges, and mapStyle', () => {
5
+ const dispatch = jest.fn()
6
+ const mapStyle = { version: 8, layers: [] }
7
+ const styleChanges = { fill: 'blue' }
8
+
9
+ setStyle({ pluginState: { dispatch }, mapState: { mapStyle } }, styleChanges, { datasetId: 'my-dataset' })
10
+
11
+ expect(dispatch).toHaveBeenCalledWith({
12
+ type: 'SET_DATASET_STYLE',
13
+ payload: { datasetId: 'my-dataset', styleChanges, mapStyle }
14
+ })
15
+ })
16
+
17
+ it('combines datasetId and sublayerId for the dispatched datasetId', () => {
18
+ const dispatch = jest.fn()
19
+ const mapStyle = { version: 8, layers: [] }
20
+ const styleChanges = { stroke: 'red' }
21
+
22
+ setStyle({ pluginState: { dispatch }, mapState: { mapStyle } }, styleChanges, { datasetId: 'parent', sublayerId: 'child' })
23
+
24
+ expect(dispatch).toHaveBeenCalledWith({
25
+ type: 'SET_DATASET_STYLE',
26
+ payload: { datasetId: 'parent-child', styleChanges, mapStyle }
27
+ })
28
+ })
29
+
30
+ it('passes mapState.mapStyle in the payload', () => {
31
+ const dispatch = jest.fn()
32
+ const mapStyle = { version: 8, sources: { s: {} }, layers: [{ id: 'base' }] }
33
+
34
+ setStyle({ pluginState: { dispatch }, mapState: { mapStyle } }, {}, { datasetId: 'ds' })
35
+
36
+ expect(dispatch).toHaveBeenCalledWith(
37
+ expect.objectContaining({ payload: expect.objectContaining({ mapStyle }) })
38
+ )
39
+ })
40
+
41
+ it('uses undefined datasetId when options are not provided', () => {
42
+ const dispatch = jest.fn()
43
+ const mapStyle = { version: 8, layers: [] }
44
+ const styleChanges = { fill: 'green' }
45
+
46
+ setStyle({ pluginState: { dispatch }, mapState: { mapStyle } }, styleChanges)
47
+
48
+ expect(dispatch).toHaveBeenCalledWith({
49
+ type: 'SET_DATASET_STYLE',
50
+ payload: { datasetId: undefined, styleChanges, mapStyle }
51
+ })
52
+ })
53
+ })
@@ -0,0 +1,51 @@
1
+ import React from 'react'
2
+ import { EmptyKey } from './EmptyKey.jsx'
3
+ import { KeyItem } from './KeyItem.jsx'
4
+ import { KeyGroupItem } from './KeyGroupItem.jsx'
5
+ import { datasetRegistry } from '../../registry/datasetRegistry.js'
6
+
7
+ export const Key = ({
8
+ pluginConfig: { noKeyItemText },
9
+ mapState: { mapStyle },
10
+ pluginState: { mappedDatasets },
11
+ services: { symbolRegistry, patternRegistry }
12
+ }) => {
13
+ const { items: keyGroups, hasGroups } = datasetRegistry.keyItems()
14
+
15
+ if (!keyGroups?.length) {
16
+ return (<EmptyKey text={noKeyItemText} />)
17
+ }
18
+
19
+ const containerClass = `im-c-datasets-key${hasGroups ? ' im-c-datasets-key--has-groups' : ''}`
20
+ return (
21
+ <div className={containerClass}>{keyGroups.map(item => {
22
+ const key = item.type === 'group' ? item.groupLabel.toLowerCase().replaceAll(/\s+/g, '-') : item.dataset.id
23
+
24
+ if (item.type === 'sublayers' || item.type === 'group') {
25
+ const datasets = item.type === 'sublayers' ? item.sublayers : item.datasets
26
+ const label = item.type === 'sublayers' ? item.dataset.label : item.groupLabel
27
+ return (
28
+ <KeyGroupItem
29
+ key={key}
30
+ headingId={`key-heading-${key}`}
31
+ label={label}
32
+ datasets={datasets}
33
+ symbolRegistry={symbolRegistry}
34
+ patternRegistry={patternRegistry}
35
+ mapStyle={mapStyle}
36
+ />
37
+ )
38
+ }
39
+ return (
40
+ <KeyItem
41
+ key={key}
42
+ registryDataset={item.dataset}
43
+ symbolRegistry={symbolRegistry}
44
+ patternRegistry={patternRegistry}
45
+ mapStyle={mapStyle}
46
+ />
47
+ )
48
+ })}
49
+ </div>
50
+ )
51
+ }