diff --git a/umap/static/umap/js/modules/rendering/template.js b/umap/static/umap/js/modules/rendering/template.js index 33fbfb9d..f14b7eeb 100644 --- a/umap/static/umap/js/modules/rendering/template.js +++ b/umap/static/umap/js/modules/rendering/template.js @@ -152,6 +152,19 @@ class GeoRSSLink extends PopupTemplate { } class OSM extends TitleMixin(PopupTemplate) { + renderTitle(feature) { + const title = DomUtil.add('h3', 'popup-title') + const color = feature.getPreviewColor() + title.style.backgroundColor = color + const iconUrl = feature.getDynamicOption('iconUrl') + const icon = Icon.makeElement(iconUrl, title) + DomUtil.addClass(icon, 'icon') + Icon.setContrast(icon, title, iconUrl, color) + if (DomUtil.contrastedColor(title, color)) title.style.color = 'white' + DomUtil.add('span', '', title, this.getName(feature)) + return title + } + getName(feature) { const props = feature.properties const locale = getLocale() @@ -162,15 +175,6 @@ class OSM extends TitleMixin(PopupTemplate) { renderBody(feature) { const props = feature.properties const body = document.createElement('div') - const title = DomUtil.add('h3', 'popup-title', container) - const color = feature.getPreviewColor() - title.style.backgroundColor = color - const iconUrl = feature.getDynamicOption('iconUrl') - const icon = Icon.makeElement(iconUrl, title) - DomUtil.addClass(icon, 'icon') - Icon.setContrast(icon, title, iconUrl, color) - if (DomUtil.contrastedColor(title, color)) title.style.color = 'white' - DomUtil.add('span', '', title, this.getName(feature)) const street = props['addr:street'] if (street) { const row = DomUtil.add('address', 'address', body) @@ -207,6 +211,13 @@ class OSM extends TitleMixin(PopupTemplate) { Utils.loadTemplate(`
${email}
`) ) } + if (props.panoramax) { + body.appendChild( + Utils.loadTemplate( + `
` + ) + ) + } const id = props['@id'] || props.id if (id) { body.appendChild( diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js index e7cf3cd5..0ddc7cba 100644 --- a/umap/static/umap/js/umap.core.js +++ b/umap/static/umap/js/umap.core.js @@ -170,22 +170,43 @@ L.DomUtil.contrastWCAG21 = (rgb) => { const contrast = (whiteLum + 0.05) / (lum + 0.05) return contrast > 3 ? 1 : 0 } +L.DomUtil.colorNameToHex = (str) => { + const ctx = document.createElement('canvas').getContext('2d') + ctx.fillStyle = str + return ctx.fillStyle +} +L.DomUtil.hexToRGB = (hex) => { + return hex + .replace( + /^#?([a-f\d])([a-f\d])([a-f\d])$/i, + (m, r, g, b) => `#${r}${r}${g}${g}${b}${b}` + ) + .substring(1) + .match(/.{2}/g) + .map((x) => Number.parseInt(x, 16)) +} const _CACHE_CONSTRAST = {} L.DomUtil.contrastedColor = (el, bgcolor) => { // Return 0 for black and 1 for white // bgcolor is a human color, it can be a any keyword (purple…) if (typeof _CACHE_CONSTRAST[bgcolor] !== 'undefined') return _CACHE_CONSTRAST[bgcolor] - let out = 0 let rgb = window.getComputedStyle(el).getPropertyValue('background-color') rgb = L.DomUtil.RGBRegex.exec(rgb) - if (!rgb || rgb.length !== 4) return out - rgb = [ - Number.parseInt(rgb[1], 10), - Number.parseInt(rgb[2], 10), - Number.parseInt(rgb[3], 10), - ] - out = L.DomUtil.contrastWCAG21(rgb) + if (rgb && rgb.length === 4) { + rgb = [ + Number.parseInt(rgb[1], 10), + Number.parseInt(rgb[2], 10), + Number.parseInt(rgb[3], 10), + ] + } else { + // The element may not yet be added to the DOM, so let's try + // another way + const hex = L.DomUtil.colorNameToHex(bgcolor) + rgb = L.DomUtil.hexToRGB(hex) + } + if (!rgb) return 1 + const out = L.DomUtil.contrastWCAG21(rgb) if (bgcolor) _CACHE_CONSTRAST[bgcolor] = out return out } diff --git a/umap/tests/integration/test_popup.py b/umap/tests/integration/test_popup.py new file mode 100644 index 00000000..23d70aab --- /dev/null +++ b/umap/tests/integration/test_popup.py @@ -0,0 +1,44 @@ +import pytest +from playwright.sync_api import expect + +from ..base import DataLayerFactory + +pytestmark = pytest.mark.django_db + +OSM_DATA = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [2.49, 48.79]}, + "properties": { + "amenity": "restaurant", + "cuisine": "italian", + "name": "A Casa di Nonna", + "panoramax": "d811b398-d930-4cf8-95a2-0c29c34d9fca", + "phone": "+33 1 48 89 54 12", + "takeaway:covid19": "yes", + "wheelchair": "no", + "id": "node/1130849864", + }, + "id": "AzMjk", + }, + ], + "_umap_options": { + "popupTemplate": "OSM", + }, +} + + +def test_openstreetmap_popup(live_server, map, page): + DataLayerFactory(map=map, data=OSM_DATA) + page.goto(f"{live_server.url}{map.get_absolute_url()}#18/48.79/2.49") + expect(page.locator(".umap-icon-active")).to_be_hidden() + page.locator(".leaflet-marker-icon").click() + expect(page.get_by_role("heading", name="A Casa di Nonna")).to_be_visible() + expect(page.get_by_text("+33 1 48 89 54 12")).to_be_visible() + img = page.locator(".umap-popup-content img") + expect(img).to_have_attribute( + "src", + "https://api.panoramax.xyz/api/pictures/d811b398-d930-4cf8-95a2-0c29c34d9fca/sd.jpg", + )