Skip to content

Commit fc0c8da

Browse files
prushforthMalvozJacky0299prushfor
authored
Malvoz attribution control (#815)
* Collapse attribution behind a button * Update attribution * Change the attribution control SVG to dark on light * attribution control * tests fix * Aliyan suggested changes * deleted comments * test fix * one flaky test * Restore and fix old test * Peter suggested changes * string localized * Get rid of unused file, formatting * Make sure aria-label has localized string, get rid of title to eliminate duplicate announcing --------- Co-authored-by: Malvoz <[email protected]> Co-authored-by: Jacky0299 <[email protected]> Co-authored-by: Jacky0299 <[email protected]> Co-authored-by: prushfor <[email protected]>
1 parent 8370cac commit fc0c8da

File tree

11 files changed

+140
-43
lines changed

11 files changed

+140
-43
lines changed

src/mapml-viewer.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,6 @@ export class MapViewer extends HTMLElement {
265265
fadeAnimation: true
266266
});
267267
this._addToHistory();
268-
// the attribution control is not optional
269-
M.attributionControl(this);
270268

271269
this._createControls();
272270
this._toggleControls();

src/mapml.css

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,9 @@
153153
.leaflet-tooltip-pane .leaflet-tooltip,
154154
.leaflet-touch .leaflet-control-layers,
155155
.leaflet-touch .leaflet-bar,
156-
.leaflet-popup-tip {
157-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 2px rgb(0, 0, 0, 0.3);
158-
}
159-
160-
/* Increase contrast between the attribution container and the underlying content. */
156+
.leaflet-popup-tip,
161157
.leaflet-container .leaflet-control-attribution {
162-
background-color: rgba(255, 255, 255, 0.95);
158+
box-shadow: rgb(0 0 0 / 30%) 0px 1px 4px -1px;
163159
}
164160

165161
/* Remove opinionated styles of attribution links. */
@@ -168,6 +164,43 @@
168164
text-decoration: revert;
169165
}
170166

167+
168+
/*
169+
* Attribution.
170+
*/
171+
172+
.leaflet-container .leaflet-control-attribution {
173+
background-color: #fff;
174+
border-radius: 1.5em;
175+
color: currentColor;
176+
margin: 5px;
177+
min-height: 30px;
178+
min-width: 30px;
179+
padding: 0;
180+
}
181+
182+
.leaflet-control-attribution summary {
183+
display: block;
184+
list-style: none;
185+
border-radius: 100%;
186+
position: absolute;
187+
bottom: 0;
188+
left: calc(100% - 30px);
189+
line-height: 0;
190+
width: 30px;
191+
height: 30px;
192+
}
193+
194+
.leaflet-control-attribution summary svg {
195+
border-radius: 100%;
196+
width: inherit;
197+
height: inherit;
198+
}
199+
200+
.mapml-attribution-container {
201+
padding: 5px 35px 5px 10px;
202+
}
203+
171204
/*
172205
* Zoom controls.
173206
*/
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
export var AttributionButton = L.Control.Attribution.extend({
2+
options: {
3+
prefix: '<a href="https://www.w3.org/community/maps4html/">Maps for HTML Community Group</a> | <img src="data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTIiIGhlaWdodD0iOCI+PHBhdGggZmlsbD0iIzRDN0JFMSIgZD0iTTAgMGgxMnY0SDB6Ii8+PHBhdGggZmlsbD0iI0ZGRDUwMCIgZD0iTTAgNGgxMnYzSDB6Ii8+PHBhdGggZmlsbD0iI0UwQkMwMCIgZD0iTTAgN2gxMnYxSDB6Ii8+PC9zdmc+" style="padding-right: 0.3em;" alt="Slava Ukraini"> <a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a> '
4+
},
5+
6+
onAdd: function (map) {
7+
map.attributionControl = this;
8+
this._container = L.DomUtil.create('details', 'leaflet-control-attribution');
9+
L.DomEvent.disableClickPropagation(this._container);
10+
11+
for (var i in map._layers) {
12+
if (map._layers[i].getAttribution) {
13+
this.addAttribution(map._layers[i].getAttribution());
14+
}
15+
}
16+
17+
this._update();
18+
19+
map.on('layeradd', this._addAttribution, this);
20+
21+
let dialog = document.createElement("dialog");
22+
dialog.setAttribute("class", "shortcuts-dialog");
23+
dialog.setAttribute("autofocus", "");
24+
dialog.onclick = function (e) {
25+
e.stopPropagation();
26+
};
27+
dialog.innerHTML = `<b>${M.options.locale.kbdShortcuts} </b><button aria-label="Close" onclick='this.parentElement.close()'>X</button>` +
28+
`<ul><b>${M.options.locale.kbdMovement}</b><li><kbd>&#8593</kbd> ${M.options.locale.kbdPanUp}</li><li><kbd>&#8595</kbd> ${M.options.locale.kbdPanDown}</li><li><kbd>&#8592</kbd> ${M.options.locale.kbdPanLeft}</li><li><kbd>&#8594</kbd> ${M.options.locale.kbdPanRight}</li><li><kbd>+</kbd> ${M.options.locale.btnZoomIn}</li><li><kbd>-</kbd> ${M.options.locale.btnZoomOut}</li><li><kbd>shift</kbd> + <kbd>&#8592/&#8593/&#8594/&#8595</kbd> 3x ${M.options.locale.kbdPanIncrement}</li><li><kbd>ctrl</kbd> + <kbd>&#8592/&#8593/&#8594/&#8595</kbd> 0.2x ${M.options.locale.kbdPanIncrement}</li><li><kbd>shift</kbd> + <kbd>+/-</kbd> ${M.options.locale.kbdZoom}</li></ul>` +
29+
`<ul><b>${M.options.locale.kbdFeature}</b><li><kbd>&#8592/&#8593</kbd> ${M.options.locale.kbdPrevFeature}</li><li><kbd>&#8594/&#8595</kbd> ${M.options.locale.kbdNextFeature}</li></ul>`;
30+
map._container.appendChild(dialog);
31+
32+
return this._container;
33+
},
34+
35+
_update: function () {
36+
if (!this._map) { return; }
37+
38+
var attribs = [];
39+
40+
for (var i in this._attributions) {
41+
if (this._attributions[i]) {
42+
attribs.push(i);
43+
}
44+
}
45+
46+
var prefixAndAttribs = [];
47+
48+
if (this.options.prefix) {
49+
prefixAndAttribs.push(this.options.prefix);
50+
}
51+
if (attribs.length) {
52+
prefixAndAttribs.push(attribs.join(', '));
53+
}
54+
this._container.innerHTML = `<summary><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 0 24 24" width="30px" fill="currentColor"><path d="M0 0h24v24H0V0z" fill="none"></path><path d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"></path></svg></summary>` + '<div class="mapml-attribution-container">' + `<button onclick="this.closest(\'.leaflet-container\').querySelector(\'.shortcuts-dialog\').showModal()" class="shortcuts-button mapml-button">${M.options.locale.kbdShortcuts}</button> | ` + prefixAndAttribs.join(' <span aria-hidden="true">|</span> ') + '</div>';
55+
this._container.setAttribute("role","group");
56+
this._container.setAttribute("aria-label",`${M.options.locale.btnAttribution}`);
57+
}
58+
});
59+
60+
L.Map.mergeOptions({
61+
attributionControl: false,
62+
toggleableAttributionControl: true
63+
});
64+
65+
L.Map.addInitHook(function () {
66+
if (this.options.toggleableAttributionControl) {
67+
new AttributionButton().addTo(this);
68+
}
69+
});
70+
71+
export var attributionButton = function (options) {
72+
return new AttributionButton(options);
73+
};

src/mapml/control/AttributionControl.js

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/mapml/index.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,20 @@ import { DebugOverlay, debugOverlay} from './layers/DebugOverlay';
5151
import { QueryHandler } from './handlers/QueryHandler';
5252
import { ContextMenu } from './handlers/ContextMenu';
5353
import { Util } from './utils/Util';
54+
import { AttributionButton, attributionButton } from './control/AttributionButton';
5455
import { ReloadButton, reloadButton } from './control/ReloadButton';
5556
import { ScaleBar, scaleBar } from './control/ScaleBar';
5657
import { FullscreenButton, fullscreenButton } from './control/FullscreenButton';
57-
import { attributionControl } from "./control/AttributionControl";
58-
import {geolocationButton} from "./control/GeolocationButton";
58+
import { geolocationButton } from "./control/GeolocationButton";
5959
import { Crosshair, crosshair } from "./layers/Crosshair";
6060
import { Feature, feature } from "./features/feature";
6161
import { FeatureRenderer, featureRenderer } from './features/featureRenderer';
6262
import { FeatureGroup, featureGroup} from './features/featureGroup';
63-
import {AnnounceMovement} from "./handlers/AnnounceMovement";
63+
import { AnnounceMovement } from "./handlers/AnnounceMovement";
6464
import { FeatureIndex } from "./handlers/FeatureIndex";
6565
import { Options } from "./options";
6666
import "./keyboard";
67-
import {featureIndexOverlay, FeatureIndexOverlay} from "./layers/FeatureIndexOverlay";
67+
import { featureIndexOverlay, FeatureIndexOverlay } from "./layers/FeatureIndexOverlay";
6868

6969
/* global L, Node */
7070
(function (window, document, undefined) {
@@ -653,6 +653,9 @@ M.featureLayer = featureLayer;
653653
M.LayerControl = LayerControl;
654654
M.layerControl = layerControl;
655655

656+
M.AttributionButton = AttributionButton;
657+
M.attributionButton = attributionButton;
658+
656659
M.ReloadButton = ReloadButton;
657660
M.reloadButton = reloadButton;
658661

@@ -662,8 +665,6 @@ M.scaleBar = scaleBar;
662665
M.FullscreenButton = FullscreenButton;
663666
M.fullscreenButton = fullscreenButton;
664667

665-
M.attributionControl = attributionControl;
666-
667668
M.geolocationButton = geolocationButton;
668669

669670
M.StaticTileLayer = StaticTileLayer;

src/mapml/options.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export var Options = {
1919
lmZoomToLayer: "Zoom To Layer",
2020
lmCopyLayer: "Copy Layer",
2121
lcOpacity: "Opacity",
22+
btnAttribution: "Map data attribution",
2223
btnZoomIn: "Zoom in",
2324
btnZoomOut: "Zoom out",
2425
btnFullScreen: "View Fullscreen",

src/web-map.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,6 @@ export class WebMap extends HTMLMapElement {
275275
fadeAnimation: true
276276
});
277277
this._addToHistory();
278-
// the attribution control is not optional
279-
M.attributionControl(this);
280278

281279
this._createControls();
282280
this._toggleControls();

test/e2e/core/kbdAttribution.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ test.describe("Keyboard shortcut attribution test", ()=> {
1818
await page.keyboard.press("Tab");
1919
}
2020

21+
await page.keyboard.press("Enter");
22+
await page.keyboard.press("Tab");
2123
await page.keyboard.press("Enter");
2224
const dialog = await page.$eval("body > mapml-viewer div > dialog",
2325
(dialog) => dialog.hasAttribute("open")

test/e2e/core/mapElement.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ test.describe("Playwright Map Element Tests", () => {
9090
await expect(projection).toEqual("OSMTILE");
9191
});
9292
test("Ensure attribution control has role='group' aria-label='Map data attribution'", async () => {
93-
let role = await page.evaluate(`document.querySelector('map')._attributionControl.getContainer().getAttribute('role')`);
93+
let role = await page.evaluate(`document.querySelector('map')._map.attributionControl.getContainer().getAttribute('role')`);
9494
await expect(role).toEqual("group");
95-
let arialabel = await page.evaluate(`document.querySelector('map')._attributionControl.getContainer().getAttribute('aria-label')`);
95+
let arialabel = await page.evaluate(`document.querySelector('map')._map.attributionControl.getContainer().getAttribute('aria-label')`);
9696
await expect(arialabel).toEqual("Map data attribution");
9797
});
98-
});
98+
});

test/e2e/core/popupTabNavigation.test.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,17 @@ test.describe("Playwright Keyboard Navigation + Query Layer Tests" , () => {
191191
});
192192

193193
test("Focus Controls focuses the first <button> child in control div", async () => {
194+
await page.waitForTimeout(1000);
195+
await page.click("body > mapml-viewer");
196+
await page.keyboard.press("Tab");
197+
await page.keyboard.press("Tab");
198+
await page.keyboard.press("Tab");
199+
await page.keyboard.press("Tab");
200+
await page.keyboard.press("Tab");
201+
await page.keyboard.press("Tab");
202+
await page.keyboard.press("Tab");
203+
await page.keyboard.press("Tab");
204+
await page.keyboard.press("Enter");
194205
await page.click("body > mapml-viewer");
195206
await page.keyboard.press("Shift+F10");
196207
await page.keyboard.press("t");
@@ -201,11 +212,8 @@ test.describe("Playwright Keyboard Navigation + Query Layer Tests" , () => {
201212
for (let i = 0; i < 5; i++)
202213
await page.keyboard.press("Tab");
203214
await page.keyboard.press("Enter");
204-
const h = await page.evaluateHandle(() => document.querySelector("mapml-viewer"));
205-
const nh = await page.evaluateHandle(doc => doc.shadowRoot, h);
206-
const rh = await page.evaluateHandle(root => root.activeElement, nh);
207-
const f = await (await page.evaluateHandle(elem => elem.innerText, rh)).jsonValue();
208-
expect(f).toEqual("Maps4HTML");
215+
let f = await page.$eval("body > mapml-viewer", (viewer) => viewer.shadowRoot.activeElement.innerHTML);
216+
expect(f).toEqual("Maps for HTML Community Group");
209217
});
210218
});
211219
});

0 commit comments

Comments
 (0)