Skip to content

feat: visibility toggle in plugin legend; persist layer visibility and basemap opacity on save [PR6] [DHIS2-18242]#3650

Open
BRaimbault wants to merge 4 commits into
feat/DHIS2-18242-PR5from
feat/DHIS2-18242-PR6
Open

feat: visibility toggle in plugin legend; persist layer visibility and basemap opacity on save [PR6] [DHIS2-18242]#3650
BRaimbault wants to merge 4 commits into
feat/DHIS2-18242-PR5from
feat/DHIS2-18242-PR6

Conversation

@BRaimbault
Copy link
Copy Markdown
Collaborator

@BRaimbault BRaimbault commented May 1, 2026

Parent

Implements

  • DHIS2-20287: Layer visibility toggle in plugin legend panel
  • DHIS2-19078: Persist layer visibility and basemap opacity on save

Overview

Sixth PR in the series. Adds a visibility toggle (eye icon) to each layer row in the plugin legend panel, letting users show/hide individual layers without leaving the plugin view. Separately, makes layer visibility and basemap opacity persistent: both are now saved when a map is saved and fully restored when it is reopened, including under the new v43+ basemaps[] API format.

Changes

Layer visibility toggle in plugin legend DHIS2-20287

An eye icon button is added to each layer row in the plugin legend panel. Clicking it toggles the layer's visibility without affecting the saved state. The toggle is managed via a visibilityOverrides map in Map.jsx, keyed by layer ID, that overlays the saved isVisible value. Overrides are reset when mapViews changes (new map loaded). The layersWithVisibility computed array (visibilityOverrides[id] ?? layer.isVisible ?? true) is passed both to MapView (so layers are actually hidden/shown) and to Legend (so the eye icon reflects the current state). The button calls e.stopPropagation() to avoid collapsing the legend panel.

Files: src/components/plugin/Map.jsx, src/components/plugin/Legend.jsx, src/components/plugin/LegendLayer.jsx, src/components/plugin/styles/Legend.css

Persist layer visibility and basemap opacity DHIS2-19078

Save path (favorites.js): cleanLayerConfig now writes hidden: layer.isVisible === false into each mapView before the property pick ('hidden' added to validLayerProperties). The old getBasemapString is replaced by getBasemapPayload, which encodes id, opacity, and hidden for the basemap. On DHIS2 v43+, it returns a basemaps: [{ id, opacity, hidden }] array (VERSION-TOGGLE DHIS2-20417); on older servers it returns a JSON-encoded basemap string. Both paths use === false checks so layers/basemaps without an explicit isVisible default to visible. FileMenu.jsx now passes serverVersion to cleanMapConfig to activate the toggle.

Load path (getMigratedMapConfig.js): extractBasemap now handles the v43+ basemaps[] array and the new JSON-encoded basemap string, recovering opacity and isVisible from both. Defaults (id ?? defaultBasemapId, opacity ?? 1, isVisible ?? true) are applied at a single return statement rather than per-branch. upgradeMapViews now runs unconditionally (not only for legacy maps) so isVisible: view.hidden !== true is set on every mapView regardless of format. 'basemaps' is added to getBaseFields so the API fetch includes the field for v43+ maps.

Layer components and loaders: All 8 loaders no longer hardcode isVisible: true, allowing the value set by getMigratedMapConfig to flow through to the Redux store. All 8 layer components call this.setLayerVisibility() immediately after map.addLayer() so the layer's initial on-map visibility matches Redux state. reducers/map.js defaults isVisible ?? true for newly-added layers.

External layer opacity: createExternalBasemapLayer and createExternalOverlayLayer no longer hardcode opacity: 1; opacity is instead provided by the layer component's default parameter (opacity = 1), consistent with all other layer types.

Files: src/util/favorites.js, src/util/getMigratedMapConfig.js, src/util/helpers.js, src/util/external.js, src/reducers/map.js, src/components/app/FileMenu.jsx, all 8 layer components, all 8 loaders

Tests update

  • src/components/plugin/__tests__/LegendLayer.spec.jsx (new) — eye-open/off button rendering; default visibility; no button without handler; click calls toggleLayerVisibility(id); click stops propagation
  • src/util/__tests__/favorites.spec.jshidden: true/false output from isVisible; hidden basemap in legacy JSON; non-default opacity in legacy JSON; v43+ basemaps[] format with and without hidden basemap
  • src/util/__tests__/getMigratedMapConfig.spec.jshidden: true/false mapView → isVisible; JSON basemap string → opacity/isVisible round-trip; v43+ basemaps[] → opacity/isVisible round-trip

Manual testing

Netlify: https://pr-3650.maps.netlify.dhis2.org/ + Instance: https://dev.im.dhis2.org/maps-app-42-3

Quality checklist

  • Jest tests added/updated
  • Docs added N/A
  • d2-ci dependencies replaced N/A
  • Include plugin in testing
  • Tester approved (@HendrikThePendric)

@BRaimbault BRaimbault requested a review from a team May 1, 2026 17:17
@dhis2-bot
Copy link
Copy Markdown
Contributor

dhis2-bot commented May 1, 2026

🚀 Deployed on https://pr-3650.maps.netlify.dhis2.org

@dhis2-bot dhis2-bot temporarily deployed to netlify May 1, 2026 17:19 Inactive
Copy link
Copy Markdown
Collaborator Author

@BRaimbault BRaimbault left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ready for review

@BRaimbault BRaimbault force-pushed the feat/DHIS2-18242-PR6 branch from c6e1d66 to 92a3e8f Compare May 1, 2026 17:23
@dhis2-bot dhis2-bot temporarily deployed to netlify May 1, 2026 17:25 Inactive
@BRaimbault BRaimbault changed the title feat: visibility toggle in plugin legend; persist layer/basemap visibility on save [DHIS2-18242] feat: visibility toggle in plugin legend; persist layer/basemap visibility on save [PR6] [DHIS2-18242] May 1, 2026
@BRaimbault BRaimbault marked this pull request as ready for review May 1, 2026 17:33
@BRaimbault BRaimbault changed the title feat: visibility toggle in plugin legend; persist layer/basemap visibility on save [PR6] [DHIS2-18242] feat: visibility toggle in plugin legend; layer visibility and basemap opacity on save [PR6] [DHIS2-18242] May 4, 2026
@BRaimbault BRaimbault changed the title feat: visibility toggle in plugin legend; layer visibility and basemap opacity on save [PR6] [DHIS2-18242] feat: visibility toggle in plugin legend; persist layer visibility and basemap opacity on save [PR6] [DHIS2-18242] May 4, 2026
@sonarqubecloud
Copy link
Copy Markdown

@dhis2-bot dhis2-bot temporarily deployed to netlify May 14, 2026 16:40 Inactive
Copy link
Copy Markdown
Contributor

@HendrikThePendric HendrikThePendric left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good to me. One thing I noticed is that you had to effectively make the same change to each layer class implementation. At first this seemed like a potential code smell to me, the base class should call this.setLayerVisibility(). But when I started to compare various layer classes I noticed that these all do different things and possibly the sequence of these things matters as well. So for this PR, I'd say what you did there is fine.

Perhaps in the future it would be worth looking into extracting some common steps into methods of the base Layer and then calling these helpers in the extending classes. But that would be completely out-of-scope for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants