From be065dcf69d87c276c9aec32a7d981530567daea Mon Sep 17 00:00:00 2001 From: Mark Fee Date: Thu, 4 Jun 2026 08:30:11 +0100 Subject: [PATCH 1/2] IM-317 can set visibility globally --- demo/js/ml-datasets.js | 6 +-- plugins/beta/datasets/src/DatasetsInit.jsx | 1 + .../maplibre/datasets/mapLibreDataset.js | 4 ++ .../maplibre/datasets/mapLibreDataset.test.js | 14 +++++++ .../adapters/maplibre/maplibreLayerAdapter.js | 40 +++++++++++-------- plugins/beta/datasets/src/reducer.js | 7 +++- plugins/beta/datasets/src/registry/dataset.js | 5 ++- .../datasets/src/registry/globalDataset.js | 2 + 8 files changed, 55 insertions(+), 24 deletions(-) diff --git a/demo/js/ml-datasets.js b/demo/js/ml-datasets.js index 454ed7d1..e250ac73 100644 --- a/demo/js/ml-datasets.js +++ b/demo/js/ml-datasets.js @@ -380,7 +380,7 @@ const testVisibility = () => { const testGlobalVisibility = () => { setTimeout(() => datasetsPlugin.setDatasetVisibility(false), 1000) - setTimeout(() => datasetsPlugin.setDatasetVisibility(true), 2000) + // setTimeout(() => datasetsPlugin.setDatasetVisibility(true), 10000) // setTimeout(() => datasetsPlugin.setDatasetVisibility(true, { datasetId: 'hedge-control' }), 500) // setTimeout(() => datasetsPlugin.setStyle({ stroke: { outdoor: '#0000ff' }, }, { datasetId: 'hedge-control' }), 2000) } @@ -472,10 +472,10 @@ interactiveMap.on('datasets:ready', function () { // testGetters() // testInvalidApiCalls() // testFeatureVisibility() - testSetOpacity() + // testSetOpacity() // testSetStyle() // testVisibility() - // testGlobalVisibility() + testGlobalVisibility() // testRemoveAndAddDataset() // testSetData() }) diff --git a/plugins/beta/datasets/src/DatasetsInit.jsx b/plugins/beta/datasets/src/DatasetsInit.jsx index cc9df73f..23dc49e8 100755 --- a/plugins/beta/datasets/src/DatasetsInit.jsx +++ b/plugins/beta/datasets/src/DatasetsInit.jsx @@ -84,6 +84,7 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m useLayerAdapterActions('applyStyle', dispatch, pluginState, [pluginState.layerAdapterActions.applyStyle]) useLayerAdapterActions('applyDatasetVisibility', dispatch, pluginState, [pluginState.layerAdapterActions.applyDatasetVisibility]) + useLayerAdapterActions('applyGlobalVisibility', dispatch, pluginState, [pluginState.layerAdapterActions.applyGlobalVisibility]) useLayerAdapterActions('applyDatasetOpacity', dispatch, pluginState, [pluginState.layerAdapterActions.applyDatasetOpacity]) useLayerAdapterActions('applyGlobalOpacity', dispatch, pluginState, [pluginState.layerAdapterActions.applyGlobalOpacity]) useLayerAdapterActions('addDataset', dispatch, pluginState, [pluginState.layerAdapterActions.addDataset]) diff --git a/plugins/beta/datasets/src/adapters/maplibre/datasets/mapLibreDataset.js b/plugins/beta/datasets/src/adapters/maplibre/datasets/mapLibreDataset.js index e52726c3..1689acba 100644 --- a/plugins/beta/datasets/src/adapters/maplibre/datasets/mapLibreDataset.js +++ b/plugins/beta/datasets/src/adapters/maplibre/datasets/mapLibreDataset.js @@ -68,6 +68,10 @@ export class MapLibreDataset extends Dataset { return response } + getLayersWithVisibility () { + return this.getLayersWithValue('visibility') + } + getLayersWithOpacity () { return this.getLayersWithValue('opacity') } diff --git a/plugins/beta/datasets/src/adapters/maplibre/datasets/mapLibreDataset.test.js b/plugins/beta/datasets/src/adapters/maplibre/datasets/mapLibreDataset.test.js index 5b6b75b8..807df7d3 100644 --- a/plugins/beta/datasets/src/adapters/maplibre/datasets/mapLibreDataset.test.js +++ b/plugins/beta/datasets/src/adapters/maplibre/datasets/mapLibreDataset.test.js @@ -104,6 +104,20 @@ describe('MapLibreDataset', () => { }) }) + describe('getLayersWithVisibility', () => { + it('returns layerIds and visibility for a layer with sublayers', () => { + const registryDataset = datasetRegistry.getDataset('land-covers') + const layersWithVisibility = registryDataset.getLayersWithVisibility() + expect(layersWithVisibility).toEqual([ + { layerIds: ['land-covers-130-131', 'land-covers-130-131-stroke'], visibility: 'visible' }, + { layerIds: ['land-covers-332', 'land-covers-332-stroke'], visibility: 'visible' }, + { layerIds: ['land-covers-110', 'land-covers-110-stroke'], visibility: 'visible' }, + { layerIds: ['land-covers-379', 'land-covers-379-stroke'], visibility: 'none' }, + { layerIds: ['land-covers-other', 'land-covers-other-stroke'], visibility: 'visible' } + ]) + }) + }) + describe('getLayersWithOpacity', () => { it('returns layerIds and opacity for a sublayer with no sublayers', () => { const dataset = datasetRegistry.getDataset('existing-fields') diff --git a/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js b/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js index 52083131..68cef5d3 100644 --- a/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +++ b/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js @@ -289,28 +289,34 @@ export default class MaplibreLayerAdapter { }) } + /** + * Apply visibility for all layers with datasetId + * @param {string} datasetId + */ applyDatasetVisibility (datasetId) { const registryDataset = datasetRegistry.getDataset(datasetId) - const style = this._map.getStyle() - if (!style?.layers) { - return - } - // Covers base fill layer (datasetId) and all suffixed layers - // (-stroke, -${sublayerId}, -${sublayerId}-stroke) without needing the dataset object. - if (registryDataset.hasSublayers) { - const { sublayerIds } = registryDataset - sublayerIds.forEach(sublayerId => { this.applyDatasetVisibility(sublayerId) }) - } else { - const { visibility } = registryDataset - const datasetId = registryDataset.id - style.layers.filter(layer => - layer.id === datasetId || layer.id.startsWith(`${datasetId}-`) - ).forEach(layer => { - this._map.setLayoutProperty(layer.id, 'visibility', visibility) - }) + if (registryDataset) { + this._applyRegistryDatasetVisibility(registryDataset) } } + /** + * Apply visibility for all layers belonging to a registryDataset. + * @param {Object} registryDataset + */ + _applyRegistryDatasetVisibility (registryDataset) { + registryDataset.getLayersWithVisibility().forEach(({ layerIds, visibility }) => { + layerIds.forEach(layerId => this._map.setLayoutProperty(layerId, 'visibility', visibility)) + }) + } + + /** + * Apply visibility for all layers + */ + applyGlobalVisibility () { + datasetRegistry.forEachDataset(registryDataset => this._applyRegistryDatasetVisibility(registryDataset)) + } + _applyFeatureFilter (registryDataset) { const layersWithFilters = registryDataset.getLayersWithFilters() layersWithFilters.forEach(({ layerIds, filter }) => { diff --git a/plugins/beta/datasets/src/reducer.js b/plugins/beta/datasets/src/reducer.js index bb49c00e..244c2c8a 100755 --- a/plugins/beta/datasets/src/reducer.js +++ b/plugins/beta/datasets/src/reducer.js @@ -19,6 +19,7 @@ const initialState = { layerAdapterActions: { applyStyle: [], applyDatasetVisibility: [], + applyGlobalVisibility: [], applyDatasetOpacity: [], applyGlobalOpacity: [], addDataset: [], @@ -108,10 +109,12 @@ const setDatasetVisibility = (state, payload) => { } const setGlobalVisibility = (state, payload) => { - const { visibility } = payload + const { visible } = payload + const applyGlobalVisibility = [...state.layerAdapterActions.applyGlobalVisibility, [visible]] return { ...state, - globals: { ...state.globals, visible: visibility !== 'hidden' } + layerAdapterActions: { ...state.layerAdapterActions, applyGlobalVisibility }, + globals: { ...state.globals, visible } } } diff --git a/plugins/beta/datasets/src/registry/dataset.js b/plugins/beta/datasets/src/registry/dataset.js index e7f9a325..1acaa670 100644 --- a/plugins/beta/datasets/src/registry/dataset.js +++ b/plugins/beta/datasets/src/registry/dataset.js @@ -2,7 +2,7 @@ import { datasetRegistry } from './datasetRegistry.js' import { hasCustomVisualStyle } from '../defaults.js' import { hasPattern } from '../../../../../src/utils/patternUtils.js' import { DynamicGeoJson } from './dynamicGeoJson.js' -import { calculateOpacity } from './globalDataset.js' +import { calculateOpacity, getGlobalVisibility } from './globalDataset.js' export class Dataset { constructor (dataset) { @@ -25,6 +25,7 @@ export class Dataset { get maxZoom () { return this._datasetDefinition.maxZoom || this.parent?.maxZoom } get showInKey () { return this._datasetDefinition.showInKey || this.parent?.showInKey || false } get groupLabel () { return this._datasetDefinition.groupLabel } + get opacity () { const myOpacity = this.style?.opacity const parentOpacity = this.parent?.style?.opacity @@ -54,7 +55,7 @@ export class Dataset { if (this.isSublayer) { return this._datasetDefinition.visible && (this.parent?.visible) } - return this._datasetDefinition.visible + return this._datasetDefinition.visible && getGlobalVisibility() } get symbolAnchor () { diff --git a/plugins/beta/datasets/src/registry/globalDataset.js b/plugins/beta/datasets/src/registry/globalDataset.js index 0be35341..c0d4fc0a 100644 --- a/plugins/beta/datasets/src/registry/globalDataset.js +++ b/plugins/beta/datasets/src/registry/globalDataset.js @@ -32,3 +32,5 @@ export const calculateOpacity = (opacityValue, parentOpacityValue) => { return calculateLocalOpacity(opacityValue, parentOpacityValue) } } + +export const getGlobalVisibility = () => globalState.visible From b34538f3b0c1bccef33f1f2762a919f4d497ba70 Mon Sep 17 00:00:00 2001 From: Mark Fee Date: Thu, 4 Jun 2026 12:19:02 +0100 Subject: [PATCH 2/2] IM-317 still show local visibility in state when global is hidden --- demo/js/ml-datasets.js | 2 +- .../components/menu/LayersMenuCheckbox.jsx | 3 +-- .../datasets/src/panels/Layers.module.scss | 4 ++++ plugins/beta/datasets/src/registry/dataset.js | 21 +++++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/demo/js/ml-datasets.js b/demo/js/ml-datasets.js index e250ac73..8c9aeae9 100644 --- a/demo/js/ml-datasets.js +++ b/demo/js/ml-datasets.js @@ -380,7 +380,7 @@ const testVisibility = () => { const testGlobalVisibility = () => { setTimeout(() => datasetsPlugin.setDatasetVisibility(false), 1000) - // setTimeout(() => datasetsPlugin.setDatasetVisibility(true), 10000) + setTimeout(() => datasetsPlugin.setDatasetVisibility(true), 10000) // setTimeout(() => datasetsPlugin.setDatasetVisibility(true, { datasetId: 'hedge-control' }), 500) // setTimeout(() => datasetsPlugin.setStyle({ stroke: { outdoor: '#0000ff' }, }, { datasetId: 'hedge-control' }), 2000) } diff --git a/plugins/beta/datasets/src/components/menu/LayersMenuCheckbox.jsx b/plugins/beta/datasets/src/components/menu/LayersMenuCheckbox.jsx index 05d462c8..dc3e5164 100644 --- a/plugins/beta/datasets/src/components/menu/LayersMenuCheckbox.jsx +++ b/plugins/beta/datasets/src/components/menu/LayersMenuCheckbox.jsx @@ -7,7 +7,6 @@ export const LayersMenuCheckbox = ({ menuGroupItem, onChange }) => { } const datasetId = registryDataset.isSublayer ? registryDataset.parentId : registryDataset.id const sublayerId = registryDataset.isSublayer ? registryDataset.id : undefined - const itemClass = `im-c-datasets-layers__item govuk-checkboxes govuk-checkboxes--small${registryDataset.visible ? '' : ' im-c-datasets-layers__item--checked'}` return (
@@ -20,7 +19,7 @@ export const LayersMenuCheckbox = ({ menuGroupItem, onChange }) => { name='layers' type='checkbox' value={registryDataset.id} - checked={registryDataset.visible} + checked={registryDataset.isLocallyVisible} onChange={onChange} />