diff --git a/src/mapml.css b/src/mapml.css
index b6a92bf44..10f2139c5 100644
--- a/src/mapml.css
+++ b/src/mapml.css
@@ -95,6 +95,19 @@ the browser)' */
color: revert;
}
+.mapml-layer-item-legend-link {
+ display: inline-block;
+ margin-block-start: .25rem;
+}
+
+.mapml-layer-item-legend-image {
+ display: block;
+ max-width: min(100%, 16rem);
+ height: auto;
+ border: 1px solid #e3e3e3;
+ border-radius: 2px;
+}
+
.leaflet-top .leaflet-control {
margin-top: 5px;
}
@@ -801,7 +814,7 @@ label.mapml-layer-item-toggle {
padding-block-start: .25rem;
padding-block-end: .25rem;
padding-inline-start: .25rem;
- padding-inline-end: 1rem;
+ display: inline-block;
}
.mapml-layer-item-settings > * {
diff --git a/src/mapml/elementSupport/layers/createLayerControlForLayer.js b/src/mapml/elementSupport/layers/createLayerControlForLayer.js
index 494a19a46..6f7bc572e 100644
--- a/src/mapml/elementSupport/layers/createLayerControlForLayer.js
+++ b/src/mapml/elementSupport/layers/createLayerControlForLayer.js
@@ -139,12 +139,38 @@ export var createLayerControlHTML = async function () {
};
input.addEventListener('change', changeCheck.bind(this));
if (this._layer._legendUrl) {
- var legendLink = document.createElement('a');
- legendLink.text = ' ' + this._layer._title;
+ layerItemName.innerText = this._layer._title;
+
+ let legendControl = DomUtil.create(
+ 'details',
+ 'mapml-layer-item-legend mapml-control-layers',
+ layerItemSettings
+ ),
+ legendSummary = DomUtil.create('summary'),
+ legendLink = document.createElement('a'),
+ legendImage = document.createElement('img');
+
+ legendSummary.innerText = mapEl.locale.lmLegend;
+ legendControl.appendChild(legendSummary);
+
legendLink.href = this._layer._legendUrl;
legendLink.target = '_blank';
+ legendLink.rel = 'noopener noreferrer';
legendLink.draggable = false;
- layerItemName.appendChild(legendLink);
+ legendLink.className = 'mapml-layer-item-legend-link';
+
+ legendImage.src = this._layer._legendUrl;
+ legendImage.alt = `${this._layer._title} legend`;
+ legendImage.loading = 'lazy';
+ legendImage.decoding = 'async';
+ legendImage.className = 'mapml-layer-item-legend-image';
+ legendImage.addEventListener('error', () => {
+ legendLink.textContent = mapEl.locale.lmOpenInNewTab;
+ legendImage.remove();
+ });
+
+ legendLink.appendChild(legendImage);
+ legendControl.appendChild(legendLink);
} else {
layerItemName.innerHTML = this._layer._title;
}
diff --git a/test/e2e/layers/layerLegend.html b/test/e2e/layers/layerLegend.html
new file mode 100644
index 000000000..ea3425f1d
--- /dev/null
+++ b/test/e2e/layers/layerLegend.html
@@ -0,0 +1,60 @@
+
+
+
+
+ Layer Legend Control Tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -75.697193 45.421530
+
+
+
+
+
+
+
diff --git a/test/e2e/layers/layerLegend.test.js b/test/e2e/layers/layerLegend.test.js
new file mode 100644
index 000000000..6339da207
--- /dev/null
+++ b/test/e2e/layers/layerLegend.test.js
@@ -0,0 +1,93 @@
+import { test, expect, chromium } from '@playwright/test';
+
+test.describe('Layer legend tests', () => {
+ let page;
+ let context;
+
+ test.beforeAll(async () => {
+ context = await chromium.launchPersistentContext('', { slowMo: 250 });
+ page = await context.newPage();
+ await page.goto('layerLegend.html');
+ await page.locator('mapml-viewer').hover();
+ });
+
+ test.afterAll(async () => {
+ await context.close();
+ });
+
+ test('Legend layer has legend details in layer settings', async () => {
+ // Get the first layer in the overlay list (the one with an img legend)
+ const layer = page
+ .locator('.leaflet-control-layers-overlays > fieldset')
+ .first();
+
+ // Open the settings for that layer and check that the legend details are present
+ const settings = layer.locator('.mapml-layer-item-settings');
+
+ // Check that the legend details, name, and link are present and correct
+ const legendDetails = settings.locator('details.mapml-layer-item-legend');
+ await expect(legendDetails).toHaveCount(1);
+ await expect(legendDetails.locator('summary')).toHaveText('Legend');
+ await expect(
+ legendDetails.locator('a.mapml-layer-item-legend-link')
+ ).toHaveAttribute(
+ 'href',
+ 'http://maps.geogratis.gc.ca/wms/toporama_en?SERVICE=WMS&REQUEST=GetLegendGraphic&LAYER=WMS-Toporama&VERSION=1.1&FORMAT=image/png'
+ );
+ await expect(
+ legendDetails.locator('img.mapml-layer-item-legend-image')
+ ).toHaveAttribute(
+ 'src',
+ 'http://maps.geogratis.gc.ca/wms/toporama_en?SERVICE=WMS&REQUEST=GetLegendGraphic&LAYER=WMS-Toporama&VERSION=1.1&FORMAT=image/png'
+ );
+
+ // check that the legend details are the second details element in the settings
+ const secondDetails = settings.locator('> details').nth(1);
+ await expect(secondDetails).toHaveClass(
+ 'mapml-layer-item-legend mapml-control-layers'
+ );
+ });
+
+ test('Layer without legend does not render legend details in settings', async () => {
+ // Get the second layer in the overlay list (the one without a legend)
+ const layer = page
+ .locator('.leaflet-control-layers-overlays > fieldset')
+ .nth(1);
+
+ // check that the settings for that layer do not contain any legend details
+ const settings = layer.locator('.mapml-layer-item-settings');
+ await expect(
+ settings.locator('details.mapml-layer-item-legend')
+ ).toHaveCount(0);
+ });
+
+ test('Layer with a non img legend renders a legend link', async () => {
+ // Get the third layer in the overlay list (the one with a non img legend)
+ const layer = page
+ .locator('.leaflet-control-layers-overlays > fieldset')
+ .nth(2);
+
+ // check that the settings for that layer contain a legend link
+ const settings = layer.locator('.mapml-layer-item-settings');
+
+ // Check that the legend details, name, and link are present and correct
+ const legendDetails = settings.locator('details.mapml-layer-item-legend');
+ await expect(legendDetails).toHaveCount(1);
+ await expect(legendDetails.locator('summary')).toHaveText('Legend');
+ await expect(
+ legendDetails.locator('a.mapml-layer-item-legend-link')
+ ).toHaveAttribute('href', 'https://maps4html.org/web-map-doc/');
+
+ // not working, need to manually open the legend for it to update.
+ /*await expect(
+ legendDetails.locator('a.mapml-layer-item-legend-link')
+ ).toHaveText("Open Legend");
+ */
+
+ // check that the legend details are the second details element in the settings
+ const secondDetails = settings.locator('> details').nth(1);
+ await expect(secondDetails).toHaveClass(
+ 'mapml-layer-item-legend mapml-control-layers'
+ );
+ });
+});