Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Malvoz attribution control #815

Merged
merged 16 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/mapml-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,6 @@ export class MapViewer extends HTMLElement {
fadeAnimation: true
});
this._addToHistory();
// the attribution control is not optional
M.attributionControl(this);

this._createControls();
this._toggleControls();
Expand Down
45 changes: 39 additions & 6 deletions src/mapml.css
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,9 @@
.leaflet-tooltip-pane .leaflet-tooltip,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar,
.leaflet-popup-tip {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 2px rgb(0, 0, 0, 0.3);
}

/* Increase contrast between the attribution container and the underlying content. */
.leaflet-popup-tip,
.leaflet-container .leaflet-control-attribution {
background-color: rgba(255, 255, 255, 0.95);
box-shadow: rgb(0 0 0 / 30%) 0px 1px 4px -1px;
}

/* Remove opinionated styles of attribution links. */
Expand All @@ -168,6 +164,43 @@
text-decoration: revert;
}


/*
* Attribution.
*/

.leaflet-container .leaflet-control-attribution {
background-color: #fff;
border-radius: 1.5em;
color: currentColor;
margin: 5px;
min-height: 30px;
min-width: 30px;
padding: 0;
}

.leaflet-control-attribution summary {
display: block;
list-style: none;
border-radius: 100%;
position: absolute;
bottom: 0;
left: calc(100% - 30px);
line-height: 0;
width: 30px;
height: 30px;
}

.leaflet-control-attribution summary svg {
border-radius: 100%;
width: inherit;
height: inherit;
}

.mapml-attribution-container {
padding: 5px 35px 5px 10px;
}

/*
* Zoom controls.
*/
Expand Down
73 changes: 73 additions & 0 deletions src/mapml/control/AttributionButton.js
prushforth marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
export var AttributionButton = L.Control.Attribution.extend({
options: {
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> '
},

onAdd: function (map) {
map.attributionControl = this;
this._container = L.DomUtil.create('details', 'leaflet-control-attribution');
L.DomEvent.disableClickPropagation(this._container);

for (var i in map._layers) {
if (map._layers[i].getAttribution) {
this.addAttribution(map._layers[i].getAttribution());
}
}

this._update();

map.on('layeradd', this._addAttribution, this);

let dialog = document.createElement("dialog");
dialog.setAttribute("class", "shortcuts-dialog");
dialog.setAttribute("autofocus", "");
dialog.onclick = function (e) {
e.stopPropagation();
};
dialog.innerHTML = `<b>${M.options.locale.kbdShortcuts} </b><button aria-label="Close" onclick='this.parentElement.close()'>X</button>` +
`<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>` +
`<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>`;
map._container.appendChild(dialog);

return this._container;
},

_update: function () {
if (!this._map) { return; }

var attribs = [];

for (var i in this._attributions) {
if (this._attributions[i]) {
attribs.push(i);
}
}

var prefixAndAttribs = [];

if (this.options.prefix) {
prefixAndAttribs.push(this.options.prefix);
}
if (attribs.length) {
prefixAndAttribs.push(attribs.join(', '));
}
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>';
this._container.setAttribute("role","group");
this._container.setAttribute("aria-label",`${M.options.locale.btnAttribution}`);
}
});

L.Map.mergeOptions({
attributionControl: false,
toggleableAttributionControl: true
});

L.Map.addInitHook(function () {
if (this.options.toggleableAttributionControl) {
new AttributionButton().addTo(this);
}
});

export var attributionButton = function (options) {
return new AttributionButton(options);
};
17 changes: 0 additions & 17 deletions src/mapml/control/AttributionControl.js

This file was deleted.

13 changes: 7 additions & 6 deletions src/mapml/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,20 @@ import { DebugOverlay, debugOverlay} from './layers/DebugOverlay';
import { QueryHandler } from './handlers/QueryHandler';
import { ContextMenu } from './handlers/ContextMenu';
import { Util } from './utils/Util';
import { AttributionButton, attributionButton } from './control/AttributionButton';
import { ReloadButton, reloadButton } from './control/ReloadButton';
import { ScaleBar, scaleBar } from './control/ScaleBar';
import { FullscreenButton, fullscreenButton } from './control/FullscreenButton';
import { attributionControl } from "./control/AttributionControl";
import {geolocationButton} from "./control/GeolocationButton";
import { geolocationButton } from "./control/GeolocationButton";
import { Crosshair, crosshair } from "./layers/Crosshair";
import { Feature, feature } from "./features/feature";
import { FeatureRenderer, featureRenderer } from './features/featureRenderer';
import { FeatureGroup, featureGroup} from './features/featureGroup';
import {AnnounceMovement} from "./handlers/AnnounceMovement";
import { AnnounceMovement } from "./handlers/AnnounceMovement";
import { FeatureIndex } from "./handlers/FeatureIndex";
import { Options } from "./options";
import "./keyboard";
import {featureIndexOverlay, FeatureIndexOverlay} from "./layers/FeatureIndexOverlay";
import { featureIndexOverlay, FeatureIndexOverlay } from "./layers/FeatureIndexOverlay";

/* global L, Node */
(function (window, document, undefined) {
Expand Down Expand Up @@ -653,6 +653,9 @@ M.featureLayer = featureLayer;
M.LayerControl = LayerControl;
M.layerControl = layerControl;

M.AttributionButton = AttributionButton;
M.attributionButton = attributionButton;

M.ReloadButton = ReloadButton;
M.reloadButton = reloadButton;

Expand All @@ -662,8 +665,6 @@ M.scaleBar = scaleBar;
M.FullscreenButton = FullscreenButton;
M.fullscreenButton = fullscreenButton;

M.attributionControl = attributionControl;

M.geolocationButton = geolocationButton;

M.StaticTileLayer = StaticTileLayer;
Expand Down
1 change: 1 addition & 0 deletions src/mapml/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export var Options = {
lmZoomToLayer: "Zoom To Layer",
lmCopyLayer: "Copy Layer",
lcOpacity: "Opacity",
btnAttribution: "Map data attribution",
btnZoomIn: "Zoom in",
btnZoomOut: "Zoom out",
btnFullScreen: "View Fullscreen",
Expand Down
2 changes: 0 additions & 2 deletions src/web-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,6 @@ export class WebMap extends HTMLMapElement {
fadeAnimation: true
});
this._addToHistory();
// the attribution control is not optional
M.attributionControl(this);

this._createControls();
this._toggleControls();
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/core/kbdAttribution.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ test.describe("Keyboard shortcut attribution test", ()=> {
await page.keyboard.press("Tab");
}

await page.keyboard.press("Enter");
await page.keyboard.press("Tab");
await page.keyboard.press("Enter");
const dialog = await page.$eval("body > mapml-viewer div > dialog",
(dialog) => dialog.hasAttribute("open")
Expand Down
6 changes: 3 additions & 3 deletions test/e2e/core/mapElement.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ test.describe("Playwright Map Element Tests", () => {
await expect(projection).toEqual("OSMTILE");
});
test("Ensure attribution control has role='group' aria-label='Map data attribution'", async () => {
let role = await page.evaluate(`document.querySelector('map')._attributionControl.getContainer().getAttribute('role')`);
let role = await page.evaluate(`document.querySelector('map')._map.attributionControl.getContainer().getAttribute('role')`);
await expect(role).toEqual("group");
let arialabel = await page.evaluate(`document.querySelector('map')._attributionControl.getContainer().getAttribute('aria-label')`);
let arialabel = await page.evaluate(`document.querySelector('map')._map.attributionControl.getContainer().getAttribute('aria-label')`);
await expect(arialabel).toEqual("Map data attribution");
});
});
});
18 changes: 13 additions & 5 deletions test/e2e/core/popupTabNavigation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,17 @@ test.describe("Playwright Keyboard Navigation + Query Layer Tests" , () => {
});

test("Focus Controls focuses the first <button> child in control div", async () => {
await page.waitForTimeout(1000);
await page.click("body > mapml-viewer");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
await page.keyboard.press("Enter");
await page.click("body > mapml-viewer");
await page.keyboard.press("Shift+F10");
await page.keyboard.press("t");
Expand All @@ -201,11 +212,8 @@ test.describe("Playwright Keyboard Navigation + Query Layer Tests" , () => {
for (let i = 0; i < 5; i++)
await page.keyboard.press("Tab");
await page.keyboard.press("Enter");
const h = await page.evaluateHandle(() => document.querySelector("mapml-viewer"));
const nh = await page.evaluateHandle(doc => doc.shadowRoot, h);
const rh = await page.evaluateHandle(root => root.activeElement, nh);
const f = await (await page.evaluateHandle(elem => elem.innerText, rh)).jsonValue();
expect(f).toEqual("Maps4HTML");
let f = await page.$eval("body > mapml-viewer", (viewer) => viewer.shadowRoot.activeElement.innerHTML);
expect(f).toEqual("Maps for HTML Community Group");
});
});
});
4 changes: 2 additions & 2 deletions test/e2e/mapml-viewer/mapml-viewer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ test.describe("Playwright mapml-viewer Element Tests", () => {
});

test("Ensure attribution control has role='group' aria-label='Map data attribution'", async () => {
let role = await page.evaluate(`document.querySelector('mapml-viewer')._attributionControl.getContainer().getAttribute('role')`);
let role = await page.evaluate(`document.querySelector('mapml-viewer')._map.attributionControl._container.getAttribute('role')`);
expect(role).toEqual("group");
let arialabel = await page.evaluate(`document.querySelector('mapml-viewer')._attributionControl.getContainer().getAttribute('aria-label')`);
let arialabel = await page.evaluate(`document.querySelector('mapml-viewer')._map.attributionControl._container.getAttribute('aria-label')`);
expect(arialabel).toEqual("Map data attribution");
prushforth marked this conversation as resolved.
Show resolved Hide resolved
});

Expand Down