@defra/interactive-map 0.0.40-alpha → 0.0.41-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/index.css +1 -1
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/im-shell.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api/control-definition.md +9 -1
- package/docs/api/slots.md +26 -5
- package/docs/assets/images/slot-map.svg +61 -262
- package/docs/examples/draw-tools.mdx +3 -3
- package/docs/plugins/draw.md +79 -16
- package/docs/plugins/search.md +1 -1
- package/govuk-prototype-kit.config.json +2 -0
- package/jest.setup.js +5 -0
- package/package.json +9 -1
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/index.js +1 -1
- package/plugins/datasets/dist/css/index.css +1 -1
- package/plugins/datasets/dist/esm/esriLayerAdapter.js +1 -1
- package/plugins/datasets/dist/esm/im-datasets-ml-adapter.js +1 -1
- package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-esri-adapter.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-ml-adapter.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/index.js +1 -1
- package/plugins/datasets/src/adapters/esri/esriLayerAdapter.test.js +175 -0
- package/plugins/datasets/src/adapters/esri/registry/esriDataset.test.js +55 -0
- package/plugins/datasets/src/adapters/loadLayerAdapter.test.js +61 -0
- package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +14 -0
- package/plugins/datasets/src/api/setOpacity.test.js +9 -0
- package/plugins/datasets/src/components/LayersMenu/LayersMenu.jsx +10 -25
- package/plugins/datasets/src/components/LayersMenu/LayersMenu.test.jsx +0 -42
- package/plugins/datasets/src/datasets.scss +0 -1
- package/plugins/datasets/src/fetch/createDynamicSource.js +135 -129
- package/plugins/datasets/src/fetch/createDynamicSource.test.js +318 -0
- package/plugins/datasets/src/fetch/fetchGeoJSON.test.js +83 -0
- package/plugins/datasets/src/index.test.js +36 -0
- package/plugins/datasets/src/initialise/DatasetsInit.jsx +17 -7
- package/plugins/datasets/src/initialise/DatasetsInit.test.jsx +262 -0
- package/plugins/datasets/src/initialise/initialiseDatasets.js +16 -4
- package/plugins/datasets/src/initialise/initialiseDatasets.test.js +42 -0
- package/plugins/datasets/src/manifest.js +0 -38
- package/plugins/datasets/src/manifest.test.js +49 -0
- package/plugins/datasets/src/reducers/datasetsToMenu.test.js +54 -1
- package/plugins/datasets/src/reducers/pluginState.js +3 -16
- package/plugins/datasets/src/reducers/pluginState.test.js +23 -0
- package/plugins/datasets/src/registry/dataset.js +13 -1
- package/plugins/datasets/src/registry/dataset.test.js +61 -0
- package/plugins/datasets/src/registry/datasetRegistry.js +12 -6
- package/plugins/datasets/src/registry/datasetRegistry.test.js +16 -4
- package/plugins/datasets/src/registry/isVisibleWhen.js +6 -3
- package/plugins/datasets/src/registry/isVisibleWhen.test.js +11 -1
- package/plugins/datasets/src/utils/bbox.test.js +125 -0
- package/plugins/draw/dist/esm/im-draw-ml-adapter.js +1 -1
- package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/esm/im-draw-plugin.js +1 -1
- package/plugins/draw/dist/esm/index.js +1 -1
- package/plugins/draw/dist/umd/im-draw-ml-adapter.js +2 -1
- package/plugins/draw/dist/umd/im-draw-ml-adapter.js.LICENSE.txt +1 -0
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/umd/im-draw-plugin.js +1 -1
- package/plugins/draw/dist/umd/index.js +1 -1
- package/plugins/draw/src/DrawInit.jsx +10 -2
- package/plugins/draw/src/DrawInit.test.jsx +51 -10
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +91 -11
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.test.js +246 -4
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.js +109 -14
- package/plugins/draw/src/adapters/maplibre/mapboxDraw.test.js +174 -11
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/clickHandlers.test.js +12 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.js +9 -1
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/lifecycle.test.js +24 -3
- package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.js +194 -0
- package/plugins/draw/src/adapters/maplibre/modes/drawPointMode.test.js +457 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/__helpers__/harness.js +102 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.js +82 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/keyboardHandlers.test.js +83 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.js +55 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointOperations.test.js +62 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.js +138 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/pointerHandlers.test.js +197 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.js +92 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/touchHandlers.test.js +101 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.js +46 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode/undoHandlers.test.js +42 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode.js +151 -0
- package/plugins/draw/src/adapters/maplibre/modes/editPointMode.test.js +156 -0
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.js +4 -27
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/keyboardHandlers.test.js +24 -39
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/pointerHandlers.js +4 -5
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.js +20 -23
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/touchHandlers.test.js +25 -5
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.js +6 -36
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/undoHandlers.test.js +7 -49
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.js +3 -53
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode/vertexOperations.test.js +8 -33
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.js +29 -72
- package/plugins/draw/src/adapters/maplibre/modes/editVertexMode.test.js +10 -3
- package/plugins/draw/src/adapters/maplibre/pointSymbolImages.js +158 -0
- package/plugins/draw/src/adapters/maplibre/pointSymbolImages.test.js +314 -0
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.js +6 -1
- package/plugins/draw/src/adapters/maplibre/snap/prototypePatches.test.js +21 -0
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.js +6 -2
- package/plugins/draw/src/adapters/maplibre/snap/snapInstance.test.js +12 -0
- package/plugins/draw/src/adapters/maplibre/styles.js +38 -11
- package/plugins/draw/src/adapters/maplibre/styles.test.js +39 -0
- package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.js +79 -0
- package/plugins/draw/src/adapters/maplibre/utils/editModeEvents.test.js +135 -0
- package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.js +42 -0
- package/plugins/draw/src/adapters/maplibre/utils/geometryValidation.test.js +85 -0
- package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.js +34 -0
- package/plugins/draw/src/adapters/maplibre/utils/keyboardShortcuts.test.js +78 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.js +16 -3
- package/plugins/draw/src/adapters/maplibre/utils/snapHelpers.test.js +21 -3
- package/plugins/draw/src/adapters/maplibre/utils/snapMovement.js +59 -0
- package/plugins/draw/src/adapters/maplibre/utils/snapMovement.test.js +73 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.js +33 -0
- package/plugins/draw/src/adapters/maplibre/utils/touchDragMath.test.js +70 -0
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.js +27 -2
- package/plugins/draw/src/adapters/openlayers/OLDrawAdapter.test.js +93 -0
- package/plugins/draw/src/adapters/openlayers/__helpers__/harness.js +9 -0
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.js +19 -1
- package/plugins/draw/src/adapters/openlayers/core/OLDrawManager.test.js +34 -1
- package/plugins/draw/src/adapters/openlayers/core/styles.js +73 -1
- package/plugins/draw/src/adapters/openlayers/core/styles.test.js +122 -3
- package/plugins/draw/src/adapters/openlayers/draw/DrawMode.test.js +12 -0
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +8 -2
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +31 -0
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +9 -5
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +33 -0
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.js +5 -3
- package/plugins/draw/src/adapters/openlayers/edit/modifyInteraction.test.js +11 -0
- package/plugins/draw/src/adapters/openlayers/edit/nudge.js +10 -3
- package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +24 -1
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.js +18 -13
- package/plugins/draw/src/adapters/openlayers/edit/touchHandler.test.js +46 -1
- package/plugins/draw/src/adapters/openlayers/olDraw.js +11 -4
- package/plugins/draw/src/adapters/openlayers/olDraw.test.js +48 -6
- package/plugins/draw/src/adapters/openlayers/point/drawPointMode.js +156 -0
- package/plugins/draw/src/adapters/openlayers/point/drawPointMode.test.js +246 -0
- package/plugins/draw/src/adapters/openlayers/point/editPointMode.js +267 -0
- package/plugins/draw/src/adapters/openlayers/point/editPointMode.test.js +327 -0
- package/plugins/draw/src/adapters/openlayers/point/pointDragInteraction.js +71 -0
- package/plugins/draw/src/adapters/openlayers/point/pointOps.js +34 -0
- package/plugins/draw/src/adapters/openlayers/point/pointOps.test.js +32 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.js +82 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSelectionState.test.js +107 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.js +83 -0
- package/plugins/draw/src/adapters/openlayers/point/pointSymbolImages.test.js +214 -0
- package/plugins/draw/src/adapters/openlayers/snap/snapIndicator.js +3 -1
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.js +6 -9
- package/plugins/draw/src/adapters/openlayers/snap/snapInteraction.test.js +15 -4
- package/plugins/draw/src/adapters/openlayers/utils/olCoords.js +1 -1
- package/plugins/draw/src/api/addFeature.js +16 -1
- package/plugins/draw/src/api/addFeature.test.js +62 -5
- package/plugins/draw/src/api/editFeature.js +7 -4
- package/plugins/draw/src/api/editFeature.test.js +16 -0
- package/plugins/draw/src/api/newPoint.js +42 -0
- package/plugins/draw/src/api/newPoint.test.js +117 -0
- package/plugins/draw/src/api/setStyle.js +43 -0
- package/plugins/draw/src/api/setStyle.test.js +75 -0
- package/plugins/draw/src/events.js +17 -10
- package/plugins/draw/src/events.test.js +14 -0
- package/plugins/draw/src/manifest.js +18 -6
- package/plugins/draw/src/manifest.test.js +32 -1
- package/plugins/draw/src/reducer.test.js +7 -0
- package/plugins/draw/src/utils/stripInternalProperties.js +19 -0
- package/plugins/draw/src/utils/stripInternalProperties.test.js +50 -0
- package/plugins/draw/src/utils/symbolKeys.js +5 -0
- package/plugins/draw/src/validation/liveDrawChecks.test.js +54 -0
- package/plugins/draw/src/validation/liveStroke.test.js +64 -1
- package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/index.js +1 -1
- package/plugins/interact/src/hooks/useHighlightSync.js +33 -12
- package/plugins/interact/src/hooks/useHighlightSync.test.js +96 -25
- package/plugins/interact/src/utils/featureQueries.js +18 -0
- package/plugins/interact/src/utils/featureQueries.test.js +15 -0
- package/plugins/interact/src/utils/spatial.js +9 -3
- package/plugins/interact/src/utils/spatial.test.js +12 -1
- package/plugins/map-key/dist/css/index.css +62 -0
- package/plugins/map-key/dist/esm/im-map-key-plugin.js +1 -0
- package/plugins/map-key/dist/esm/index.js +1 -0
- package/plugins/map-key/dist/umd/im-map-key-plugin.js +2 -0
- package/plugins/map-key/dist/umd/im-map-key-plugin.js.LICENSE.txt +1 -0
- package/plugins/map-key/dist/umd/index.js +2 -0
- package/plugins/map-key/dist/umd/index.js.LICENSE.txt +1 -0
- package/plugins/map-key/package.json +28 -0
- package/plugins/map-key/src/components/Key/EmptyKey.jsx +7 -0
- package/plugins/{datasets → map-key}/src/components/Key/EmptyKey.test.jsx +2 -2
- package/plugins/map-key/src/components/Key/Key.jsx +41 -0
- package/plugins/{datasets → map-key}/src/components/Key/Key.module.scss +15 -14
- package/plugins/map-key/src/components/Key/Key.test.jsx +73 -0
- package/plugins/map-key/src/components/Key/KeyGroupItem.jsx +16 -0
- package/plugins/map-key/src/components/Key/KeyGroupItem.test.jsx +53 -0
- package/plugins/map-key/src/components/Key/KeyItem.jsx +29 -0
- package/plugins/map-key/src/components/Key/KeyItem.test.jsx +67 -0
- package/plugins/map-key/src/components/Key/KeySvg.jsx +42 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvg.test.jsx +14 -12
- package/plugins/map-key/src/components/Key/KeySvgLine.jsx +28 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgLine.test.jsx +1 -1
- package/plugins/map-key/src/components/Key/KeySvgPattern.jsx +23 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgPattern.test.jsx +2 -2
- package/plugins/map-key/src/components/Key/KeySvgRect.jsx +33 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgRect.test.jsx +1 -1
- package/plugins/map-key/src/components/Key/KeySvgSymbol.jsx +27 -0
- package/plugins/{datasets → map-key}/src/components/Key/KeySvgSymbol.test.jsx +5 -6
- package/plugins/map-key/src/components/Key/MapKey.jsx +50 -0
- package/plugins/map-key/src/components/Key/MapKey.test.jsx +130 -0
- package/plugins/{datasets → map-key}/src/components/Key/svgProperties.js +2 -2
- package/plugins/{datasets → map-key}/src/components/Key/svgProperties.test.js +2 -2
- package/plugins/map-key/src/index.js +13 -0
- package/plugins/map-key/src/index.test.js +31 -0
- package/plugins/map-key/src/initialise/MapKeyInit.jsx +15 -0
- package/plugins/map-key/src/initialise/MapKeyInit.test.jsx +39 -0
- package/plugins/map-key/src/manifest.js +30 -0
- package/plugins/map-key/src/manifest.test.js +63 -0
- package/plugins/map-key/src/mapKey.scss +4 -0
- package/plugins/map-key/src/registry/getDatasetRegistry.js +6 -0
- package/plugins/map-key/src/registry/getDatasetRegistry.test.js +23 -0
- package/plugins/map-key/src/registry/index.js +3 -0
- package/plugins/map-key/src/registry/index.test.js +15 -0
- package/plugins/menu/dist/css/index.css +80 -0
- package/plugins/menu/dist/esm/im-menu-plugin.js +1 -0
- package/plugins/menu/dist/esm/index.js +1 -0
- package/plugins/menu/dist/umd/im-menu-plugin.js +1 -0
- package/plugins/menu/dist/umd/index.js +2 -0
- package/plugins/menu/dist/umd/index.js.LICENSE.txt +1 -0
- package/plugins/menu/package.json +28 -0
- package/plugins/menu/src/components/Menu/CheckboxGroupWrapper.jsx +21 -0
- package/plugins/menu/src/components/Menu/GroupLegend.jsx +17 -0
- package/plugins/menu/src/components/Menu/GroupLegend.test.jsx +68 -0
- package/plugins/menu/src/components/Menu/Menu.jsx +19 -0
- package/plugins/menu/src/components/Menu/Menu.module.scss +90 -0
- package/plugins/menu/src/components/Menu/Menu.test.jsx +161 -0
- package/plugins/menu/src/components/Menu/MenuCheckbox.jsx +35 -0
- package/plugins/menu/src/components/Menu/MenuCheckbox.test.jsx +93 -0
- package/plugins/menu/src/components/Menu/MenuGroup.jsx +11 -0
- package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.jsx → menu/src/components/Menu/MenuRadio.jsx} +6 -4
- package/plugins/{datasets/src/components/LayersMenu/LayersMenuRadio.test.jsx → menu/src/components/Menu/MenuRadio.test.jsx} +18 -18
- package/plugins/menu/src/components/Menu/RadioGroupWrapper.jsx +32 -0
- package/plugins/{datasets/src/components/LayersMenu/LayersRadioGroupWrapper.test.jsx → menu/src/components/Menu/RadioGroupWrapper.test.jsx} +21 -21
- package/plugins/menu/src/components/Menu/useIdPrefix.jsx +4 -0
- package/plugins/menu/src/index.js +13 -0
- package/plugins/menu/src/index.test.js +31 -0
- package/plugins/menu/src/initialise/MenuInit.jsx +24 -0
- package/plugins/menu/src/initialise/MenuInit.test.jsx +50 -0
- package/plugins/menu/src/initialise/createMenu.js +10 -0
- package/plugins/menu/src/initialise/createMenu.test.js +37 -0
- package/plugins/menu/src/manifest.js +35 -0
- package/plugins/menu/src/manifest.test.js +63 -0
- package/plugins/menu/src/menu.scss +4 -0
- package/plugins/menu/src/reducers/menuStateReducer.test.js +38 -0
- package/plugins/menu/src/reducers/pluginState.js +35 -0
- package/plugins/menu/src/reducers/pluginState.test.js +57 -0
- package/plugins/menu/src/registry/getDatasetRegistry.js +9 -0
- package/plugins/menu/src/registry/getDatasetRegistry.test.js +23 -0
- package/plugins/menu/src/registry/index.js +3 -0
- package/plugins/menu/src/registry/index.test.js +15 -0
- package/plugins/menu/src/registry/isVisibleWhen.js +43 -0
- package/plugins/menu/src/registry/isVisibleWhen.test.js +48 -0
- package/plugins/search/dist/css/index.css +1 -1
- package/plugins/search/dist/esm/im-search-plugin.js +1 -1
- package/plugins/search/dist/esm/index.js +1 -1
- package/plugins/search/dist/umd/im-search-plugin.js +1 -1
- package/plugins/search/dist/umd/index.js +1 -1
- package/plugins/search/src/Search.jsx +2 -3
- package/plugins/search/src/components/Form/Form.module.scss +9 -20
- package/plugins/search/src/index.js +0 -1
- package/plugins/search/src/index.test.js +2 -4
- package/plugins/search/src/manifest.js +1 -1
- package/plugins/search/src/search.scss +17 -16
- package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/index.js +1 -1
- package/providers/beta/openlayers/src/openlayersProvider.js +4 -1
- package/providers/beta/openlayers/src/openlayersProvider.test.js +18 -0
- package/providers/beta/openlayers/src/utils/highlightFeatures.js +52 -5
- package/providers/beta/openlayers/src/utils/highlightFeatures.test.js +176 -0
- package/providers/beta/openlayers/src/utils/symbolImages.js +40 -0
- package/providers/beta/openlayers/src/utils/symbolImages.test.js +58 -0
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/index.js +1 -1
- package/providers/maplibre/src/utils/drawLayerBuckets.js +14 -0
- package/providers/maplibre/src/utils/drawLayerBuckets.test.js +18 -0
- package/providers/maplibre/src/utils/highlightFeatures.js +80 -55
- package/providers/maplibre/src/utils/highlightFeatures.test.js +156 -17
- package/providers/maplibre/src/utils/hoverCursor.js +30 -14
- package/providers/maplibre/src/utils/hoverCursor.test.js +35 -0
- package/providers/maplibre/src/utils/patternImages.js +1 -1
- package/providers/maplibre/src/utils/symbolImages.js +41 -3
- package/providers/maplibre/src/utils/symbolImages.test.js +40 -6
- package/rollup.esm.mjs +11 -1
- package/src/App/components/Actions/Actions.test.jsx +47 -0
- package/src/App/components/Hints/Hints.test.jsx +13 -0
- package/src/App/components/MoveControls/MoveControls.module.scss +41 -10
- package/src/App/components/Panel/Panel.jsx +18 -3
- package/src/App/components/Panel/Panel.module.scss +18 -8
- package/src/App/components/Panel/Panel.test.jsx +34 -0
- package/src/App/components/Tooltip/Tooltip.test.jsx +12 -14
- package/src/App/hooks/useFeatureItems.js +16 -7
- package/src/App/hooks/useFeatureItems.test.js +57 -0
- package/src/App/hooks/useLayoutMeasurements.js +95 -84
- package/src/App/hooks/useLayoutMeasurements.test.js +61 -1
- package/src/App/layout/Layout.jsx +9 -10
- package/src/App/layout/Layout.test.jsx +2 -0
- package/src/App/layout/layout.module.scss +31 -17
- package/src/App/registry/pluginRegistry.js +1 -1
- package/src/App/registry/pluginRegistry.test.js +12 -0
- package/src/App/renderer/HtmlElementHost.jsx +18 -5
- package/src/App/renderer/HtmlElementHost.test.jsx +54 -0
- package/src/App/renderer/mapButtons.js +28 -12
- package/src/App/renderer/mapButtons.test.js +17 -4
- package/src/App/renderer/mapControls.js +3 -1
- package/src/App/renderer/mapControls.test.js +16 -0
- package/src/App/renderer/mapPanels.js +44 -9
- package/src/App/renderer/mapPanels.test.js +51 -2
- package/src/App/renderer/orderItems.js +28 -0
- package/src/App/renderer/orderItems.test.js +50 -0
- package/src/App/renderer/slotAggregator.js +2 -18
- package/src/App/renderer/slotHelpers.js +3 -1
- package/src/App/renderer/slotHelpers.test.js +5 -0
- package/src/App/renderer/slots.js +3 -0
- package/src/App/store/AppProvider.jsx +1 -0
- package/src/App/store/appDispatchMiddleware.js +1 -1
- package/src/App/store/appDispatchMiddleware.test.js +8 -0
- package/src/config/events.js +2 -0
- package/src/scss/settings/_colors.scss +1 -0
- package/src/scss/settings/_dimensions.scss +2 -3
- package/src/scss/settings/_transition.scss +1 -1
- package/src/services/eventBus.js +44 -0
- package/src/services/eventBus.test.js +83 -0
- package/src/services/symbolRegistry.js +1 -1
- package/src/types.js +11 -1
- package/webpack.umd.mjs +2 -0
- package/plugins/datasets/src/components/Key/EmptyKey.jsx +0 -7
- package/plugins/datasets/src/components/Key/Key.jsx +0 -50
- package/plugins/datasets/src/components/Key/KeyGroupItem.jsx +0 -18
- package/plugins/datasets/src/components/Key/KeyItem.jsx +0 -23
- package/plugins/datasets/src/components/Key/KeySvg.jsx +0 -22
- package/plugins/datasets/src/components/Key/KeySvgLine.jsx +0 -19
- package/plugins/datasets/src/components/Key/KeySvgPattern.jsx +0 -13
- package/plugins/datasets/src/components/Key/KeySvgRect.jsx +0 -22
- package/plugins/datasets/src/components/Key/KeySvgSymbol.jsx +0 -16
- package/plugins/datasets/src/components/LayersMenu/LayersRadioGroupWrapper.jsx +0 -41
- /package/plugins/{datasets → menu}/src/reducers/menuStateReducer.js +0 -0
- /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.js +0 -0
- /package/{providers/maplibre/src → src}/utils/rasteriseToImageData.test.js +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { fetchGeoJSON } from './fetchGeoJSON.js'
|
|
2
|
+
|
|
3
|
+
const baseUrl = 'https://api.example.com/features'
|
|
4
|
+
const context = { bbox: [-1, -1, 1, 1], zoom: 10 }
|
|
5
|
+
const signal = { aborted: false }
|
|
6
|
+
|
|
7
|
+
const mockResponse = ({ ok = true, status = 200, statusText = 'OK', body = {} } = {}) => ({
|
|
8
|
+
ok,
|
|
9
|
+
status,
|
|
10
|
+
statusText,
|
|
11
|
+
json: async () => body
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
global.fetch = jest.fn()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('fetchGeoJSON', () => {
|
|
19
|
+
it('calls transformRequest with the base URL and context, then fetches the returned url/headers', async () => {
|
|
20
|
+
const featureCollection = { type: 'FeatureCollection', features: [] }
|
|
21
|
+
global.fetch.mockResolvedValueOnce(mockResponse({ body: featureCollection }))
|
|
22
|
+
const transformRequest = jest.fn(() => ({ url: `${baseUrl}?bbox=${context.bbox.join(',')}`, headers: { Authorization: 'Bearer token' } }))
|
|
23
|
+
|
|
24
|
+
const result = await fetchGeoJSON(baseUrl, context, transformRequest, signal)
|
|
25
|
+
|
|
26
|
+
expect(transformRequest).toHaveBeenCalledWith(baseUrl, context)
|
|
27
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
28
|
+
`${baseUrl}?bbox=-1,-1,1,1`,
|
|
29
|
+
{ headers: { Authorization: 'Bearer token' }, signal }
|
|
30
|
+
)
|
|
31
|
+
expect(result).toBe(featureCollection)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('supports transformRequest returning a plain URL string, defaulting headers to {}', async () => {
|
|
35
|
+
global.fetch.mockResolvedValueOnce(mockResponse({ body: { type: 'FeatureCollection', features: [] } }))
|
|
36
|
+
const transformRequest = () => baseUrl
|
|
37
|
+
|
|
38
|
+
await fetchGeoJSON(baseUrl, context, transformRequest, signal)
|
|
39
|
+
|
|
40
|
+
expect(fetch).toHaveBeenCalledWith(baseUrl, { headers: {}, signal })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('defaults headers to {} when transformRequest returns an object without headers', async () => {
|
|
44
|
+
global.fetch.mockResolvedValueOnce(mockResponse({ body: { type: 'FeatureCollection', features: [] } }))
|
|
45
|
+
const transformRequest = () => ({ url: baseUrl })
|
|
46
|
+
|
|
47
|
+
await fetchGeoJSON(baseUrl, context, transformRequest, signal)
|
|
48
|
+
|
|
49
|
+
expect(fetch).toHaveBeenCalledWith(baseUrl, { headers: {}, signal })
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('throws when the response is not ok', async () => {
|
|
53
|
+
global.fetch.mockResolvedValueOnce(mockResponse({ ok: false, status: 500, statusText: 'Server Error' }))
|
|
54
|
+
|
|
55
|
+
await expect(fetchGeoJSON(baseUrl, context, () => baseUrl, signal))
|
|
56
|
+
.rejects.toThrow('Failed to fetch GeoJSON: 500 Server Error')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('wraps a single Feature into a FeatureCollection', async () => {
|
|
60
|
+
const feature = { type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] } }
|
|
61
|
+
global.fetch.mockResolvedValueOnce(mockResponse({ body: feature }))
|
|
62
|
+
|
|
63
|
+
const result = await fetchGeoJSON(baseUrl, context, () => baseUrl, signal)
|
|
64
|
+
|
|
65
|
+
expect(result).toEqual({ type: 'FeatureCollection', features: [feature] })
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('wraps a bare array of features into a FeatureCollection', async () => {
|
|
69
|
+
const features = [{ type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] } }]
|
|
70
|
+
global.fetch.mockResolvedValueOnce(mockResponse({ body: features }))
|
|
71
|
+
|
|
72
|
+
const result = await fetchGeoJSON(baseUrl, context, () => baseUrl, signal)
|
|
73
|
+
|
|
74
|
+
expect(result).toEqual({ type: 'FeatureCollection', features })
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('throws for an unrecognized response shape', async () => {
|
|
78
|
+
global.fetch.mockResolvedValueOnce(mockResponse({ body: { type: 'NotGeoJSON' } }))
|
|
79
|
+
|
|
80
|
+
await expect(fetchGeoJSON(baseUrl, context, () => baseUrl, signal))
|
|
81
|
+
.rejects.toThrow('Invalid GeoJSON response')
|
|
82
|
+
})
|
|
83
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import createPlugin from './index.js'
|
|
2
|
+
|
|
3
|
+
jest.mock('./datasets.scss', () => {})
|
|
4
|
+
jest.mock('./manifest.js', () => ({
|
|
5
|
+
manifest: { InitComponent: jest.fn(), reducer: {} }
|
|
6
|
+
}))
|
|
7
|
+
|
|
8
|
+
describe('createPlugin', () => {
|
|
9
|
+
it('returns a plugin with id "datasets"', () => {
|
|
10
|
+
const plugin = createPlugin()
|
|
11
|
+
expect(plugin.id).toBe('datasets')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('applies default noKeyItemText', () => {
|
|
15
|
+
const plugin = createPlugin()
|
|
16
|
+
expect(plugin.noKeyItemText).toBe('No features displayed')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('merges provided options (options override defaults, id stays fixed)', () => {
|
|
20
|
+
const plugin = createPlugin({ noKeyItemText: 'Custom text', extra: true })
|
|
21
|
+
expect(plugin.noKeyItemText).toBe('Custom text')
|
|
22
|
+
expect(plugin.extra).toBe(true)
|
|
23
|
+
expect(plugin.id).toBe('datasets')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('exposes a load function', () => {
|
|
27
|
+
const plugin = createPlugin()
|
|
28
|
+
expect(typeof plugin.load).toBe('function')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('load() resolves to the manifest', async () => {
|
|
32
|
+
const plugin = createPlugin()
|
|
33
|
+
const result = await plugin.load()
|
|
34
|
+
expect(result).toBeDefined()
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -3,15 +3,27 @@ import { useEffect, useRef } from 'react'
|
|
|
3
3
|
import { EVENTS } from '../../../../src/config/events.js'
|
|
4
4
|
import { initialiseDatasets } from './initialiseDatasets.js'
|
|
5
5
|
import { datasetRegistry } from '../registry/datasetRegistry.js'
|
|
6
|
-
import { setMenuState } from '../registry/isVisibleWhen.js'
|
|
7
6
|
import { attachGlobalState } from '../registry/globalDataset.js'
|
|
8
7
|
import { loadLayerAdapter, layerAdapter } from '../adapters/loadLayerAdapter.js'
|
|
9
8
|
|
|
9
|
+
const pluginConfigHasMenuItems = (pluginConfig) =>
|
|
10
|
+
pluginConfig.datasets
|
|
11
|
+
.some(dataset => dataset.showInMenu || dataset.sublayers?.some(sublayer => sublayer.showInMenu))
|
|
12
|
+
|
|
10
13
|
export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, mapProvider, services }) {
|
|
11
14
|
const { dispatch } = pluginState
|
|
12
15
|
const { eventBus, symbolRegistry, patternRegistry } = services
|
|
13
16
|
const isBaseMapReady = Boolean(mapProvider?.isBaseMapReady())
|
|
14
17
|
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const hasMenu = pluginConfig.hasMenu !== false && pluginConfigHasMenuItems(pluginConfig)
|
|
20
|
+
|
|
21
|
+
if (!hasMenu) {
|
|
22
|
+
eventBus.emit(EVENTS.APP_REMOVE_PANEL, 'datasetsLayers')
|
|
23
|
+
eventBus.emit(EVENTS.APP_TOGGLE_BUTTON_STATE, { id: 'datasetsLayers', prop: 'hidden', value: true })
|
|
24
|
+
}
|
|
25
|
+
}, [pluginConfig.hasMenu])
|
|
26
|
+
|
|
15
27
|
// Keep a ref to the latest pluginState so event handlers can access current data
|
|
16
28
|
const pluginStateRef = useRef(pluginState)
|
|
17
29
|
pluginStateRef.current = pluginState
|
|
@@ -51,12 +63,10 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m
|
|
|
51
63
|
}, [isBaseMapReady, appState.mode])
|
|
52
64
|
|
|
53
65
|
useEffect(() => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
useEffect(() => datasetRegistry.attach(pluginState.mappedDatasets, pluginState.orderedDatasets),
|
|
59
|
-
[pluginState.mappedDatasets, pluginState.orderedDatasets])
|
|
66
|
+
datasetRegistry.attach(pluginState.mappedDatasets, pluginState.orderedDatasets)
|
|
67
|
+
eventBus.emit('datasets:changed')
|
|
68
|
+
},
|
|
69
|
+
[pluginState.mappedDatasets, pluginState.orderedDatasets])
|
|
60
70
|
|
|
61
71
|
useEffect(() => {
|
|
62
72
|
datasetRegistry.attachMapStyle(mapState.mapStyle)
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { render, act } from '@testing-library/react'
|
|
2
|
+
import { DatasetsInit } from './DatasetsInit.jsx'
|
|
3
|
+
import { datasetRegistry } from '../registry/datasetRegistry.js'
|
|
4
|
+
import { attachGlobalState } from '../registry/globalDataset.js'
|
|
5
|
+
import { loadLayerAdapter, layerAdapter } from '../adapters/loadLayerAdapter.js'
|
|
6
|
+
import { initialiseDatasets } from './initialiseDatasets.js'
|
|
7
|
+
import { EVENTS } from '../../../../src/config/events.js'
|
|
8
|
+
|
|
9
|
+
jest.mock('../registry/datasetRegistry.js', () => ({
|
|
10
|
+
datasetRegistry: {
|
|
11
|
+
attach: jest.fn(),
|
|
12
|
+
attachMapStyle: jest.fn(),
|
|
13
|
+
attachCreateDataset: jest.fn()
|
|
14
|
+
}
|
|
15
|
+
}))
|
|
16
|
+
jest.mock('../registry/globalDataset.js', () => ({
|
|
17
|
+
attachGlobalState: jest.fn()
|
|
18
|
+
}))
|
|
19
|
+
jest.mock('../adapters/loadLayerAdapter.js', () => ({
|
|
20
|
+
loadLayerAdapter: jest.fn(),
|
|
21
|
+
layerAdapter: {}
|
|
22
|
+
}))
|
|
23
|
+
jest.mock('./initialiseDatasets.js')
|
|
24
|
+
|
|
25
|
+
const makeEventBus = () => ({
|
|
26
|
+
emit: jest.fn(),
|
|
27
|
+
on: jest.fn(),
|
|
28
|
+
off: jest.fn()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const makeAdapter = () => ({
|
|
32
|
+
init: jest.fn().mockResolvedValue(undefined)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const makePluginState = (overrides = {}) => ({
|
|
36
|
+
dispatch: jest.fn(),
|
|
37
|
+
mappedDatasets: {},
|
|
38
|
+
orderedDatasets: [],
|
|
39
|
+
globals: { visible: true, opacity: 1 },
|
|
40
|
+
actionsArray: [],
|
|
41
|
+
...overrides
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const makeProps = (overrides = {}) => ({
|
|
45
|
+
pluginConfig: {
|
|
46
|
+
datasets: [{ id: 'roads', label: 'Roads', showInMenu: true }]
|
|
47
|
+
},
|
|
48
|
+
pluginState: makePluginState(),
|
|
49
|
+
appState: { mode: 'default' },
|
|
50
|
+
mapState: { mapStyle: { id: 'outdoor' } },
|
|
51
|
+
mapProvider: { isBaseMapReady: jest.fn().mockReturnValue(true) },
|
|
52
|
+
services: {
|
|
53
|
+
eventBus: makeEventBus(),
|
|
54
|
+
symbolRegistry: {},
|
|
55
|
+
patternRegistry: {}
|
|
56
|
+
},
|
|
57
|
+
...overrides
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('DatasetsInit', () => {
|
|
61
|
+
let adapter
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
jest.clearAllMocks()
|
|
65
|
+
adapter = makeAdapter()
|
|
66
|
+
loadLayerAdapter.mockResolvedValue(adapter)
|
|
67
|
+
initialiseDatasets.mockReturnValue({ remove: jest.fn() })
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('renders null', () => {
|
|
71
|
+
const { container } = render(<DatasetsInit {...makeProps()} />)
|
|
72
|
+
expect(container.firstChild).toBeNull()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// ─── hasMenu / panel removal ─────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
describe('menu panel management', () => {
|
|
78
|
+
it('removes the datasetsLayers panel when no datasets have showInMenu', async () => {
|
|
79
|
+
const props = makeProps({
|
|
80
|
+
pluginConfig: { datasets: [{ id: 'roads', label: 'Roads' }] }
|
|
81
|
+
})
|
|
82
|
+
await render(<DatasetsInit {...props} />)
|
|
83
|
+
expect(props.services.eventBus.emit).toHaveBeenCalledWith(
|
|
84
|
+
EVENTS.APP_REMOVE_PANEL, 'datasetsLayers'
|
|
85
|
+
)
|
|
86
|
+
expect(props.services.eventBus.emit).toHaveBeenCalledWith(
|
|
87
|
+
EVENTS.APP_TOGGLE_BUTTON_STATE, { id: 'datasetsLayers', prop: 'hidden', value: true }
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('does not remove the panel when at least one dataset has showInMenu', async () => {
|
|
92
|
+
const props = makeProps()
|
|
93
|
+
await render(<DatasetsInit {...props} />)
|
|
94
|
+
expect(props.services.eventBus.emit).not.toHaveBeenCalledWith(
|
|
95
|
+
EVENTS.APP_REMOVE_PANEL, 'datasetsLayers'
|
|
96
|
+
)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('removes the panel when hasMenu is explicitly false', async () => {
|
|
100
|
+
const props = makeProps({
|
|
101
|
+
pluginConfig: { hasMenu: false, datasets: [{ id: 'roads', label: 'Roads', showInMenu: true }] }
|
|
102
|
+
})
|
|
103
|
+
await render(<DatasetsInit {...props} />)
|
|
104
|
+
expect(props.services.eventBus.emit).toHaveBeenCalledWith(
|
|
105
|
+
EVENTS.APP_REMOVE_PANEL, 'datasetsLayers'
|
|
106
|
+
)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('includes datasets with sublayer showInMenu when checking for menu items', async () => {
|
|
110
|
+
const props = makeProps({
|
|
111
|
+
pluginConfig: {
|
|
112
|
+
datasets: [{
|
|
113
|
+
id: 'parent',
|
|
114
|
+
label: 'Parent',
|
|
115
|
+
sublayers: [{ id: 'child', showInMenu: true }]
|
|
116
|
+
}]
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
await render(<DatasetsInit {...props} />)
|
|
120
|
+
expect(props.services.eventBus.emit).not.toHaveBeenCalledWith(
|
|
121
|
+
EVENTS.APP_REMOVE_PANEL, 'datasetsLayers'
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// ─── initialisation ──────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
describe('initialisation', () => {
|
|
129
|
+
it('calls loadLayerAdapter and initialiseDatasets when base map is ready', async () => {
|
|
130
|
+
const props = makeProps()
|
|
131
|
+
await render(<DatasetsInit {...props} />)
|
|
132
|
+
expect(loadLayerAdapter).toHaveBeenCalledWith(
|
|
133
|
+
props.mapProvider, props.services.symbolRegistry, props.services.patternRegistry
|
|
134
|
+
)
|
|
135
|
+
expect(initialiseDatasets).toHaveBeenCalled()
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('does not initialise when base map is not ready', async () => {
|
|
139
|
+
const props = makeProps({
|
|
140
|
+
mapProvider: { isBaseMapReady: jest.fn().mockReturnValue(false) }
|
|
141
|
+
})
|
|
142
|
+
await render(<DatasetsInit {...props} />)
|
|
143
|
+
expect(loadLayerAdapter).not.toHaveBeenCalled()
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('does not initialise when mode is not in includeModes', async () => {
|
|
147
|
+
const props = makeProps({
|
|
148
|
+
pluginConfig: { datasets: [], includeModes: ['edit'] },
|
|
149
|
+
appState: { mode: 'default' }
|
|
150
|
+
})
|
|
151
|
+
await render(<DatasetsInit {...props} />)
|
|
152
|
+
expect(loadLayerAdapter).not.toHaveBeenCalled()
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('does not initialise when mode is in excludeModes', async () => {
|
|
156
|
+
const props = makeProps({
|
|
157
|
+
pluginConfig: { datasets: [], excludeModes: ['default'] },
|
|
158
|
+
appState: { mode: 'default' }
|
|
159
|
+
})
|
|
160
|
+
await render(<DatasetsInit {...props} />)
|
|
161
|
+
expect(loadLayerAdapter).not.toHaveBeenCalled()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('does not initialise twice when re-rendered', async () => {
|
|
165
|
+
const props = makeProps()
|
|
166
|
+
const { rerender } = render(<DatasetsInit {...props} />)
|
|
167
|
+
await act(async () => {})
|
|
168
|
+
rerender(<DatasetsInit {...props} mapState={{ mapStyle: { id: 'dark' } }} />)
|
|
169
|
+
await act(async () => {})
|
|
170
|
+
expect(loadLayerAdapter).toHaveBeenCalledTimes(1)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('skips init when datasetsInstanceRef is already populated (mode change)', async () => {
|
|
174
|
+
const props = makeProps()
|
|
175
|
+
const { rerender } = render(<DatasetsInit {...props} />)
|
|
176
|
+
await act(async () => {})
|
|
177
|
+
// Change appState.mode — that's in the effect deps, so the effect re-runs
|
|
178
|
+
await act(async () => {
|
|
179
|
+
rerender(<DatasetsInit {...props} appState={{ mode: 'edit' }} />)
|
|
180
|
+
})
|
|
181
|
+
// loadLayerAdapter should only be called once despite the re-run
|
|
182
|
+
expect(loadLayerAdapter).toHaveBeenCalledTimes(1)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('calls remove on cleanup when unmounted after init', async () => {
|
|
186
|
+
const removeFn = jest.fn()
|
|
187
|
+
initialiseDatasets.mockReturnValue({ remove: removeFn })
|
|
188
|
+
const props = makeProps()
|
|
189
|
+
const { unmount } = render(<DatasetsInit {...props} />)
|
|
190
|
+
await act(async () => {})
|
|
191
|
+
unmount()
|
|
192
|
+
expect(removeFn).toHaveBeenCalled()
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// ─── registry / map style effects ────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
describe('datasetRegistry attach effects', () => {
|
|
199
|
+
it('attaches mappedDatasets and orderedDatasets to the registry', async () => {
|
|
200
|
+
const mappedDatasets = { roads: { id: 'roads' } }
|
|
201
|
+
const orderedDatasets = ['roads']
|
|
202
|
+
const props = makeProps({
|
|
203
|
+
pluginState: makePluginState({ mappedDatasets, orderedDatasets })
|
|
204
|
+
})
|
|
205
|
+
await render(<DatasetsInit {...props} />)
|
|
206
|
+
expect(datasetRegistry.attach).toHaveBeenCalledWith(mappedDatasets, orderedDatasets)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('attaches the map style to the registry', async () => {
|
|
210
|
+
const props = makeProps()
|
|
211
|
+
await render(<DatasetsInit {...props} />)
|
|
212
|
+
expect(datasetRegistry.attachMapStyle).toHaveBeenCalledWith(props.mapState.mapStyle)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('calls layerAdapter.onMapStyleChange when available', async () => {
|
|
216
|
+
const onMapStyleChange = jest.fn()
|
|
217
|
+
Object.assign(layerAdapter, { onMapStyleChange })
|
|
218
|
+
const props = makeProps()
|
|
219
|
+
await render(<DatasetsInit {...props} />)
|
|
220
|
+
expect(onMapStyleChange).toHaveBeenCalled()
|
|
221
|
+
delete layerAdapter.onMapStyleChange
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
// ─── globals effect ──────────────────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
describe('globals effect', () => {
|
|
228
|
+
it('calls attachGlobalState with the current globals', async () => {
|
|
229
|
+
const globals = { visible: false, opacity: 0.5 }
|
|
230
|
+
const props = makeProps({ pluginState: makePluginState({ globals }) })
|
|
231
|
+
await render(<DatasetsInit {...props} />)
|
|
232
|
+
expect(attachGlobalState).toHaveBeenCalledWith(globals)
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
// ─── actionsArray effect ─────────────────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
describe('actionsArray effect', () => {
|
|
239
|
+
it('calls each queued adapter method and dispatches REMOVE_ADAPTER_ACTIONS', async () => {
|
|
240
|
+
const applyStyle = jest.fn()
|
|
241
|
+
Object.assign(layerAdapter, { applyStyle })
|
|
242
|
+
const actions = [{ method: 'applyStyle', parameters: ['roads', {}] }]
|
|
243
|
+
const dispatch = jest.fn()
|
|
244
|
+
const props = makeProps({
|
|
245
|
+
pluginState: makePluginState({ actionsArray: actions, dispatch })
|
|
246
|
+
})
|
|
247
|
+
await render(<DatasetsInit {...props} />)
|
|
248
|
+
expect(applyStyle).toHaveBeenCalledWith('roads', {})
|
|
249
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'REMOVE_ADAPTER_ACTIONS', payload: actions })
|
|
250
|
+
delete layerAdapter.applyStyle
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('skips dispatching when actionsArray is empty', async () => {
|
|
254
|
+
const dispatch = jest.fn()
|
|
255
|
+
const props = makeProps({ pluginState: makePluginState({ actionsArray: [], dispatch }) })
|
|
256
|
+
await render(<DatasetsInit {...props} />)
|
|
257
|
+
expect(dispatch).not.toHaveBeenCalledWith(
|
|
258
|
+
expect.objectContaining({ type: 'REMOVE_ADAPTER_ACTIONS' })
|
|
259
|
+
)
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
})
|
|
@@ -3,8 +3,7 @@ import { createDynamicSource } from '../fetch/createDynamicSource.js'
|
|
|
3
3
|
import { applyDatasetDefaults, datasetDefaults } from './defaults.js'
|
|
4
4
|
import { mappedDatasetsReducer } from '../reducers/mappedDatasetsReducer.js'
|
|
5
5
|
import { datasetRegistry } from '../registry/datasetRegistry.js'
|
|
6
|
-
import {
|
|
7
|
-
import { buildMenuState } from '../reducers/menuStateReducer.js'
|
|
6
|
+
import { attachMenuStateRef } from '../registry/isVisibleWhen.js'
|
|
8
7
|
import { datasetsToMenu } from '../reducers/datasetsToMenu.js'
|
|
9
8
|
|
|
10
9
|
export const initialiseDatasets = ({
|
|
@@ -30,8 +29,10 @@ export const initialiseDatasets = ({
|
|
|
30
29
|
datasetRegistry.attachCreateDataset(adapter.createDataset)
|
|
31
30
|
}
|
|
32
31
|
datasetRegistry.attach(mappedDatasets, orderedDatasets, mapStyle)
|
|
33
|
-
const menu =
|
|
34
|
-
|
|
32
|
+
const menu = datasetsToMenu({ datasets: processedDatasets })
|
|
33
|
+
|
|
34
|
+
eventBus.requestOnce('menu:state', attachMenuStateRef) // Request the menu state from the menu plugin
|
|
35
|
+
|
|
35
36
|
dispatch({ type: 'SET_MENU', payload: { menu } })
|
|
36
37
|
dispatch({ type: 'SET_DATASETS', payload: { mappedDatasets, orderedDatasets } })
|
|
37
38
|
|
|
@@ -58,9 +59,20 @@ export const initialiseDatasets = ({
|
|
|
58
59
|
|
|
59
60
|
eventBus.on(events.MAP_SIZE_CHANGE, onMapSizeChange)
|
|
60
61
|
|
|
62
|
+
// Add a listener for the datasets:registry event, which will be emitted
|
|
63
|
+
// whenever requested by other plugins, e.g. the menu and the mapKey plugin,
|
|
64
|
+
// so they can access the dataset registry.
|
|
65
|
+
const removeRegistryListener = eventBus.emitWhenRequested('datasets:registry', datasetRegistry)
|
|
66
|
+
|
|
67
|
+
eventBus.on('menu:changed', (menuState) => {
|
|
68
|
+
datasetRegistry.invalidateKeyItemsOnMenuStateChange(menuState)
|
|
69
|
+
adapter.applyGlobalVisibility()
|
|
70
|
+
})
|
|
71
|
+
|
|
61
72
|
return {
|
|
62
73
|
remove () {
|
|
63
74
|
eventBus.off(events.MAP_SIZE_CHANGE, onMapSizeChange)
|
|
75
|
+
removeRegistryListener()
|
|
64
76
|
|
|
65
77
|
// Clean up dynamic sources
|
|
66
78
|
dynamicSources.forEach(source => source.destroy())
|
|
@@ -8,6 +8,7 @@ jest.mock('../registry/datasetRegistry.js', () => ({
|
|
|
8
8
|
attach: jest.fn(),
|
|
9
9
|
attachCreateDataset: jest.fn(),
|
|
10
10
|
forEachDataset: jest.fn(),
|
|
11
|
+
invalidateKeyItemsOnMenuStateChange: jest.fn(),
|
|
11
12
|
_invalidateCache: jest.fn()
|
|
12
13
|
}
|
|
13
14
|
}))
|
|
@@ -20,6 +21,7 @@ const makeAdapter = (overrides = {}) => ({
|
|
|
20
21
|
onMapStyleChange: jest.fn(),
|
|
21
22
|
onMapSizeChange: jest.fn(),
|
|
22
23
|
setData: jest.fn(),
|
|
24
|
+
applyGlobalVisibility: jest.fn(),
|
|
23
25
|
destroy: jest.fn(),
|
|
24
26
|
createDataset: jest.fn(),
|
|
25
27
|
...overrides
|
|
@@ -29,6 +31,8 @@ const makeEventBus = () => {
|
|
|
29
31
|
const handlers = {}
|
|
30
32
|
return {
|
|
31
33
|
on: jest.fn((event, handler) => { handlers[event] = handler }),
|
|
34
|
+
requestOnce: jest.fn((event, handler) => { handlers[event] = handler }),
|
|
35
|
+
emitWhenRequested: jest.fn(() => () => {}),
|
|
32
36
|
off: jest.fn(),
|
|
33
37
|
emit: jest.fn(),
|
|
34
38
|
_handlers: handlers
|
|
@@ -253,3 +257,41 @@ describe('returned API', () => {
|
|
|
253
257
|
expect(() => instance.clearDatasetCache('unknown')).not.toThrow()
|
|
254
258
|
})
|
|
255
259
|
})
|
|
260
|
+
|
|
261
|
+
// ─── onUpdate callback ────────────────────────────────────────────────────────
|
|
262
|
+
|
|
263
|
+
describe('onUpdate callback', () => {
|
|
264
|
+
it('calls adapter.setData when the onUpdate callback fires', async () => {
|
|
265
|
+
const mockSource = { destroy: jest.fn(), refresh: jest.fn(), clear: jest.fn(), getFeatureCount: jest.fn() }
|
|
266
|
+
let capturedOnUpdate
|
|
267
|
+
createDynamicSource.mockImplementation(({ onUpdate }) => {
|
|
268
|
+
capturedOnUpdate = onUpdate
|
|
269
|
+
return mockSource
|
|
270
|
+
})
|
|
271
|
+
const mockDataset = { id: 'parcels', hasDynamicGeoJSON: true, dynamicGeoJSON: {} }
|
|
272
|
+
datasetRegistry.forEachDataset.mockImplementation(cb => cb(mockDataset))
|
|
273
|
+
|
|
274
|
+
const args = makeArgs()
|
|
275
|
+
initialiseDatasets(args)
|
|
276
|
+
await Promise.resolve()
|
|
277
|
+
|
|
278
|
+
const geojson = { type: 'FeatureCollection', features: [] }
|
|
279
|
+
capturedOnUpdate('parcels', geojson)
|
|
280
|
+
expect(args.adapter.setData).toHaveBeenCalledWith('parcels', geojson)
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
// ─── menu:changed handler ─────────────────────────────────────────────────────
|
|
285
|
+
|
|
286
|
+
describe('menu:changed handler', () => {
|
|
287
|
+
it('calls invalidateKeyItemsOnMenuStateChange and applyGlobalVisibility', () => {
|
|
288
|
+
const args = makeArgs()
|
|
289
|
+
initialiseDatasets(args)
|
|
290
|
+
|
|
291
|
+
const menuState = { items: [{ id: 'roads', visible: true }] }
|
|
292
|
+
args.eventBus._handlers['menu:changed'](menuState)
|
|
293
|
+
|
|
294
|
+
expect(datasetRegistry.invalidateKeyItemsOnMenuStateChange).toHaveBeenCalledWith(menuState)
|
|
295
|
+
expect(args.adapter.applyGlobalVisibility).toHaveBeenCalled()
|
|
296
|
+
})
|
|
297
|
+
})
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { initialState, actions } from './reducers/pluginState.js'
|
|
3
3
|
import { DatasetsInit } from './initialise/DatasetsInit.jsx'
|
|
4
4
|
import { LayersMenu } from './components/LayersMenu/LayersMenu.jsx'
|
|
5
|
-
import { Key } from './components/Key/Key.jsx'
|
|
6
5
|
import { addDataset } from './api/addDataset.js'
|
|
7
6
|
import { removeDataset } from './api/removeDataset.js'
|
|
8
7
|
import { setDatasetVisibility } from './api/setDatasetVisibility.js'
|
|
@@ -44,22 +43,6 @@ export const manifest = {
|
|
|
44
43
|
width: '280px'
|
|
45
44
|
},
|
|
46
45
|
render: LayersMenu
|
|
47
|
-
}, {
|
|
48
|
-
id: 'datasetsKey',
|
|
49
|
-
label: 'Key',
|
|
50
|
-
mobile: {
|
|
51
|
-
slot: 'drawer',
|
|
52
|
-
modal: true
|
|
53
|
-
},
|
|
54
|
-
tablet: {
|
|
55
|
-
slot: 'left-top',
|
|
56
|
-
width: '260px'
|
|
57
|
-
},
|
|
58
|
-
desktop: {
|
|
59
|
-
slot: 'left-top',
|
|
60
|
-
width: '280px'
|
|
61
|
-
},
|
|
62
|
-
render: Key
|
|
63
46
|
}],
|
|
64
47
|
|
|
65
48
|
buttons: [{
|
|
@@ -67,9 +50,6 @@ export const manifest = {
|
|
|
67
50
|
label: 'Layers',
|
|
68
51
|
panelId: 'datasetsLayers',
|
|
69
52
|
iconId: 'layers',
|
|
70
|
-
excludeWhen: ({ pluginConfig }) => !pluginConfig.menu && !pluginConfig.datasets.some(l =>
|
|
71
|
-
l.showInMenu || l.sublayers?.some(r => r.showInMenu)
|
|
72
|
-
),
|
|
73
53
|
mobile: {
|
|
74
54
|
slot: 'top-left',
|
|
75
55
|
showLabel: true
|
|
@@ -82,24 +62,6 @@ export const manifest = {
|
|
|
82
62
|
slot: 'top-left',
|
|
83
63
|
showLabel: true
|
|
84
64
|
}
|
|
85
|
-
}, {
|
|
86
|
-
id: 'datasetsKey',
|
|
87
|
-
label: 'Key',
|
|
88
|
-
panelId: 'datasetsKey',
|
|
89
|
-
iconId: 'key',
|
|
90
|
-
excludeWhen: ({ pluginConfig }) => !pluginConfig.datasets.some(l => l.showInKey),
|
|
91
|
-
mobile: {
|
|
92
|
-
slot: 'top-left',
|
|
93
|
-
showLabel: false
|
|
94
|
-
},
|
|
95
|
-
tablet: {
|
|
96
|
-
slot: 'top-left',
|
|
97
|
-
showLabel: true
|
|
98
|
-
},
|
|
99
|
-
desktop: {
|
|
100
|
-
slot: 'top-left',
|
|
101
|
-
showLabel: true
|
|
102
|
-
}
|
|
103
65
|
}],
|
|
104
66
|
|
|
105
67
|
icons: [{
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { manifest } from './manifest.js'
|
|
2
|
+
|
|
3
|
+
jest.mock('./reducers/pluginState.js', () => ({ initialState: {}, actions: {} }))
|
|
4
|
+
jest.mock('./initialise/DatasetsInit.jsx', () => ({ DatasetsInit: jest.fn() }))
|
|
5
|
+
jest.mock('./components/LayersMenu/LayersMenu.jsx', () => ({ LayersMenu: jest.fn() }))
|
|
6
|
+
jest.mock('./api/addDataset.js', () => ({ addDataset: jest.fn() }))
|
|
7
|
+
jest.mock('./api/removeDataset.js', () => ({ removeDataset: jest.fn() }))
|
|
8
|
+
jest.mock('./api/setDatasetVisibility.js', () => ({ setDatasetVisibility: jest.fn() }))
|
|
9
|
+
jest.mock('./api/setFeatureVisibility.js', () => ({ setFeatureVisibility: jest.fn() }))
|
|
10
|
+
jest.mock('./api/setStyle.js', () => ({ setStyle: jest.fn() }))
|
|
11
|
+
jest.mock('./api/getStyle.js', () => ({ getStyle: jest.fn() }))
|
|
12
|
+
jest.mock('./api/setOpacity.js', () => ({ setOpacity: jest.fn() }))
|
|
13
|
+
jest.mock('./api/getOpacity.js', () => ({ getOpacity: jest.fn() }))
|
|
14
|
+
jest.mock('./api/setData.js', () => ({ setData: jest.fn() }))
|
|
15
|
+
jest.mock('./api/setGlobals.js', () => ({ setGlobals: jest.fn() }))
|
|
16
|
+
|
|
17
|
+
describe('manifest', () => {
|
|
18
|
+
it('exports a manifest object', () => {
|
|
19
|
+
expect(manifest).toBeDefined()
|
|
20
|
+
expect(typeof manifest).toBe('object')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('has an InitComponent', () => {
|
|
24
|
+
expect(manifest.InitComponent).toBeDefined()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('has a reducer with initialState and actions', () => {
|
|
28
|
+
expect(manifest.reducer).toBeDefined()
|
|
29
|
+
expect(manifest.reducer.initialState).toBeDefined()
|
|
30
|
+
expect(manifest.reducer.actions).toBeDefined()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('has panels with a datasetsLayers panel', () => {
|
|
34
|
+
expect(Array.isArray(manifest.panels)).toBe(true)
|
|
35
|
+
expect(manifest.panels.find(p => p.id === 'datasetsLayers')).toBeDefined()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('has a buttons array', () => {
|
|
39
|
+
expect(Array.isArray(manifest.buttons)).toBe(true)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('has an api with all expected methods', () => {
|
|
43
|
+
expect(manifest.api.addDataset).toBeDefined()
|
|
44
|
+
expect(manifest.api.removeDataset).toBeDefined()
|
|
45
|
+
expect(manifest.api.setDatasetVisibility).toBeDefined()
|
|
46
|
+
expect(manifest.api.setOpacity).toBeDefined()
|
|
47
|
+
expect(manifest.api.getOpacity).toBeDefined()
|
|
48
|
+
})
|
|
49
|
+
})
|