feat: add defaultPanelMode setting

cf https://forum.openstreetmap.fr/t/umap-2-3-changement-de-gestion-des-pop-up/23680/3

Trying to make this panel expanded/condenses thing simpler and more intuitive.

It's mode can be set:
- by explicitely setting defaultPanelMode = xxx
- or, if defaultPanelMode is undefined, with sensible default when there is
  an onLoadPanel defined, and to respect previous uMap behaviour
- or, if defaultPanelMode is unset, and some feature opens in the panel, it
  will be set to expanded (here again to respect previous behaviour
- then, when user change it manually (by clicking on the toggle button), then
  we should never change it automatically, and respect the previous mode when
  reopening the panel

(We are only talking about the left panel, here.)
This commit is contained in:
Yohan Boniface 2024-05-16 19:38:51 +02:00
parent 3a19b921a7
commit c860866fe9
11 changed files with 93 additions and 29 deletions

View file

@ -465,6 +465,7 @@ input.switch:checked ~ label:after {
.button-bar { .button-bar {
grid-gap: 7px; grid-gap: 7px;
} }
.umap-multiplechoice.by2,
.button-bar.half { .button-bar.half {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
} }

View file

@ -9,18 +9,7 @@ export default class Browser {
filter: '', filter: '',
inBbox: false, inBbox: false,
} }
this._mode = 'layers' this.mode = 'layers'
}
set mode(value) {
// Store the mode so we can respect it when we redraw
if (['data', 'filters'].includes(value)) this.map.panel.mode = 'expanded'
else if (value === 'layers') this.map.panel.mode = 'condensed'
this._mode = value
}
get mode() {
return this._mode
} }
addFeature(feature, parent) { addFeature(feature, parent) {

View file

@ -2,11 +2,11 @@ import { DomUtil, DomEvent } from '../../vendors/leaflet/leaflet-src.esm.js'
import { translate } from './i18n.js' import { translate } from './i18n.js'
export class Panel { export class Panel {
constructor(map) { constructor(map, mode = null) {
this.parent = map._controlContainer this.parent = map._controlContainer
this.map = map this.map = map
this.container = DomUtil.create('div', '', this.parent) this.container = DomUtil.create('div', '', this.parent)
this.mode = 'condensed' this.mode = mode
this.classname = 'left' this.classname = 'left'
DomEvent.disableClickPropagation(this.container) DomEvent.disableClickPropagation(this.container)
DomEvent.on(this.container, 'contextmenu', DomEvent.stopPropagation) // Do not activate our custom context menu. DomEvent.on(this.container, 'contextmenu', DomEvent.stopPropagation) // Do not activate our custom context menu.
@ -14,8 +14,12 @@ export class Panel {
DomEvent.on(this.container, 'MozMousePixelScroll', DomEvent.stopPropagation) DomEvent.on(this.container, 'MozMousePixelScroll', DomEvent.stopPropagation)
} }
setDefaultMode(mode) {
if (!this.mode) this.mode = mode
}
open({ content, className, actions = [] } = {}) { open({ content, className, actions = [] } = {}) {
this.container.className = `with-transition panel ${this.classname} ${this.mode}` this.container.className = `with-transition panel ${this.classname} ${this.mode || ''}`
this.container.innerHTML = '' this.container.innerHTML = ''
const actionsContainer = DomUtil.create('ul', 'toolbox', this.container) const actionsContainer = DomUtil.create('ul', 'toolbox', this.container)
const body = DomUtil.create('div', 'body', this.container) const body = DomUtil.create('div', 'body', this.container)
@ -40,14 +44,14 @@ export class Panel {
} }
resize() { resize() {
if (this.mode === 'expanded') { if (this.mode === 'condensed') {
this.mode = 'condensed'
this.container.classList.remove('expanded')
this.container.classList.add('condensed')
} else {
this.mode = 'expanded' this.mode = 'expanded'
this.container.classList.remove('condensed') this.container.classList.remove('condensed')
this.container.classList.add('expanded') this.container.classList.add('expanded')
} else {
this.mode = 'condensed'
this.container.classList.remove('expanded')
this.container.classList.add('condensed')
} }
} }

View file

@ -52,6 +52,14 @@ export const SCHEMA = {
label: translate('Display the data layers control'), label: translate('Display the data layers control'),
default: true, default: true,
}, },
defaultPanelMode: {
type: String,
label: translate('Default panel mode'),
choices: [
['condensed', translate('Condensed')],
['expanded', translate('Expanded')],
],
},
defaultView: { defaultView: {
type: String, type: String,
impacts: [], // no need to update the ui, only useful when loading the map impacts: [], // no need to update the ui, only useful when loading the map

View file

@ -941,7 +941,11 @@ L.FormBuilder.MultiChoice = L.FormBuilder.Element.extend({
if (!this.container.querySelector(`input[type="radio"][value="${value}"]`)) { if (!this.container.querySelector(`input[type="radio"][value="${value}"]`)) {
value = this.options.default !== undefined ? this.options.default : this.default value = this.options.default !== undefined ? this.options.default : this.default
} }
this.container.querySelector(`input[type="radio"][value="${value}"]`).checked = true const choices = this.getChoices().map(([value, label]) => value)
if (choices.includes(value)) {
this.container.querySelector(`input[type="radio"][value="${value}"]`).checked =
true
}
}, },
value: function () { value: function () {

View file

@ -56,7 +56,7 @@ U.Map = L.Map.extend({
if (geojson.geometry) this.options.center = this.latLng(geojson.geometry) if (geojson.geometry) this.options.center = this.latLng(geojson.geometry)
this.urls = new U.URLs(this.options.urls) this.urls = new U.URLs(this.options.urls)
this.panel = new U.Panel(this) this.panel = new U.Panel(this, this.options.defaultPanelMode)
if (this.hasEditMode()) { if (this.hasEditMode()) {
this.editPanel = new U.EditPanel(this) this.editPanel = new U.EditPanel(this)
this.fullPanel = new U.FullPanel(this) this.fullPanel = new U.FullPanel(this)
@ -214,14 +214,16 @@ U.Map = L.Map.extend({
if (L.Util.queryString('share')) { if (L.Util.queryString('share')) {
this.share.open() this.share.open()
} else if (this.options.onLoadPanel === 'databrowser') { } else if (this.options.onLoadPanel === 'databrowser') {
this.panel.setDefaultMode('expanded')
this.openBrowser('data') this.openBrowser('data')
} else if (this.options.onLoadPanel === 'datalayers') { } else if (this.options.onLoadPanel === 'datalayers') {
this.panel.setDefaultMode('condensed')
this.openBrowser('layers') this.openBrowser('layers')
} else if (this.options.onLoadPanel === 'datafilters') { } else if (this.options.onLoadPanel === 'datafilters') {
this.panel.mode = 'expanded' this.panel.setDefaultMode('expanded')
this.openBrowser('filters') this.openBrowser('filters')
} else if (this.options.onLoadPanel === 'caption') { } else if (this.options.onLoadPanel === 'caption') {
this.panel.mode = 'condensed' this.panel.setDefaultMode('condensed')
this.openCaption() this.openCaption()
} }
if (L.Util.queryString('edit')) { if (L.Util.queryString('edit')) {
@ -1172,6 +1174,7 @@ U.Map = L.Map.extend({
'options.scaleControl', 'options.scaleControl',
'options.onLoadPanel', 'options.onLoadPanel',
'options.defaultView', 'options.defaultView',
'options.defaultPanelMode',
'options.displayPopupFooter', 'options.displayPopupFooter',
'options.captionBar', 'options.captionBar',
'options.captionMenus', 'options.captionMenus',

View file

@ -55,6 +55,7 @@ U.Popup.Panel = U.Popup.extend({
}, },
onAdd: function (map) { onAdd: function (map) {
map.panel.setDefaultMode('expanded')
map.panel.open({ map.panel.open({
content: this._content, content: this._content,
actions: [U.Browser.backButton(map)], actions: [U.Browser.backButton(map)],

View file

@ -1,3 +1,4 @@
import re
from copy import deepcopy from copy import deepcopy
from time import sleep from time import sleep
@ -63,13 +64,22 @@ def bootstrap(map, live_server):
def test_data_browser_should_be_open(live_server, page, bootstrap, map): def test_data_browser_should_be_open(live_server, page, bootstrap, map):
page.goto(f"{live_server.url}{map.get_absolute_url()}") page.goto(f"{live_server.url}{map.get_absolute_url()}")
el = page.locator(".umap-browser") panel = page.locator(".panel.left.on")
expect(el).to_be_visible() expect(panel).to_have_class(re.compile(".*expanded.*"))
expect(panel.locator(".umap-browser")).to_be_visible()
expect(page.get_by_text("one point in france")).to_be_visible() expect(page.get_by_text("one point in france")).to_be_visible()
expect(page.get_by_text("one line in new zeland")).to_be_visible() expect(page.get_by_text("one line in new zeland")).to_be_visible()
expect(page.get_by_text("one polygon in greenland")).to_be_visible() expect(page.get_by_text("one polygon in greenland")).to_be_visible()
def test_can_force_panel_mode(live_server, page, bootstrap, map):
map.settings["properties"]["defaultPanelMode"] = "condensed"
map.save()
page.goto(f"{live_server.url}{map.get_absolute_url()}")
panel = page.locator(".panel.left.on")
expect(panel).to_have_class(re.compile(".*condensed.*"))
def test_data_browser_should_be_filterable(live_server, page, bootstrap, map): def test_data_browser_should_be_filterable(live_server, page, bootstrap, map):
page.goto(f"{live_server.url}{map.get_absolute_url()}") page.goto(f"{live_server.url}{map.get_absolute_url()}")
expect(page.get_by_title("Features in this layer: 3")).to_be_visible() expect(page.get_by_title("Features in this layer: 3")).to_be_visible()

View file

@ -1,3 +1,5 @@
import re
import pytest import pytest
from playwright.sync_api import expect from playwright.sync_api import expect
@ -15,10 +17,20 @@ def test_caption(live_server, page, map):
) )
hidden = DataLayerFactory(map=map, name="Hidden", settings={"inCaption": False}) hidden = DataLayerFactory(map=map, name="Hidden", settings={"inCaption": False})
page.goto(f"{live_server.url}{map.get_absolute_url()}") page.goto(f"{live_server.url}{map.get_absolute_url()}")
panel = page.locator(".umap-caption") panel = page.locator(".panel.left.on")
expect(panel).to_be_visible() expect(panel).to_have_class(re.compile(".*condensed.*"))
expect(panel.locator(".umap-caption")).to_be_visible()
expect(panel.locator(".datalayer-legend").get_by_text(basic.name)).to_be_visible() expect(panel.locator(".datalayer-legend").get_by_text(basic.name)).to_be_visible()
expect( expect(
panel.locator(".datalayer-legend .off").get_by_text(non_loaded.name) panel.locator(".datalayer-legend .off").get_by_text(non_loaded.name)
).to_be_visible() ).to_be_visible()
expect(panel.locator(".datalayer-legend").get_by_text(hidden.name)).to_be_hidden() expect(panel.locator(".datalayer-legend").get_by_text(hidden.name)).to_be_hidden()
def test_can_force_panel_mode(live_server, page, map):
map.settings["properties"]["onLoadPanel"] = "caption"
map.settings["properties"]["defaultPanelMode"] = "expanded"
map.save()
page.goto(f"{live_server.url}{map.get_absolute_url()}")
panel = page.locator(".panel.left.on")
expect(panel).to_have_class(re.compile(".*expanded.*"))

View file

@ -1,4 +1,5 @@
import copy import copy
import re
import pytest import pytest
from playwright.sync_api import expect from playwright.sync_api import expect
@ -101,7 +102,9 @@ def test_simple_facet_search(live_server, page, map):
DataLayerFactory(map=map, data=DATALAYER_DATA2) DataLayerFactory(map=map, data=DATALAYER_DATA2)
DataLayerFactory(map=map, data=DATALAYER_DATA3) DataLayerFactory(map=map, data=DATALAYER_DATA3)
page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670") page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
panel = page.locator(".umap-browser") panel = page.locator(".panel.left.on")
expect(panel).to_have_class(re.compile(".*expanded.*"))
expect(panel.locator(".umap-browser")).to_be_visible()
# From a non browsable datalayer, should not be impacted # From a non browsable datalayer, should not be impacted
paths = page.locator(".leaflet-overlay-pane path") paths = page.locator(".leaflet-overlay-pane path")
expect(paths).to_be_visible() expect(paths).to_be_visible()

View file

@ -1,3 +1,4 @@
import re
from copy import deepcopy from copy import deepcopy
import pytest import pytest
@ -62,3 +63,31 @@ def test_should_display_tooltip_with_variable(live_server, map, page, bootstrap)
map.save() map.save()
page.goto(f"{live_server.url}{map.get_absolute_url()}") page.goto(f"{live_server.url}{map.get_absolute_url()}")
expect(page.get_by_text("Foo test marker")).to_be_visible() expect(page.get_by_text("Foo test marker")).to_be_visible()
def test_should_open_popup_panel_on_click(live_server, map, page, bootstrap):
map.settings["properties"]["popupShape"] = "Panel"
map.save()
page.goto(f"{live_server.url}{map.get_absolute_url()}")
panel = page.locator(".panel.left.on")
expect(panel).to_be_hidden()
page.locator(".leaflet-marker-icon").click()
expect(panel).to_be_visible()
expect(panel).to_have_class(re.compile(".*expanded.*"))
expect(panel.get_by_role("heading", name="test marker")).to_be_visible()
expect(panel.get_by_text("Some description")).to_be_visible()
# Close popup
page.locator("#map").click()
expect(panel).to_be_hidden()
def test_can_force_popup_panel_mode(live_server, map, page, bootstrap):
map.settings["properties"]["popupShape"] = "Panel"
map.settings["properties"]["defaultPanelMode"] = "condensed"
map.save()
page.goto(f"{live_server.url}{map.get_absolute_url()}")
panel = page.locator(".panel.left.on")
expect(panel).to_be_hidden()
page.locator(".leaflet-marker-icon").click()
expect(panel).to_be_visible()
expect(panel).to_have_class(re.compile(".*condensed.*"))