@defra/interactive-map 0.0.39-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/api.md +4 -4
- package/docs/assets/images/slot-map.svg +61 -262
- package/docs/examples/draw-tools.mdx +3 -3
- package/docs/plugins/draw.md +80 -17
- 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/esm/im-map-styles-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.js +2 -3
- package/plugins/datasets/src/adapters/esri/esriLayerAdapter.test.js +175 -0
- package/plugins/datasets/src/adapters/esri/registry/esriDataset.js +1 -1
- package/plugins/datasets/src/adapters/esri/registry/esriDataset.test.js +55 -0
- package/plugins/datasets/src/adapters/loadLayerAdapter.js +1 -1
- package/plugins/datasets/src/adapters/loadLayerAdapter.test.js +61 -0
- package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +1 -3
- package/plugins/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +17 -3
- 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 -5
- 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 +15 -4
- package/plugins/datasets/src/registry/dataset.test.js +61 -0
- package/plugins/datasets/src/registry/datasetRegistry.js +12 -9
- package/plugins/datasets/src/registry/datasetRegistry.test.js +17 -5
- package/plugins/datasets/src/registry/globalDataset.js +0 -3
- 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 +12 -4
- package/plugins/draw/src/DrawInit.test.jsx +51 -10
- package/plugins/draw/src/adapters/maplibre/MaplibreDrawAdapter.js +92 -12
- 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/drawMode/pointerHandlers.js +1 -1
- package/plugins/draw/src/adapters/maplibre/modes/drawMode/pointerHandlers.test.js +1 -1
- 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 +5 -28
- 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 +4 -54
- 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 +11 -4
- 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.js +1 -0
- package/plugins/draw/src/adapters/openlayers/draw/DrawMode.test.js +27 -1
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.js +24 -4
- package/plugins/draw/src/adapters/openlayers/draw/drawInput.test.js +47 -3
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.js +1 -1
- package/plugins/draw/src/adapters/openlayers/edit/EditMode.test.js +32 -1
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.js +9 -5
- package/plugins/draw/src/adapters/openlayers/edit/keyboardHandler.test.js +34 -1
- 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 +12 -5
- package/plugins/draw/src/adapters/openlayers/edit/nudge.test.js +26 -3
- 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 +21 -14
- package/plugins/draw/src/events.test.js +15 -1
- 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/useCrossHairVisibility.js +12 -4
- package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +88 -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 +7 -5
- 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/sonar-project.properties +7 -1
- package/src/App/components/Actions/Actions.test.jsx +47 -0
- package/src/App/components/Attributions/Attributions.module.scss +10 -0
- package/src/App/components/Hints/Hints.jsx +7 -2
- package/src/App/components/Hints/Hints.test.jsx +13 -0
- package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +2 -3
- package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +3 -6
- package/src/App/components/{MoveControl/MoveControl.jsx → MoveControls/MoveControls.jsx} +10 -10
- package/src/App/components/{MoveControl/MoveControl.module.scss → MoveControls/MoveControls.module.scss} +60 -29
- package/src/App/components/{MoveControl/MoveControl.test.jsx → MoveControls/MoveControls.test.jsx} +48 -48
- 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/components/Viewport/Viewport.jsx +9 -1
- package/src/App/components/Viewport/Viewport.test.jsx +11 -0
- 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/initialiseApp.js +11 -8
- package/src/App/initialiseApp.test.js +15 -3
- package/src/App/layout/Layout.jsx +12 -11
- package/src/App/layout/Layout.test.jsx +2 -0
- package/src/App/layout/layout.module.scss +31 -17
- package/src/App/registry/keyboardShortcutRegistry.js +25 -13
- package/src/App/registry/keyboardShortcutRegistry.test.js +49 -32
- package/src/App/registry/pluginRegistry.js +2 -3
- package/src/App/registry/pluginRegistry.test.js +16 -3
- 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 +4 -2
- package/src/App/store/appDispatchMiddleware.js +1 -1
- package/src/App/store/appDispatchMiddleware.test.js +8 -0
- package/src/InteractiveMap/InteractiveMap.js +8 -2
- package/src/InteractiveMap/InteractiveMap.test.js +20 -3
- package/src/config/appConfig.js +17 -15
- package/src/config/appConfig.test.js +22 -22
- package/src/config/defaults.js +3 -1
- package/src/config/events.js +2 -0
- package/src/config/mergeConfig.js +7 -0
- package/src/config/mergeConfig.test.js +20 -0
- package/src/scss/main.scss +1 -1
- 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/test-utils.js +5 -0
- package/src/types.js +25 -7
- 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,117 @@
|
|
|
1
|
+
import { newPoint } from './newPoint.js'
|
|
2
|
+
|
|
3
|
+
const makeContext = (overrides = {}) => {
|
|
4
|
+
const dispatch = jest.fn()
|
|
5
|
+
const eventBus = { emit: jest.fn() }
|
|
6
|
+
const draw = { setSnapLayers: jest.fn(), changeMode: jest.fn(), isSnapEnabled: jest.fn(() => false) }
|
|
7
|
+
const context = {
|
|
8
|
+
appState: { layoutRefs: { viewportRef: { current: 'viewport' } }, interfaceType: 'mouse' },
|
|
9
|
+
appConfig: { id: 'app' },
|
|
10
|
+
pluginConfig: { snapLayers: ['pc-layer'] },
|
|
11
|
+
pluginState: { dispatch },
|
|
12
|
+
mapState: { crossHair: true },
|
|
13
|
+
mapProvider: { draw },
|
|
14
|
+
services: { eventBus },
|
|
15
|
+
...overrides
|
|
16
|
+
}
|
|
17
|
+
return { context, dispatch, eventBus, draw }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
beforeEach(() => jest.clearAllMocks())
|
|
21
|
+
|
|
22
|
+
describe('newPoint', () => {
|
|
23
|
+
test('does nothing when there is no draw instance', () => {
|
|
24
|
+
const { context, eventBus } = makeContext({ mapProvider: { draw: null } })
|
|
25
|
+
newPoint(context, 'f1')
|
|
26
|
+
expect(eventBus.emit).not.toHaveBeenCalled()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('starts point placement, splitting symbol-config keys out of properties', () => {
|
|
30
|
+
const { context, dispatch, eventBus, draw } = makeContext()
|
|
31
|
+
|
|
32
|
+
newPoint(context, 'f1', {
|
|
33
|
+
symbol: 'pin',
|
|
34
|
+
symbolBackgroundColor: { outdoor: '#1d70b8' },
|
|
35
|
+
properties: { name: 'x' },
|
|
36
|
+
extra: 'opt'
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
expect(eventBus.emit).toHaveBeenCalledWith('draw:started', { mode: 'draw_point' })
|
|
40
|
+
|
|
41
|
+
expect(draw.changeMode).toHaveBeenCalledWith('draw_point', expect.objectContaining({
|
|
42
|
+
container: 'viewport',
|
|
43
|
+
addVertexButtonId: 'app-draw-add-point',
|
|
44
|
+
interfaceType: 'mouse',
|
|
45
|
+
crossHair: true,
|
|
46
|
+
featureId: 'f1',
|
|
47
|
+
extra: 'opt',
|
|
48
|
+
properties: { name: 'x', symbol: 'pin', symbolBackgroundColor: { outdoor: '#1d70b8' } }
|
|
49
|
+
}))
|
|
50
|
+
|
|
51
|
+
const opts = draw.changeMode.mock.calls[0][1]
|
|
52
|
+
expect(opts.getSnapEnabled()).toBe(false)
|
|
53
|
+
expect(draw.isSnapEnabled).toHaveBeenCalled()
|
|
54
|
+
// Symbol keys are pulled off modeOptions entirely, not just copied into properties.
|
|
55
|
+
expect(opts).not.toHaveProperty('symbol')
|
|
56
|
+
expect(opts).not.toHaveProperty('symbolBackgroundColor')
|
|
57
|
+
|
|
58
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_MODE', payload: 'draw_point' })
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('carries every recognised symbol-config key through to properties', () => {
|
|
62
|
+
const { context, draw } = makeContext()
|
|
63
|
+
const symbolStyle = {
|
|
64
|
+
symbol: 'pin',
|
|
65
|
+
symbolSvgContent: '<path/>',
|
|
66
|
+
symbolBackgroundColor: '#fff',
|
|
67
|
+
symbolForegroundColor: '#000',
|
|
68
|
+
symbolHaloWidth: 2,
|
|
69
|
+
symbolGraphic: 'g',
|
|
70
|
+
symbolViewBox: '0 0 10 10',
|
|
71
|
+
symbolAnchor: [0.5, 0.9]
|
|
72
|
+
}
|
|
73
|
+
newPoint(context, 'f1', symbolStyle)
|
|
74
|
+
expect(draw.changeMode.mock.calls[0][1].properties).toEqual(symbolStyle)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('omits symbol-config keys entirely when none are supplied', () => {
|
|
78
|
+
const { context, draw } = makeContext()
|
|
79
|
+
newPoint(context, 'f1')
|
|
80
|
+
expect(draw.changeMode.mock.calls[0][1].properties).toEqual({})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('prefers explicit option snapLayers and flags them', () => {
|
|
84
|
+
const { context, draw, dispatch } = makeContext()
|
|
85
|
+
newPoint(context, 'f1', { snapLayers: ['opt'] })
|
|
86
|
+
expect(draw.setSnapLayers).toHaveBeenCalledWith(['opt'])
|
|
87
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_HAS_SNAP_LAYERS', payload: true })
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('falls back to the plugin config snapLayers', () => {
|
|
91
|
+
const { context, draw } = makeContext()
|
|
92
|
+
newPoint(context, 'f1')
|
|
93
|
+
expect(draw.setSnapLayers).toHaveBeenCalledWith(['pc-layer'])
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('falls back to null when neither option nor config is set', () => {
|
|
97
|
+
const { context, draw, dispatch } = makeContext({ pluginConfig: {} })
|
|
98
|
+
newPoint(context, 'f1')
|
|
99
|
+
expect(draw.setSnapLayers).toHaveBeenCalledWith(null)
|
|
100
|
+
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_HAS_SNAP_LAYERS', payload: false })
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('stores a per-call onGeometryChange validator without leaking it into mode options', () => {
|
|
104
|
+
const { context, draw } = makeContext()
|
|
105
|
+
const onGeometryChange = jest.fn()
|
|
106
|
+
newPoint(context, 'f1', { onGeometryChange })
|
|
107
|
+
expect(draw._geometryValidator).toBe(onGeometryChange)
|
|
108
|
+
expect(draw.changeMode.mock.calls[0][1]).not.toHaveProperty('onGeometryChange')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('falls back to the plugin-level onGeometryChange validator', () => {
|
|
112
|
+
const pluginOnGeometryChange = jest.fn()
|
|
113
|
+
const { context, draw } = makeContext({ pluginConfig: { onGeometryChange: pluginOnGeometryChange } })
|
|
114
|
+
newPoint(context, 'f1')
|
|
115
|
+
expect(draw._geometryValidator).toBe(pluginOnGeometryChange)
|
|
116
|
+
})
|
|
117
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { flattenStyleProperties } from '../utils/flattenStyleProperties.js'
|
|
2
|
+
import { logger } from '../../../../src/services/logger.js'
|
|
3
|
+
|
|
4
|
+
const STYLE_KEYS = new Set(['stroke', 'fill', 'strokeWidth'])
|
|
5
|
+
|
|
6
|
+
// A partial patch — unlike addFeature.js building a whole new feature from scratch,
|
|
7
|
+
// flattenStyleProperties would otherwise write stroke/fill/strokeWidth as explicit
|
|
8
|
+
// `undefined` for any of the three the caller didn't mention, blanking out whatever that
|
|
9
|
+
// feature already had.
|
|
10
|
+
const definedOnly = (properties) => {
|
|
11
|
+
const result = {}
|
|
12
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
13
|
+
if (value !== undefined) {
|
|
14
|
+
result[key] = value
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return result
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const setStyle = ({ mapProvider, services }, featureId, styleChanges = {}) => {
|
|
21
|
+
const { draw } = mapProvider
|
|
22
|
+
const { eventBus } = services
|
|
23
|
+
|
|
24
|
+
if (!draw) {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!draw.get(featureId)) {
|
|
29
|
+
logger.warn('setStyle: no feature found for id', featureId)
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const styleProps = {}
|
|
34
|
+
const symbolProps = {}
|
|
35
|
+
for (const [key, value] of Object.entries(styleChanges)) {
|
|
36
|
+
(STYLE_KEYS.has(key) ? styleProps : symbolProps)[key] = value
|
|
37
|
+
}
|
|
38
|
+
const properties = definedOnly({ ...flattenStyleProperties(styleProps), ...symbolProps })
|
|
39
|
+
|
|
40
|
+
draw.setStyle(featureId, properties)
|
|
41
|
+
eventBus.emit('draw:stylechange', { featureId, properties })
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { setStyle } from './setStyle.js'
|
|
2
|
+
import { logger } from '../../../../src/services/logger.js'
|
|
3
|
+
|
|
4
|
+
jest.mock('../../../../src/services/logger.js', () => ({
|
|
5
|
+
logger: { warn: jest.fn(), error: jest.fn() }
|
|
6
|
+
}))
|
|
7
|
+
|
|
8
|
+
const setup = (draw) => {
|
|
9
|
+
const eventBus = { emit: jest.fn() }
|
|
10
|
+
return { context: { mapProvider: { draw }, services: { eventBus } }, eventBus }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
beforeEach(() => jest.clearAllMocks())
|
|
14
|
+
|
|
15
|
+
describe('setStyle', () => {
|
|
16
|
+
test('does nothing when there is no draw instance', () => {
|
|
17
|
+
const { context, eventBus } = setup(undefined)
|
|
18
|
+
expect(setStyle(context, 'a', { stroke: 'red' })).toBe(false)
|
|
19
|
+
expect(eventBus.emit).not.toHaveBeenCalled()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('warns and does nothing for an id with no existing feature', () => {
|
|
23
|
+
const draw = { get: jest.fn(() => null), setStyle: jest.fn() }
|
|
24
|
+
const { context, eventBus } = setup(draw)
|
|
25
|
+
expect(setStyle(context, 'missing', { stroke: 'red' })).toBe(false)
|
|
26
|
+
expect(draw.setStyle).not.toHaveBeenCalled()
|
|
27
|
+
expect(eventBus.emit).not.toHaveBeenCalled()
|
|
28
|
+
expect(logger.warn).toHaveBeenCalledWith('setStyle: no feature found for id', 'missing')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('flattens stroke/fill/strokeWidth and forwards symbol keys as-is', () => {
|
|
32
|
+
const draw = { get: jest.fn(() => ({ id: 'a' })), setStyle: jest.fn() }
|
|
33
|
+
const { context, eventBus } = setup(draw)
|
|
34
|
+
|
|
35
|
+
const result = setStyle(context, 'a', {
|
|
36
|
+
stroke: { outdoor: '#1d70b8', dark: '#4c9ed9' },
|
|
37
|
+
symbol: 'square',
|
|
38
|
+
symbolBackgroundColor: { outdoor: '#ca3535', dark: '#ffffff' }
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const expected = {
|
|
42
|
+
stroke: '#1d70b8',
|
|
43
|
+
strokeOutdoor: '#1d70b8',
|
|
44
|
+
strokeDark: '#4c9ed9',
|
|
45
|
+
symbol: 'square',
|
|
46
|
+
symbolBackgroundColor: { outdoor: '#ca3535', dark: '#ffffff' }
|
|
47
|
+
}
|
|
48
|
+
expect(draw.setStyle).toHaveBeenCalledWith('a', expected)
|
|
49
|
+
expect(eventBus.emit).toHaveBeenCalledWith('draw:stylechange', { featureId: 'a', properties: expected })
|
|
50
|
+
expect(result).toBe(true)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// A partial patch — omitting fill/strokeWidth must leave whatever the feature already has
|
|
54
|
+
// alone, not blank them out to undefined.
|
|
55
|
+
test('only includes the style keys actually supplied, dropping the unset ones', () => {
|
|
56
|
+
const draw = { get: jest.fn(() => ({ id: 'a' })), setStyle: jest.fn() }
|
|
57
|
+
const { context } = setup(draw)
|
|
58
|
+
setStyle(context, 'a', { stroke: '#e6c700' })
|
|
59
|
+
expect(draw.setStyle).toHaveBeenCalledWith('a', { stroke: '#e6c700' })
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('drops keys explicitly passed as undefined, from both style and symbol props', () => {
|
|
63
|
+
const draw = { get: jest.fn(() => ({ id: 'a' })), setStyle: jest.fn() }
|
|
64
|
+
const { context } = setup(draw)
|
|
65
|
+
setStyle(context, 'a', { stroke: 'red', fill: undefined, symbol: 'pin', symbolBackgroundColor: undefined })
|
|
66
|
+
expect(draw.setStyle).toHaveBeenCalledWith('a', { stroke: 'red', symbol: 'pin' })
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('defaults styleChanges to {} and still resolves cleanly', () => {
|
|
70
|
+
const draw = { get: jest.fn(() => ({ id: 'a' })), setStyle: jest.fn() }
|
|
71
|
+
const { context } = setup(draw)
|
|
72
|
+
setStyle(context, 'a')
|
|
73
|
+
expect(draw.setStyle).toHaveBeenCalledWith('a', {})
|
|
74
|
+
})
|
|
75
|
+
})
|
|
@@ -9,10 +9,17 @@ import { ADAPTER_EVENTS } from './adapterEvents.js'
|
|
|
9
9
|
import { validateGeometry } from './validation/validateGeometry.js'
|
|
10
10
|
import { MIN_VERTICES_REASONS } from './validation/rules.js'
|
|
11
11
|
import { MAP_SIZE_SCALES } from './defaults.js'
|
|
12
|
+
import { stripInternalProperties } from './utils/stripInternalProperties.js'
|
|
12
13
|
|
|
13
14
|
const EDIT_VERTEX_MODE = 'edit_vertex'
|
|
14
15
|
const GEOMETRY_INVALID_EVENT = 'draw:geometryinvalid'
|
|
15
16
|
|
|
17
|
+
// edit_point behaves like edit_vertex for the two checks below (restore-on-cancel, live
|
|
18
|
+
// invalid-stroke gating) — a relocate session, just with one coordinate instead of a ring.
|
|
19
|
+
// enterEditVertexMode (below) stays edit_vertex-only — it's only ever reached for a shape
|
|
20
|
+
// that finished invalid, and Point geometry has no such failure mode (see rules.js).
|
|
21
|
+
const EDIT_MODES = new Set([EDIT_VERTEX_MODE, 'edit_point'])
|
|
22
|
+
|
|
16
23
|
// A shape that simply hasn't reached its minimum vertex count yet isn't a mistake —
|
|
17
24
|
// it's the normal, expected state of "still being drawn" — so it's excluded below
|
|
18
25
|
// even though it's a genuine SOFT_RULES failure like any other.
|
|
@@ -29,8 +36,8 @@ const createGeometryInvalidEmitter = (eventBus, hints) => (payload) => {
|
|
|
29
36
|
if (payload.reason && !isIncompleteShape(payload.reason)) { hints.show(payload.reason) }
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
// Claims
|
|
33
|
-
// mapProvider.activeMoveTarget contract (
|
|
39
|
+
// Claims MoveControls' D-pad for the currently selected vertex — see the generic
|
|
40
|
+
// mapProvider.activeMoveTarget contract (MoveControls.jsx). Any plugin could use
|
|
34
41
|
// this slot; the draw plugin is just its first consumer.
|
|
35
42
|
const buildVertexMoveTarget = (draw) => ({
|
|
36
43
|
move: (dx, dy, isLargeStep) => draw.nudgeSelectedVertex(dx, dy, isLargeStep),
|
|
@@ -38,7 +45,7 @@ const buildVertexMoveTarget = (draw) => ({
|
|
|
38
45
|
})
|
|
39
46
|
|
|
40
47
|
// Vertex-selection handlers: sync pluginState.selectedVertexIndex, and claim/release
|
|
41
|
-
//
|
|
48
|
+
// MoveControls' D-pad via buildVertexMoveTarget above.
|
|
42
49
|
const createVertexSelectionHandlers = ({ draw, pluginState, mapProvider, eventBus }) => ({
|
|
43
50
|
onVertexSelection: (e) => {
|
|
44
51
|
pluginState.dispatch({ type: 'SET_SELECTED_VERTEX_INDEX', payload: e })
|
|
@@ -86,10 +93,10 @@ function createHandlers ({ appState, appConfig, mapState, pluginState, mapProvid
|
|
|
86
93
|
handleDone: () => { draw.done() },
|
|
87
94
|
handleCancel: () => {
|
|
88
95
|
const mode = draw.getMode()
|
|
89
|
-
if (mode
|
|
96
|
+
if (EDIT_MODES.has(mode) && tempFeature?.id) { draw.add(feature) }
|
|
90
97
|
pendingCreateId = null
|
|
91
98
|
draw.cancel(); resetState()
|
|
92
|
-
eventBus.emit('draw:cancelled', feature)
|
|
99
|
+
eventBus.emit('draw:cancelled', stripInternalProperties(feature))
|
|
93
100
|
},
|
|
94
101
|
handleUndo: () => draw.undo(),
|
|
95
102
|
handleDeleteVertex: () => draw.deleteVertex(),
|
|
@@ -104,11 +111,11 @@ function createHandlers ({ appState, appConfig, mapState, pluginState, mapProvid
|
|
|
104
111
|
const { valid, reason } = validateGeometry(f, { phase: 'create', mode: draw.getMode() }, { onGeometryChange: draw._geometryValidator })
|
|
105
112
|
if (!valid) {
|
|
106
113
|
pendingCreateId = f.id
|
|
107
|
-
emitGeometryInvalid({ feature: f, reason, phase: 'create', mode: EDIT_VERTEX_MODE })
|
|
114
|
+
emitGeometryInvalid({ feature: stripInternalProperties(f), reason, phase: 'create', mode: EDIT_VERTEX_MODE })
|
|
108
115
|
setTimeout(() => enterEditVertexMode({ draw, appState, appConfig, mapState, dispatch }, f.id), 0)
|
|
109
116
|
return
|
|
110
117
|
}
|
|
111
|
-
resetState(); setTimeout(() => draw.changeMode('disabled'), 0); eventBus.emit('draw:created', f)
|
|
118
|
+
resetState(); setTimeout(() => draw.changeMode('disabled'), 0); eventBus.emit('draw:created', stripInternalProperties(f))
|
|
112
119
|
},
|
|
113
120
|
onEditFinish: (f) => {
|
|
114
121
|
resetState()
|
|
@@ -116,15 +123,15 @@ function createHandlers ({ appState, appConfig, mapState, pluginState, mapProvid
|
|
|
116
123
|
// A shape that was drawn-then-fixed reports as a creation, not an edit.
|
|
117
124
|
if (pendingCreateId && f.id === pendingCreateId) {
|
|
118
125
|
pendingCreateId = null
|
|
119
|
-
eventBus.emit('draw:created', f)
|
|
126
|
+
eventBus.emit('draw:created', stripInternalProperties(f))
|
|
120
127
|
} else {
|
|
121
|
-
eventBus.emit('draw:edited', f)
|
|
128
|
+
eventBus.emit('draw:edited', stripInternalProperties(f))
|
|
122
129
|
}
|
|
123
130
|
},
|
|
124
131
|
onCancel: () => {},
|
|
125
132
|
...createVertexSelectionHandlers({ draw, pluginState, mapProvider, eventBus }),
|
|
126
133
|
onUndoChange: (l) => { pluginState.dispatch({ type: 'SET_UNDO_STACK_LENGTH', payload: l }) },
|
|
127
|
-
onUpdate: (f) => { eventBus.emit('draw:updated', f) },
|
|
134
|
+
onUpdate: (f) => { eventBus.emit('draw:updated', stripInternalProperties(f)) },
|
|
128
135
|
onGeometryChange: (e) => {
|
|
129
136
|
// Only commit-level changes (commit-add/move/insert/delete) carry a `phase`.
|
|
130
137
|
// Preview events (e.g. split's live preview) have none and are ignored.
|
|
@@ -141,17 +148,17 @@ function createHandlers ({ appState, appConfig, mapState, pluginState, mapProvid
|
|
|
141
148
|
// The invalid stroke is committed-validity-driven in edit mode only; in draw
|
|
142
149
|
// mode the adapters drive it live from the displayed geometry (placed
|
|
143
150
|
// vertices + cursor) on every rubber-band move.
|
|
144
|
-
if (mode
|
|
151
|
+
if (EDIT_MODES.has(mode)) {
|
|
145
152
|
draw.setInvalid?.(!valid)
|
|
146
153
|
}
|
|
147
154
|
if (!valid) {
|
|
148
|
-
emitGeometryInvalid({ reason, ...context, feature: e.feature })
|
|
155
|
+
emitGeometryInvalid({ reason, ...context, feature: stripInternalProperties(e.feature) })
|
|
149
156
|
}
|
|
150
157
|
},
|
|
151
158
|
// A vertex placement was rejected (hard rule or user callback veto). Surface it
|
|
152
159
|
// on the public bus with phase 'place', and as a hint toast, so the reason is visible.
|
|
153
160
|
onPlacementBlocked: (e) => {
|
|
154
|
-
emitGeometryInvalid(e)
|
|
161
|
+
emitGeometryInvalid({ ...e, feature: stripInternalProperties(e.feature) })
|
|
155
162
|
},
|
|
156
163
|
// Live (mid-drag) validity flip while editing — the displayed shape is exactly
|
|
157
164
|
// what Done finishes there, so it gates the Done button in real time. Emitted
|
|
@@ -226,7 +233,7 @@ export function attachEvents ({ appState, appConfig, mapState, pluginState, mapP
|
|
|
226
233
|
const resetState = () => {
|
|
227
234
|
pluginState.dispatch({ type: 'SET_MODE', payload: null })
|
|
228
235
|
pluginState.dispatch({ type: 'SET_FEATURE', payload: { feature: null, tempFeature: null } })
|
|
229
|
-
// Release
|
|
236
|
+
// Release MoveControls' D-pad back to panning the map whenever a draw/edit
|
|
230
237
|
// session ends — a stale claim here would silently hijack it for good.
|
|
231
238
|
mapProvider.activeMoveTarget = null
|
|
232
239
|
}
|
|
@@ -88,6 +88,13 @@ describe('button handlers', () => {
|
|
|
88
88
|
expect(mapProvider.activeMoveTarget).toBeNull()
|
|
89
89
|
})
|
|
90
90
|
|
|
91
|
+
test('cancel re-adds the feature when cancelling a point edit too', () => {
|
|
92
|
+
const { buttonConfig, draw } = setup()
|
|
93
|
+
draw.getMode.mockReturnValue('edit_point')
|
|
94
|
+
buttonConfig.drawCancel.onClick()
|
|
95
|
+
expect(draw.add).toHaveBeenCalledWith({ id: 'F' })
|
|
96
|
+
})
|
|
97
|
+
|
|
91
98
|
test('cancel does not re-add outside a vertex edit', () => {
|
|
92
99
|
const { buttonConfig, draw } = setup()
|
|
93
100
|
draw.getMode.mockReturnValue('draw_polygon')
|
|
@@ -172,7 +179,7 @@ describe('draw event handlers', () => {
|
|
|
172
179
|
expect(() => drawHandler(draw, 'cancel')()).not.toThrow()
|
|
173
180
|
})
|
|
174
181
|
|
|
175
|
-
test('vertexselection dispatches, emits, and claims mapProvider.activeMoveTarget for
|
|
182
|
+
test('vertexselection dispatches, emits, and claims mapProvider.activeMoveTarget for MoveControls', () => {
|
|
176
183
|
const { draw, dispatch, eventBus, mapProvider } = setup()
|
|
177
184
|
drawHandler(draw, 'vertexselection')({ index: 2 })
|
|
178
185
|
expect(dispatch).toHaveBeenCalledWith({ type: 'SET_SELECTED_VERTEX_INDEX', payload: { index: 2 } })
|
|
@@ -308,6 +315,13 @@ describe('geometrychange validation', () => {
|
|
|
308
315
|
expect(draw.setInvalid).toHaveBeenCalledWith(true)
|
|
309
316
|
})
|
|
310
317
|
|
|
318
|
+
test('drives the invalid stroke from committed validity in a point edit too', () => {
|
|
319
|
+
const { draw } = setup()
|
|
320
|
+
draw.getMode.mockReturnValue('edit_point')
|
|
321
|
+
drawHandler(draw, 'geometrychange')({ feature: squareFeature, phase: 'commit-move', vertexIndex: 0 })
|
|
322
|
+
expect(draw.setInvalid).toHaveBeenCalledWith(false)
|
|
323
|
+
})
|
|
324
|
+
|
|
311
325
|
test('a live validity flip (edit drag) drives the Done gate', () => {
|
|
312
326
|
const { draw, dispatch } = setup()
|
|
313
327
|
drawHandler(draw, 'validitychange')({ valid: false, reason: 'Shape must not intersect itself' })
|
|
@@ -2,8 +2,10 @@ import { initialState, actions } from './reducer.js'
|
|
|
2
2
|
import { DrawInit } from './DrawInit.jsx'
|
|
3
3
|
import { newPolygon } from './api/newPolygon.js'
|
|
4
4
|
import { newLine } from './api/newLine.js'
|
|
5
|
+
import { newPoint } from './api/newPoint.js'
|
|
5
6
|
import { editFeature } from './api/editFeature.js'
|
|
6
7
|
import { addFeature } from './api/addFeature.js'
|
|
8
|
+
import { setStyle } from './api/setStyle.js'
|
|
7
9
|
import { deleteFeature } from './api/deleteFeature.js'
|
|
8
10
|
import { split } from './api/split.js'
|
|
9
11
|
import { merge } from './api/merge.js'
|
|
@@ -11,6 +13,11 @@ import { isMac } from '../../../src/utils/isMac.js'
|
|
|
11
13
|
|
|
12
14
|
const DRAW_ACTIONS_SLOT = 'top-middle'
|
|
13
15
|
|
|
16
|
+
// edit_point behaves like edit_vertex for Done/Menu/Undo, but stays out of drawDeletePoint and mergeShapes (neither applies to a single coordinate).
|
|
17
|
+
const EDIT_MODES = new Set(['edit_vertex', 'edit_point'])
|
|
18
|
+
// .has(), not spread — [...aSet] can silently misbehave under a loose-mode Babel build (see OLDrawManager.js's emit()).
|
|
19
|
+
const isEditMode = (mode) => EDIT_MODES.has(mode)
|
|
20
|
+
|
|
14
21
|
// Show the platform-appropriate undo modifier (⌘ on macOS, Ctrl elsewhere).
|
|
15
22
|
const undoCommand = isMac() ? '<kbd>Command</kbd> + <kbd>Z</kbd>' : '<kbd>Ctrl</kbd> + <kbd>Z</kbd>'
|
|
16
23
|
|
|
@@ -43,7 +50,7 @@ export const manifest = {
|
|
|
43
50
|
variant: 'primary',
|
|
44
51
|
exclusiveSlot: true,
|
|
45
52
|
hiddenWhen: ({ appState, pluginState }) =>
|
|
46
|
-
!['draw_polygon', 'draw_line'].includes(pluginState.mode) || appState.interfaceType !== 'touch',
|
|
53
|
+
!['draw_polygon', 'draw_line', 'draw_point'].includes(pluginState.mode) || appState.interfaceType !== 'touch',
|
|
47
54
|
// Disabled while placing at the crosshair would be vetoed (validatePlacement) —
|
|
48
55
|
// driven live so the button never looks active when a tap would do nothing.
|
|
49
56
|
enableWhen: ({ pluginState }) => pluginState.canAddPoint,
|
|
@@ -54,27 +61,30 @@ export const manifest = {
|
|
|
54
61
|
label: 'Done',
|
|
55
62
|
variant: 'primary',
|
|
56
63
|
exclusiveSlot: true,
|
|
57
|
-
hiddenWhen: ({ pluginState }) => !['draw_polygon', 'draw_line'
|
|
64
|
+
hiddenWhen: ({ pluginState }) => !(['draw_polygon', 'draw_line'].includes(pluginState.mode) || isEditMode(pluginState.mode)),
|
|
58
65
|
enableWhen: ({ pluginState }) => {
|
|
59
66
|
const { mode, geometryValid } = pluginState
|
|
60
67
|
// Min-vertices, area and self-intersection are all enforced by the validation
|
|
61
68
|
// rules via geometryValid, so the gate is simply "is the geometry valid now".
|
|
62
|
-
return ['draw_polygon', 'draw_line'
|
|
69
|
+
return (['draw_polygon', 'draw_line'].includes(mode) || isEditMode(mode)) && geometryValid
|
|
63
70
|
},
|
|
64
71
|
...createButtonSlots(true)
|
|
65
72
|
},
|
|
66
73
|
{
|
|
67
74
|
id: 'drawMenu',
|
|
68
|
-
label: ({ pluginState }) => pluginState.mode
|
|
75
|
+
label: ({ pluginState }) => isEditMode(pluginState.mode) ? 'Edit actions' : 'Draw actions',
|
|
69
76
|
iconId: 'menu',
|
|
70
77
|
exclusiveSlot: true,
|
|
71
|
-
|
|
78
|
+
// draw_point belongs here too — it has no Undo (nothing to undo before a single-click
|
|
79
|
+
// commit, gated separately below) and no delete-vertex, but it DOES support snapping,
|
|
80
|
+
// and the Snap toggle is a menuItem living inside this same button.
|
|
81
|
+
hiddenWhen: ({ pluginState }) => !(['draw_polygon', 'draw_line', 'draw_point'].includes(pluginState.mode) || isEditMode(pluginState.mode)),
|
|
72
82
|
menuItems: [
|
|
73
83
|
{
|
|
74
84
|
id: 'drawUndo',
|
|
75
85
|
label: 'Undo',
|
|
76
86
|
iconId: 'undo',
|
|
77
|
-
hiddenWhen: ({ pluginState }) => !['draw_polygon', 'draw_line'
|
|
87
|
+
hiddenWhen: ({ pluginState }) => !(['draw_polygon', 'draw_line'].includes(pluginState.mode) || isEditMode(pluginState.mode)),
|
|
78
88
|
enableWhen: ({ pluginState }) => {
|
|
79
89
|
if (['draw_polygon', 'draw_line'].includes(pluginState.mode)) {
|
|
80
90
|
return pluginState.numVertices > 0
|
|
@@ -161,8 +171,10 @@ export const manifest = {
|
|
|
161
171
|
api: {
|
|
162
172
|
newPolygon,
|
|
163
173
|
newLine,
|
|
174
|
+
newPoint,
|
|
164
175
|
editFeature,
|
|
165
176
|
addFeature,
|
|
177
|
+
setStyle,
|
|
166
178
|
deleteFeature,
|
|
167
179
|
split,
|
|
168
180
|
merge
|
|
@@ -9,7 +9,7 @@ describe('manifest structure', () => {
|
|
|
9
9
|
expect(manifest.reducer).toHaveProperty('actions')
|
|
10
10
|
expect(manifest.InitComponent).toBeDefined()
|
|
11
11
|
expect(Object.keys(manifest.api)).toEqual(expect.arrayContaining([
|
|
12
|
-
'newPolygon', 'newLine', 'editFeature', 'addFeature', 'deleteFeature', 'split', 'merge'
|
|
12
|
+
'newPolygon', 'newLine', 'newPoint', 'editFeature', 'addFeature', 'setStyle', 'deleteFeature', 'split', 'merge'
|
|
13
13
|
]))
|
|
14
14
|
})
|
|
15
15
|
})
|
|
@@ -44,12 +44,14 @@ describe('drawDone', () => {
|
|
|
44
44
|
test('is hidden outside draw/edit modes', () => {
|
|
45
45
|
expect(btn().hiddenWhen({ pluginState: { mode: null } })).toBe(true)
|
|
46
46
|
expect(btn().hiddenWhen({ pluginState: { mode: 'draw_polygon' } })).toBe(false)
|
|
47
|
+
expect(btn().hiddenWhen({ pluginState: { mode: 'edit_point' } })).toBe(false)
|
|
47
48
|
})
|
|
48
49
|
|
|
49
50
|
test('enables in a draw/edit mode only when the geometry is valid', () => {
|
|
50
51
|
expect(btn().enableWhen({ pluginState: { mode: 'draw_polygon', geometryValid: true } })).toBe(true)
|
|
51
52
|
expect(btn().enableWhen({ pluginState: { mode: 'draw_line', geometryValid: true } })).toBe(true)
|
|
52
53
|
expect(btn().enableWhen({ pluginState: { mode: 'edit_vertex', geometryValid: true } })).toBe(true)
|
|
54
|
+
expect(btn().enableWhen({ pluginState: { mode: 'edit_point', geometryValid: true } })).toBe(true)
|
|
53
55
|
expect(btn().enableWhen({ pluginState: { mode: 'disabled', geometryValid: true } })).toBe(false)
|
|
54
56
|
})
|
|
55
57
|
|
|
@@ -57,6 +59,7 @@ describe('drawDone', () => {
|
|
|
57
59
|
expect(btn().enableWhen({ pluginState: { mode: 'draw_polygon', geometryValid: false } })).toBe(false)
|
|
58
60
|
expect(btn().enableWhen({ pluginState: { mode: 'draw_line', geometryValid: false } })).toBe(false)
|
|
59
61
|
expect(btn().enableWhen({ pluginState: { mode: 'edit_vertex', geometryValid: false } })).toBe(false)
|
|
62
|
+
expect(btn().enableWhen({ pluginState: { mode: 'edit_point', geometryValid: false } })).toBe(false)
|
|
60
63
|
})
|
|
61
64
|
})
|
|
62
65
|
|
|
@@ -66,12 +69,24 @@ describe('drawMenu', () => {
|
|
|
66
69
|
expect(findButton('drawMenu').hiddenWhen({ pluginState: { mode: 'edit_vertex' } })).toBe(false)
|
|
67
70
|
})
|
|
68
71
|
|
|
72
|
+
// draw_point has no Undo and no delete-vertex, but it does support snapping, and Snap is
|
|
73
|
+
// a menuItem inside this same button — so the button itself must stay visible for it even
|
|
74
|
+
// though drawUndo/drawDeletePoint (below) correctly keep excluding it.
|
|
75
|
+
test('is visible during draw_point, for the Snap toggle, even though it has no undo/delete', () => {
|
|
76
|
+
expect(findButton('drawMenu').hiddenWhen({ pluginState: { mode: 'draw_point' } })).toBe(false)
|
|
77
|
+
})
|
|
78
|
+
|
|
69
79
|
test('labels "Edit actions" in edit mode, "Draw actions" otherwise', () => {
|
|
70
80
|
expect(findButton('drawMenu').label({ pluginState: { mode: 'edit_vertex' } })).toBe('Edit actions')
|
|
81
|
+
expect(findButton('drawMenu').label({ pluginState: { mode: 'edit_point' } })).toBe('Edit actions')
|
|
71
82
|
expect(findButton('drawMenu').label({ pluginState: { mode: 'draw_polygon' } })).toBe('Draw actions')
|
|
72
83
|
expect(findButton('drawMenu').label({ pluginState: { mode: 'draw_line' } })).toBe('Draw actions')
|
|
73
84
|
})
|
|
74
85
|
|
|
86
|
+
test('is visible during edit_point', () => {
|
|
87
|
+
expect(findButton('drawMenu').hiddenWhen({ pluginState: { mode: 'edit_point' } })).toBe(false)
|
|
88
|
+
})
|
|
89
|
+
|
|
75
90
|
describe('drawUndo', () => {
|
|
76
91
|
const item = () => findMenuItem('drawMenu', 'drawUndo')
|
|
77
92
|
|
|
@@ -80,11 +95,21 @@ describe('drawMenu', () => {
|
|
|
80
95
|
expect(item().hiddenWhen({ pluginState: { mode: 'draw_line' } })).toBe(false)
|
|
81
96
|
})
|
|
82
97
|
|
|
98
|
+
test('is visible in edit_point', () => {
|
|
99
|
+
expect(item().hiddenWhen({ pluginState: { mode: 'edit_point' } })).toBe(false)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('stays hidden for draw_point — nothing to undo before a single-click commit', () => {
|
|
103
|
+
expect(item().hiddenWhen({ pluginState: { mode: 'draw_point' } })).toBe(true)
|
|
104
|
+
})
|
|
105
|
+
|
|
83
106
|
test('enables from vertex count while drawing and from the undo stack while editing', () => {
|
|
84
107
|
expect(item().enableWhen({ pluginState: { mode: 'draw_polygon', numVertices: 1 } })).toBe(true)
|
|
85
108
|
expect(item().enableWhen({ pluginState: { mode: 'draw_polygon', numVertices: 0 } })).toBe(false)
|
|
86
109
|
expect(item().enableWhen({ pluginState: { mode: 'edit_vertex', undoStackLength: 2 } })).toBe(true)
|
|
87
110
|
expect(item().enableWhen({ pluginState: { mode: 'edit_vertex', undoStackLength: 0 } })).toBe(false)
|
|
111
|
+
expect(item().enableWhen({ pluginState: { mode: 'edit_point', undoStackLength: 1 } })).toBe(true)
|
|
112
|
+
expect(item().enableWhen({ pluginState: { mode: 'edit_point', undoStackLength: 0 } })).toBe(false)
|
|
88
113
|
})
|
|
89
114
|
})
|
|
90
115
|
|
|
@@ -111,6 +136,12 @@ describe('drawMenu', () => {
|
|
|
111
136
|
expect(item().hiddenWhen({ pluginState: { mode: 'edit_vertex' } })).toBe(false)
|
|
112
137
|
})
|
|
113
138
|
|
|
139
|
+
// Deliberately excluded from edit_point — deleting a point's one coordinate is deleting
|
|
140
|
+
// the whole feature (deleteFeature's job), not a per-vertex action.
|
|
141
|
+
test('stays hidden for edit_point', () => {
|
|
142
|
+
expect(item().hiddenWhen({ pluginState: { mode: 'edit_point' } })).toBe(true)
|
|
143
|
+
})
|
|
144
|
+
|
|
114
145
|
test('enables only with a selection and enough vertices (polygon vs line)', () => {
|
|
115
146
|
expect(item().enableWhen({ pluginState: { selectedVertexIndex: -1 } })).toBe(false)
|
|
116
147
|
expect(item().enableWhen({ pluginState: { selectedVertexIndex: 0, feature: { geometry: { type: 'Polygon' } }, numVertices: 4 } })).toBe(true)
|
|
@@ -77,6 +77,13 @@ describe('SET_UNDO_STACK_LENGTH', () => {
|
|
|
77
77
|
})
|
|
78
78
|
})
|
|
79
79
|
|
|
80
|
+
describe('SET_GEOMETRY_VALID', () => {
|
|
81
|
+
test('coerces the payload to a boolean', () => {
|
|
82
|
+
expect(actions.SET_GEOMETRY_VALID(initialState, false).geometryValid).toBe(false)
|
|
83
|
+
expect(actions.SET_GEOMETRY_VALID(initialState, 1).geometryValid).toBe(true)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
80
87
|
describe('SET_CAN_ADD_POINT', () => {
|
|
81
88
|
test('coerces the payload to a boolean', () => {
|
|
82
89
|
expect(actions.SET_CAN_ADD_POINT(initialState, false).canAddPoint).toBe(false)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SYMBOL_KEYS } from './symbolKeys.js'
|
|
2
|
+
|
|
3
|
+
// Drops adapter-derived bookkeeping from a feature before it reaches a public event:
|
|
4
|
+
// symbol* keys not in SYMBOL_KEYS (e.g. symbolImageId, symbolPixelRatio), and `sortKey`
|
|
5
|
+
// (drives fill/line/symbol-sort-key render order — see MaplibreDrawAdapter.js — never
|
|
6
|
+
// caller-supplied or meaningful application data).
|
|
7
|
+
export const stripInternalProperties = (feature) => {
|
|
8
|
+
if (!feature?.properties) {
|
|
9
|
+
return feature
|
|
10
|
+
}
|
|
11
|
+
const properties = {}
|
|
12
|
+
for (const [key, value] of Object.entries(feature.properties)) {
|
|
13
|
+
const isInternal = key === 'sortKey' || (key.startsWith('symbol') && !SYMBOL_KEYS.has(key))
|
|
14
|
+
if (!isInternal) {
|
|
15
|
+
properties[key] = value
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return { ...feature, properties }
|
|
19
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { stripInternalProperties } from './stripInternalProperties.js'
|
|
2
|
+
|
|
3
|
+
describe('stripInternalProperties', () => {
|
|
4
|
+
test('drops adapter-derived symbol* keys not in SYMBOL_KEYS', () => {
|
|
5
|
+
const feature = {
|
|
6
|
+
id: 'a',
|
|
7
|
+
properties: {
|
|
8
|
+
symbol: 'pin',
|
|
9
|
+
symbolBackgroundColor: '#ca3535',
|
|
10
|
+
symbolImageId: 'symbol-abc-2x',
|
|
11
|
+
symbolIconAnchor: 'bottom',
|
|
12
|
+
symbolIconOffset: [0, -10],
|
|
13
|
+
symbolPixelRatio: 2,
|
|
14
|
+
symbolActiveImageId: 'symbol-act-abc-2x',
|
|
15
|
+
symbolSelectedImageId: 'symbol-sel-abc-2x'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
expect(stripInternalProperties(feature)).toEqual({
|
|
20
|
+
id: 'a',
|
|
21
|
+
properties: { symbol: 'pin', symbolBackgroundColor: '#ca3535' }
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('drops the internal sortKey property', () => {
|
|
26
|
+
const feature = { id: 'a', properties: { label: 'My point', sortKey: 3 } }
|
|
27
|
+
expect(stripInternalProperties(feature)).toEqual({ id: 'a', properties: { label: 'My point' } })
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('leaves non-symbol-prefixed properties untouched', () => {
|
|
31
|
+
const feature = { id: 'a', properties: { label: 'My point', stroke: '#1d70b8' } }
|
|
32
|
+
expect(stripInternalProperties(feature)).toEqual(feature)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('does not mutate the original feature', () => {
|
|
36
|
+
const feature = { id: 'a', properties: { symbol: 'pin', symbolImageId: 'x' } }
|
|
37
|
+
stripInternalProperties(feature)
|
|
38
|
+
expect(feature.properties.symbolImageId).toBe('x')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('returns the feature unchanged when it has no properties', () => {
|
|
42
|
+
const feature = { id: 'a', geometry: { type: 'Point', coordinates: [0, 0] } }
|
|
43
|
+
expect(stripInternalProperties(feature)).toBe(feature)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('returns null/undefined as-is', () => {
|
|
47
|
+
expect(stripInternalProperties(null)).toBeNull()
|
|
48
|
+
expect(stripInternalProperties(undefined)).toBeUndefined()
|
|
49
|
+
})
|
|
50
|
+
})
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Public symbol-config keys a caller can set on a Point (see docs/api/symbol-config.md).
|
|
2
|
+
export const SYMBOL_KEYS = new Set([
|
|
3
|
+
'symbol', 'symbolSvgContent', 'symbolBackgroundColor', 'symbolForegroundColor',
|
|
4
|
+
'symbolHaloWidth', 'symbolGraphic', 'symbolViewBox', 'symbolAnchor'
|
|
5
|
+
])
|