From c58196b2856467cb38e5763b60caf2609bfdbe8c Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Tue, 14 May 2024 16:00:26 +0200 Subject: [PATCH 1/3] chore: move caption to its own module --- umap/static/umap/js/modules/caption.js | 108 +++++++++++++++++++++++++ umap/static/umap/js/modules/global.js | 2 + umap/static/umap/js/umap.controls.js | 94 +-------------------- umap/static/umap/js/umap.js | 13 ++- 4 files changed, 122 insertions(+), 95 deletions(-) create mode 100644 umap/static/umap/js/modules/caption.js diff --git a/umap/static/umap/js/modules/caption.js b/umap/static/umap/js/modules/caption.js new file mode 100644 index 00000000..4b859188 --- /dev/null +++ b/umap/static/umap/js/modules/caption.js @@ -0,0 +1,108 @@ +import { DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js' +import { translate } from './i18n.js' +import * as Utils from './utils.js' + +export default class Caption { + constructor(map) { + this.map = map + } + + open() { + const container = DomUtil.create('div', 'umap-caption') + DomUtil.createTitle(container, this.map.options.name, 'icon-caption') + this.map.permissions.addOwnerLink('h5', container) + if (this.map.options.description) { + const description = DomUtil.element({ + tagName: 'div', + className: 'umap-map-description', + safeHTML: Utils.toHTML(this.map.options.description), + parent: container, + }) + } + const datalayerContainer = DomUtil.create('div', 'datalayer-container', container) + this.map.eachVisibleDataLayer((datalayer) => this.addDataLayer(datalayer, datalayerContainer)) + const creditsContainer = DomUtil.create('div', 'credits-container', container) + this.addCredits(creditsContainer) + this.map.panel.open({ content: container }) + } + + addDataLayer(datalayer, container) { + if (!datalayer.options.inCaption) return + const p = DomUtil.create('p', 'datalayer-legend', container), + legend = DomUtil.create('span', '', p), + headline = DomUtil.create('strong', '', p) + datalayer.onceLoaded(function () { + datalayer.renderLegend(legend) + if (datalayer.options.description) { + DomUtil.element({ + tagName: 'span', + parent: p, + safeHTML: Utils.toHTML(datalayer.options.description), + }) + } + }) + datalayer.renderToolbox(headline) + DomUtil.add('span', '', headline, `${datalayer.options.name} `) + } + + addCredits(container) { + const credits = DomUtil.createFieldset(container, translate('Credits')) + let title = DomUtil.add('h5', '', credits, translate('User content credits')) + if (this.map.options.shortCredit || this.map.options.longCredit) { + DomUtil.element({ + tagName: 'p', + parent: credits, + safeHTML: Utils.toHTML( + this.map.options.longCredit || this.map.options.shortCredit + ), + }) + } + if (this.map.options.licence) { + const licence = DomUtil.add( + 'p', + '', + credits, + `${translate('Map user content has been published under licence')} ` + ) + DomUtil.createLink( + '', + licence, + this.map.options.licence.name, + this.map.options.licence.url + ) + } else { + DomUtil.add('p', '', credits, translate('No licence has been set')) + } + title = DomUtil.create('h5', '', credits) + title.textContent = translate('Map background credits') + const tilelayerCredit = DomUtil.create('p', '', credits) + DomUtil.element({ + tagName: 'strong', + parent: tilelayerCredit, + textContent: `${this.map.selected_tilelayer.options.name} `, + }) + DomUtil.element({ + tagName: 'span', + parent: tilelayerCredit, + safeHTML: this.map.selected_tilelayer.getAttribution(), + }) + const urls = { + leaflet: 'http://leafletjs.com', + django: 'https://www.djangoproject.com', + umap: 'http://wiki.openstreetmap.org/wiki/UMap', + changelog: 'https://umap-project.readthedocs.io/en/master/changelog/', + version: this.map.options.umap_version, + } + const creditHTML = translate( + ` + Powered by Leaflet and + Django, + glued by uMap project + (version {version}). + `, + urls + ) + DomUtil.element({ tagName: 'p', innerHTML: creditHTML, parent: credits }) + + } +} diff --git a/umap/static/umap/js/modules/global.js b/umap/static/umap/js/modules/global.js index 16742508..f665ef52 100644 --- a/umap/static/umap/js/modules/global.js +++ b/umap/static/umap/js/modules/global.js @@ -1,6 +1,7 @@ import URLs from './urls.js' import Browser from './browser.js' import Facets from './facets.js' +import Caption from './caption.js' import { Panel, EditPanel, FullPanel } from './panel.js' import * as Utils from './utils.js' import { SCHEMA } from './schema.js' @@ -25,4 +26,5 @@ window.U = { Utils, SCHEMA, Orderable, + Caption, } diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index 770bcda5..e019fb7b 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -530,7 +530,7 @@ U.CaptionControl = L.Control.Button.extend({ }, onClick: function () { - this.map.displayCaption() + this.map.openCaption() }, }) @@ -671,96 +671,6 @@ const ControlsMixin = { 'tilelayers', ], - displayCaption: function () { - const container = L.DomUtil.create('div', 'umap-caption') - L.DomUtil.createTitle(container, this.options.name, 'icon-caption') - this.permissions.addOwnerLink('h5', container) - if (this.options.description) { - const description = L.DomUtil.element({ - tagName: 'div', - className: 'umap-map-description', - safeHTML: U.Utils.toHTML(this.options.description), - parent: container, - }) - } - const datalayerContainer = L.DomUtil.create('div', 'datalayer-container', container) - this.eachVisibleDataLayer((datalayer) => { - if (!datalayer.options.inCaption) return - const p = L.DomUtil.create('p', 'datalayer-legend', datalayerContainer), - legend = L.DomUtil.create('span', '', p), - headline = L.DomUtil.create('strong', '', p) - datalayer.onceLoaded(function () { - datalayer.renderLegend(legend) - if (datalayer.options.description) { - L.DomUtil.element({ - tagName: 'span', - parent: p, - safeHTML: U.Utils.toHTML(datalayer.options.description), - }) - } - }) - datalayer.renderToolbox(headline) - L.DomUtil.add('span', '', headline, `${datalayer.options.name} `) - }) - const creditsContainer = L.DomUtil.create('div', 'credits-container', container), - credits = L.DomUtil.createFieldset(creditsContainer, L._('Credits')) - title = L.DomUtil.add('h5', '', credits, L._('User content credits')) - if (this.options.shortCredit || this.options.longCredit) { - L.DomUtil.element({ - tagName: 'p', - parent: credits, - safeHTML: U.Utils.toHTML(this.options.longCredit || this.options.shortCredit), - }) - } - if (this.options.licence) { - const licence = L.DomUtil.add( - 'p', - '', - credits, - `${L._('Map user content has been published under licence')} ` - ) - L.DomUtil.createLink( - '', - licence, - this.options.licence.name, - this.options.licence.url - ) - } else { - L.DomUtil.add('p', '', credits, L._('No licence has been set')) - } - title = L.DomUtil.create('h5', '', credits) - title.textContent = L._('Map background credits') - const tilelayerCredit = L.DomUtil.create('p', '', credits) - L.DomUtil.element({ - tagName: 'strong', - parent: tilelayerCredit, - textContent: `${this.selected_tilelayer.options.name} `, - }) - L.DomUtil.element({ - tagName: 'span', - parent: tilelayerCredit, - safeHTML: this.selected_tilelayer.getAttribution(), - }) - const urls = { - leaflet: 'http://leafletjs.com', - django: 'https://www.djangoproject.com', - umap: 'http://wiki.openstreetmap.org/wiki/UMap', - changelog: 'https://umap-project.readthedocs.io/en/master/changelog/', - version: this.options.umap_version, - } - const creditHTML = L._( - ` - Powered by Leaflet and - Django, - glued by uMap project - (version {version}). - `, - urls - ) - L.DomUtil.element({ tagName: 'p', innerHTML: creditHTML, parent: credits }) - this.panel.open({ content: container }) - }, - renderEditToolbar: function () { const container = L.DomUtil.create( 'div', @@ -1077,7 +987,7 @@ U.AttributionControl = L.Control.Attribution.extend({ if (captionMenus) { const link = L.DomUtil.add('a', '', container, ` — ${L._('About')}`) L.DomEvent.on(link, 'click', L.DomEvent.stop) - .on(link, 'click', this._map.displayCaption, this._map) + .on(link, 'click', this._map.openCaption, this._map) .on(link, 'dblclick', L.DomEvent.stop) } if (window.top === window.self && captionMenus) { diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index b37767a5..e99d346e 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -222,7 +222,7 @@ U.Map = L.Map.extend({ this.openBrowser('filters') } else if (this.options.onLoadPanel === 'caption') { this.panel.mode = 'condensed' - this.displayCaption() + this.openCaption() } if (L.Util.queryString('edit')) { if (this.hasEditMode()) this.enableEdit() @@ -382,6 +382,7 @@ U.Map = L.Map.extend({ else this.scrollWheelZoom.disable() this.browser = new U.Browser(this) this.facets = new U.Facets(this) + this.caption = new U.Caption(this) this.importer = new U.Importer(this) this.drop = new U.DropControl(this) this.share = new U.Share(this) @@ -916,6 +917,12 @@ U.Map = L.Map.extend({ }) }, + openCaption: function () { + this.onceDatalayersLoaded(function () { + this.caption.open() + }) + }, + eachDataLayer: function (method, context) { for (let i = 0; i < this.datalayers_index.length; i++) { method.call(context, this.datalayers_index[i]) @@ -1591,7 +1598,7 @@ U.Map = L.Map.extend({ 'umap-about-link flat', container, L._('About'), - this.displayCaption, + this.openCaption, this ) L.DomUtil.createButton( @@ -1758,7 +1765,7 @@ U.Map = L.Map.extend({ items.push( { text: L._('About'), - callback: this.displayCaption, + callback: this.openCaption, }, { text: this.help.displayLabel('SEARCH'), From e6b4a54499ca547544d721ebdf5b3ff2b970e34e Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Tue, 14 May 2024 16:26:00 +0200 Subject: [PATCH 2/3] fix: show non visible layers in caption fix #1820 --- umap/static/umap/js/modules/caption.js | 2 +- umap/tests/base.py | 1 + umap/tests/integration/test_caption.py | 24 ++++++++++++++++++++++++ umap/tests/integration/test_map.py | 15 --------------- 4 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 umap/tests/integration/test_caption.py diff --git a/umap/static/umap/js/modules/caption.js b/umap/static/umap/js/modules/caption.js index 4b859188..3374eabc 100644 --- a/umap/static/umap/js/modules/caption.js +++ b/umap/static/umap/js/modules/caption.js @@ -20,7 +20,7 @@ export default class Caption { }) } const datalayerContainer = DomUtil.create('div', 'datalayer-container', container) - this.map.eachVisibleDataLayer((datalayer) => this.addDataLayer(datalayer, datalayerContainer)) + this.map.eachDataLayer((datalayer) => this.addDataLayer(datalayer, datalayerContainer)) const creditsContainer = DomUtil.create('div', 'credits-container', container) this.addCredits(creditsContainer) this.map.panel.open({ content: container }) diff --git a/umap/tests/base.py b/umap/tests/base.py index 62f948eb..e1abba32 100644 --- a/umap/tests/base.py +++ b/umap/tests/base.py @@ -125,6 +125,7 @@ class DataLayerFactory(factory.django.DjangoModelFactory): **kwargs["settings"], } data.setdefault("_umap_options", {}) + kwargs["settings"]["name"] = kwargs["name"] data["_umap_options"]["name"] = kwargs["name"] kwargs["geojson"] = ContentFile(json.dumps(data), "foo.json") return kwargs diff --git a/umap/tests/integration/test_caption.py b/umap/tests/integration/test_caption.py new file mode 100644 index 00000000..2936d65c --- /dev/null +++ b/umap/tests/integration/test_caption.py @@ -0,0 +1,24 @@ +import pytest +from playwright.sync_api import expect + +from ..base import DataLayerFactory + +pytestmark = pytest.mark.django_db + + +def test_caption(live_server, page, map): + map.settings["properties"]["onLoadPanel"] = "caption" + map.save() + basic = DataLayerFactory(map=map, name="Basic layer") + non_loaded = DataLayerFactory( + map=map, name="Non loaded", settings={"displayOnLoad": False} + ) + hidden = DataLayerFactory(map=map, name="Hidden", settings={"inCaption": False}) + page.goto(f"{live_server.url}{map.get_absolute_url()}") + panel = page.locator(".umap-caption") + expect(panel).to_be_visible() + expect(panel.locator(".datalayer-legend").get_by_text(basic.name)).to_be_visible() + expect( + panel.locator(".datalayer-legend .off").get_by_text(non_loaded.name) + ).to_be_visible() + expect(panel.locator(".datalayer-legend").get_by_text(hidden.name)).to_be_hidden() diff --git a/umap/tests/integration/test_map.py b/umap/tests/integration/test_map.py index 87c673eb..5bbc128f 100644 --- a/umap/tests/integration/test_map.py +++ b/umap/tests/integration/test_map.py @@ -188,21 +188,6 @@ def test_remote_layer_should_not_be_used_as_datalayer_for_created_features( expect(layers).to_have_count(2) -def test_can_hide_datalayer_from_caption(openmap, live_server, datalayer, page): - # Add another DataLayer - other = DataLayerFactory(map=openmap, name="Hidden", settings={"inCaption": False}) - page.goto(f"{live_server.url}{openmap.get_absolute_url()}") - toggle = page.get_by_text("About").first - expect(toggle).to_be_visible() - toggle.click() - layers = page.locator(".umap-caption .datalayer-legend") - expect(layers).to_have_count(1) - found = page.locator(".panel.left.on").get_by_text(datalayer.name) - expect(found).to_be_visible() - hidden = page.locator(".panel.left.on").get_by_text(other.name) - expect(hidden).to_be_hidden() - - def test_minimap_on_load(map, live_server, datalayer, page): page.goto(f"{live_server.url}{map.get_absolute_url()}") expect(page.locator(".leaflet-control-minimap")).to_be_hidden() From 9e2a29002a2d22541e84dd9e09ef007c717269cf Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Tue, 14 May 2024 17:09:17 +0200 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: David Larlet <3556+davidbgk@users.noreply.github.com> --- umap/static/umap/js/modules/caption.js | 6 +++--- umap/static/umap/js/umap.js | 8 ++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/umap/static/umap/js/modules/caption.js b/umap/static/umap/js/modules/caption.js index 3374eabc..9ebe01ce 100644 --- a/umap/static/umap/js/modules/caption.js +++ b/umap/static/umap/js/modules/caption.js @@ -31,7 +31,7 @@ export default class Caption { const p = DomUtil.create('p', 'datalayer-legend', container), legend = DomUtil.create('span', '', p), headline = DomUtil.create('strong', '', p) - datalayer.onceLoaded(function () { + datalayer.onceLoaded(() => { datalayer.renderLegend(legend) if (datalayer.options.description) { DomUtil.element({ @@ -89,8 +89,8 @@ export default class Caption { const urls = { leaflet: 'http://leafletjs.com', django: 'https://www.djangoproject.com', - umap: 'http://wiki.openstreetmap.org/wiki/UMap', - changelog: 'https://umap-project.readthedocs.io/en/master/changelog/', + umap: 'https://umap-project.org/', + changelog: 'https://docs.umap-project.org/en/master/changelog/', version: this.map.options.umap_version, } const creditHTML = translate( diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index e99d346e..8bc9ac21 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -912,15 +912,11 @@ U.Map = L.Map.extend({ }, openBrowser: function (mode) { - this.onceDatalayersLoaded(function () { - this.browser.open(mode) - }) + this.onceDatalayersLoaded(() => this.browser.open(mode)) }, openCaption: function () { - this.onceDatalayersLoaded(function () { - this.caption.open() - }) + this.onceDatalayersLoaded(() => this.caption.open()) }, eachDataLayer: function (method, context) {