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

Create <map-caption> custom element #741

Merged
merged 15 commits into from
Feb 15, 2023
Merged
1 change: 1 addition & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module.exports = function(grunt) {
'dist/mapml.js': ['<%= rollup.main.dest %>'],
'dist/web-map.js': ['src/web-map.js'],
'dist/mapml-viewer.js': ['src/mapml-viewer.js'],
'dist/map-caption.js': ['src/map-caption.js'],
'dist/map-area.js': ['src/map-area.js'],
'dist/layer.js': ['src/layer.js'],
'dist/leaflet.js': ['dist/leaflet-src.js',
Expand Down
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
</head>
<body>
<mapml-viewer projection="CBMTILE" zoom="2" lat="45" lon="-90" controls>
<map-caption>A pleasing map of Canada</map-caption>
<layer- label="CBMT" src="https://geogratis.gc.ca/mapml/en/cbmtile/cbmt/" checked></layer->
</mapml-viewer>
</body>
Expand Down
42 changes: 42 additions & 0 deletions src/map-caption.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
implemented for both mapml-viewer and web-map; however web-map does not focus on map element in the browser resulting in NVDA
not being able to read out map-caption and stating that it's an interactive map region
*/
export class MapCaption extends HTMLElement {
constructor() {
super();
}

// called when element is inserted into DOM (setup code)
connectedCallback() {
if (this.parentElement.nodeName === "MAPML-VIEWER" || this.parentElement.nodeName === "MAP") {

// calls MutationObserver; needed to observe changes to content between <map-caption> tags and update to aria-label
let mapcaption = this.parentElement.querySelector('map-caption').textContent;

kevinkim31 marked this conversation as resolved.
Show resolved Hide resolved
this.observer = new MutationObserver(() => {
let mapcaptionupdate = this.parentElement.querySelector('map-caption').textContent;

if (mapcaptionupdate !== mapcaption) {
this.parentElement.setAttribute('aria-label', this.parentElement.querySelector('map-caption').textContent);
}
});

this.observer.observe(this, {
characterData: true,
subtree: true,
attributes: true,
childList: true
});

// don't change aria-label if one already exists from user (checks when element is first created)
if (!this.parentElement.hasAttribute('aria-label')) {
const ariaLabel = this.textContent;
this.parentElement.setAttribute('aria-label', ariaLabel);
}
}
}
disconnectedCallback() {
this.observer.disconnect();
}
}
27 changes: 27 additions & 0 deletions src/mapml-viewer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import './leaflet.js'; // bundled with proj4, proj4leaflet, modularized
import './mapml.js';
import { MapLayer } from './layer.js';
import { MapCaption } from './map-caption.js';

export class MapViewer extends HTMLElement {
static get observedAttributes() {
Expand Down Expand Up @@ -233,6 +234,31 @@ export class MapViewer extends HTMLElement {
if(!custom){
this.dispatchEvent(new CustomEvent('createmap'));
}

/*
1. only deletes aria-label when the last (only remaining) map caption is removed
2. only deletes aria-label if the aria-label was defined by the map caption element itself
kevinkim31 marked this conversation as resolved.
Show resolved Hide resolved
*/

let mapcaption = this.querySelector('map-caption');

if (mapcaption !== null) {
setTimeout(() => {
let ariaupdate = this.getAttribute('aria-label');

if (ariaupdate === mapcaption.innerHTML) {
this.mapCaptionObserver = new MutationObserver((m) => {
let mapcaptionupdate = this.querySelector('map-caption');
if (mapcaptionupdate !== mapcaption) {
this.removeAttribute('aria-label');
}
});
this.mapCaptionObserver.observe(this, {
childList: true
});
}
}, 0);
}
}
}
disconnectedCallback() {
Expand Down Expand Up @@ -783,3 +809,4 @@ export class MapViewer extends HTMLElement {
// need to provide options { extends: ... } for custom built-in elements
window.customElements.define('mapml-viewer', MapViewer);
window.customElements.define('layer-', MapLayer);
window.customElements.define('map-caption',MapCaption);
27 changes: 27 additions & 0 deletions src/web-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import './leaflet.js'; // a lightly modified version of Leaflet for use as brow
import './mapml.js'; // refactored URI usage, replaced with URL standard
import { MapLayer } from './layer.js';
import { MapArea } from './map-area.js';
import { MapCaption } from './map-caption.js';

export class WebMap extends HTMLMapElement {
static get observedAttributes() {
Expand Down Expand Up @@ -274,6 +275,31 @@ export class WebMap extends HTMLMapElement {
if(!custom){
this.dispatchEvent(new CustomEvent('createmap'));
}

/*
1. only deletes aria-label when the last (only remaining) map caption is removed
2. only deletes aria-label if the aria-label was defined by the map caption element itself
*/

let mapcaption = this.querySelector('map-caption');

if (mapcaption !== null) {
setTimeout(() => {
let ariaupdate = this.getAttribute('aria-label');

if (ariaupdate === mapcaption.innerHTML) {
this.mapCaptionObserver = new MutationObserver((m) => {
let mapcaptionupdate = this.querySelector('map-caption');
if (mapcaptionupdate !== mapcaption) {
this.removeAttribute('aria-label');
}
});
this.mapCaptionObserver.observe(this, {
childList: true
});
}
}, 0);
}
}
}
disconnectedCallback() {
Expand Down Expand Up @@ -844,3 +870,4 @@ export class WebMap extends HTMLMapElement {
window.customElements.define('web-map', WebMap, { extends: 'map' });
window.customElements.define('layer-', MapLayer);
window.customElements.define('map-area', MapArea, {extends: 'area'});
window.customElements.define('map-caption',MapCaption);
37 changes: 37 additions & 0 deletions test/e2e/mapml-viewer/mapml-viewerCaption.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">

<script type="module" src="mapml-viewer.js"></script>
<style>
html {
height: 100%
}

body {
height: inherit
}

* {
margin: 0;
padding: 0;
}
</style>
</head>

<body>

<mapml-viewer id="nocontrols" style="height: 600px;width:500px;" projection="CBMTILE" zoom="0" lat="47" lon="-92">
<map-caption>This is a test for mapml-viewer</map-caption>
<map-caption id="test2">Test2</map-caption>
<map-caption id="test3">Test3</map-caption>

<layer- label="CBMT" src="https://geogratis.gc.ca/mapml/en/cbmtile/cbmt/" checked>
<map-caption> Layer Test</map-caption>
</layer->
</mapml-viewer>

</body>
</html>
47 changes: 47 additions & 0 deletions test/e2e/mapml-viewer/mapml-viewerCaption.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { test, expect, chromium } from '@playwright/test';

test.describe("Playwright mapml-viewer map-captions Test", () => {
let page;
let context;
test.beforeAll(async () => {
context = await chromium.launchPersistentContext('');
page = context.pages().find((page) => page.url() === 'about:blank') || await context.newPage();
page = await context.newPage();
await page.goto("mapml-viewerCaption.html");
});

test.afterAll(async function () {
await context.close();
});

test("Aria-label matches map-caption", async () => {
let arialabel = await page.evaluate(`document.querySelector('mapml-viewer').getAttribute('aria-label')`);
expect(arialabel).toEqual("This is a test for mapml-viewer");
});
test("Changing first map-caption changes aria-label", async () => {
await page.evaluateHandle(() => document.querySelector('map-caption').innerHTML="Testing 1");
let arialabel = await page.evaluate(`document.querySelector('mapml-viewer').getAttribute('aria-label')`);
expect(arialabel).toEqual("Testing 1");
});
test("Changing not-first map-caption doesn't change aria-label", async () => {
await page.evaluateHandle(() => document.getElementById('test2').innerHTML="Testing 2");
let arialabel = await page.evaluate(`document.querySelector('mapml-viewer').getAttribute('aria-label')`);
expect(arialabel).toEqual("Testing 1"); // since aria-label didn't change, should still = "Testing 1" from previous test
});
test("Removing not-first map-caption doesn't remove aria-label", async () => {
await page.evaluateHandle(() => document.getElementById('test3').remove());
let arialabel = await page.evaluate(`document.querySelector('mapml-viewer').getAttribute('aria-label')`);
expect(arialabel).toEqual("Testing 1"); // since aria-label is still there, shoudl still = "Testing 1" from previous test
});
test("Removing first map-caption removes aria-label", async () => {
await page.evaluateHandle(() => document.querySelector('map-caption').remove());
let arialabel = await page.evaluate(`document.querySelector('mapml-viewer').getAttribute('aria-label')`);
expect(arialabel).toEqual(null); // since aria-label is removed, should = null
});
test("Map Caption doesn't create aria-label on a layer", async () => {
let arialabel = await page.evaluate(`document.querySelector('layer-').getAttribute('aria-label')`);
expect(arialabel).toEqual(null);
});


});
37 changes: 37 additions & 0 deletions test/e2e/web-map/mapCaption.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">

<script type="module" src="web-map.js"></script>
<style>
html {
height: 100%
}

body {
height: inherit
}

* {
margin: 0;
padding: 0;
}
</style>
</head>

<body>

<map is="web-map" style="width: 500px;height: 500px;" projection="CBMTILE" zoom="2" lat="45.5052040" lon="-75.2202344">
<map-caption>This is a test for web-map</map-caption>
<map-caption id="test2">Test2</map-caption>
<map-caption id="test3">Test3</map-caption>

<layer- label="CBMT" src="https://geogratis.gc.ca/mapml/en/cbmtile/cbmt/" checked>
<map-caption> Layer Test</map-caption>
</layer->
</map>

</body>
</html>
46 changes: 46 additions & 0 deletions test/e2e/web-map/mapCaption.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { test, expect, chromium } from '@playwright/test';

test.describe("Playwright web-map map-captions Test", () => {
let page;
let context;
test.beforeAll(async () => {
context = await chromium.launchPersistentContext('');
page = context.pages().find((page) => page.url() === 'about:blank') || await context.newPage();
page = await context.newPage();
await page.goto("mapCaption.html");
});

test.afterAll(async function () {
await context.close();
});

test("Aria-label matches map-caption", async () => {
let arialabel = await page.evaluate(`document.querySelector('map').getAttribute('aria-label')`);
expect(arialabel).toEqual("This is a test for web-map");
});
test("Changing map-caption changes aria-label", async () => {
await page.evaluateHandle(() => document.querySelector('map-caption').innerHTML="Testing 1");
let arialabel = await page.evaluate(`document.querySelector('map').getAttribute('aria-label')`);
expect(arialabel).toEqual("Testing 1");
});
test("Changing not-first map-caption doesn't change aria-label", async () => {
await page.evaluateHandle(() => document.getElementById('test2').innerHTML="Testing 2");
let arialabel = await page.evaluate(`document.querySelector('map').getAttribute('aria-label')`);
expect(arialabel).toEqual("Testing 1"); // since aria-label didn't change, should still = "Testing 1" from previous test
});
test("Removing not-first map-caption doesn't remove aria-label", async () => {
await page.evaluateHandle(() => document.getElementById('test3').remove());
let arialabel = await page.evaluate(`document.querySelector('map').getAttribute('aria-label')`);
expect(arialabel).toEqual("Testing 1"); // since aria-label is still there, shoudl still = "Testing 1" from previous test
});
test("Removing first map-caption removes aria-label", async () => {
await page.evaluateHandle(() => document.querySelector('map-caption').remove());
let arialabel = await page.evaluate(`document.querySelector('map').getAttribute('aria-label')`);
expect(arialabel).toEqual(null); // since aria-label is removed, should = null
});
test("Map Caption doesn't create aria-label on a layer", async () => {
let arialabel = await page.evaluate(`document.querySelector('layer-').getAttribute('aria-label')`);
expect(arialabel).toEqual(null);
});

});