From f71c22972fab264452b71db1c630fc9e1a683068 Mon Sep 17 00:00:00 2001 From: AliyanH Date: Mon, 1 May 2023 12:12:33 -0400 Subject: [PATCH 01/10] remove map-feature bug --- src/map-feature.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/map-feature.js b/src/map-feature.js index 66939394d..177d69d07 100644 --- a/src/map-feature.js +++ b/src/map-feature.js @@ -160,13 +160,13 @@ export class MapFeature extends HTMLElement { } _addFeature() { - let parentEl = (this.parentNode.nodeName.toUpperCase() === "LAYER-" || - this.parentNode.nodeName.toUpperCase() === "MAP-EXTENT" ? - this.parentNode : this.parentNode.host); + this._parentEl = (this.parentNode.nodeName.toUpperCase() === "LAYER-" || + this.parentNode.nodeName.toUpperCase() === "MAP-EXTENT" ? + this.parentNode : this.parentNode.host); // arrow function is not hoisted, define before use var _attachedToMap = (e) => { - if (!parentEl._layer._map) { + if (!this._parentEl._layer._map) { // if the parent layer- el has not yet added to the map (i.e. not yet rendered), wait until it is added this._layer.once('attached', function () { this._map = this._layer._map; @@ -196,18 +196,18 @@ export class MapFeature extends HTMLElement { } }; - if (!parentEl._layer) { + if (!this._parentEl._layer) { // for custom projection cases, the MapMLLayer has not yet created and binded with the layer- at this point, // because the "createMap" event of mapml-viewer has not yet been dispatched, the map has not yet been created // the event will be dispatched after defineCustomProjection > projection setter // should wait until MapMLLayer is built - let parentLayer = parentEl.nodeName.toUpperCase() === "LAYER-" ? parentEl : (parentEl.parentElement || parentEl.parentNode.host); + let parentLayer = this._parentEl.nodeName.toUpperCase() === "LAYER-" ? this._parentEl : (this._parentEl.parentElement || this._parentEl.parentNode.host); parentLayer.parentNode.addEventListener('createmap', (e) => { this._layer = parentLayer._layer; _attachedToMap(); }); } else { - this._layer = parentEl._layer; + this._layer = this._parentEl._layer; _attachedToMap(); } } @@ -285,11 +285,11 @@ export class MapFeature extends HTMLElement { return this._layer._mapmlvectors._getNativeVariables(content); } else if (content.nodeName.toUpperCase() === "LAYER-") { // for inline features, read native zoom and cs from inline map-meta - let zoomMeta = this.parentElement.querySelectorAll('map-meta[name=zoom]'), + let zoomMeta = this._parentEl.querySelectorAll('map-meta[name=zoom]'), zoomLength = zoomMeta?.length; nativeZoom = zoomLength ? +(zoomMeta[zoomLength - 1].getAttribute('content')?.split(',').find(str => str.includes("value"))?.split('=')[1]) : 0; - let csMeta = this.parentElement.querySelectorAll("map-meta[name=cs]"), + let csMeta = this._parentEl.querySelectorAll("map-meta[name=cs]"), csLength = csMeta?.length; nativeCS = csLength ? csMeta[csLength - 1].getAttribute('content') : 'pcrs'; return {zoom: nativeZoom, cs: nativeCS}; From 39561800827845640ef0136cb525235546acbe97 Mon Sep 17 00:00:00 2001 From: AliyanH Date: Thu, 4 May 2023 00:41:52 -0400 Subject: [PATCH 02/10] fix/update Event implementation --- src/map-feature.js | 67 ++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/src/map-feature.js b/src/map-feature.js index 7e10c0dc0..8baec91c5 100644 --- a/src/map-feature.js +++ b/src/map-feature.js @@ -88,15 +88,6 @@ export class MapFeature extends HTMLElement { } break; } - case 'onfocus': - case 'onclick': - case 'onblur': - if (this._groupEl) { - // "synchronize" the onevent properties (i.e. onfocus, onclick, onblur) - // between the mapFeature and its associated element - this._groupEl[name] = this[name].bind(this._groupEl); - break; - } } } @@ -275,10 +266,6 @@ export class MapFeature extends HTMLElement { _setUpEvents() { ['click', 'focus', 'blur'].forEach((name) => { - // onevent properties & onevent attributes - if (this[`on${name}`] && typeof this[`on${name}`] === 'function') { - this._groupEl[`on${name}`] = this[`on${name}`]; - } // handle event handlers set via addEventlistener // for HTMLElement // when is clicked / focused / blurred @@ -287,15 +274,12 @@ export class MapFeature extends HTMLElement { // this === mapFeature as arrow function does not have their own "this" pointer // store onEvent handler of mapFeature if there is any to ensure that it will not be re-triggered when the cloned mouseevent is dispatched // so that only the event handlers set on HTMLFeatureElement via addEventListener method will be triggered - const handler = this[`on${name}`]; // a deep copy, var handler will not change when this.onevent is set to null (i.e. store the onevent property) - this[`on${name}`] = null; if (name === 'click') { // dispatch a cloned mouseevent to trigger the click event handlers set on HTMLFeatureElement this.dispatchEvent(new PointerEvent(name, { ...e })); } else { this.dispatchEvent(new FocusEvent(name, { ...e })); } - this[`on${name}`] = handler; }); }); } @@ -324,9 +308,7 @@ export class MapFeature extends HTMLElement { return this._layer._mapmlvectors._getNativeVariables(content); } else if (content.nodeName.toUpperCase() === 'LAYER-') { // for inline features, read native zoom and cs from inline map-meta - let zoomMeta = this._parentEl.querySelectorAll( - 'map-meta[name=zoom]' - ), + let zoomMeta = this._parentEl.querySelectorAll('map-meta[name=zoom]'), zoomLength = zoomMeta?.length; nativeZoom = zoomLength ? +zoomMeta[zoomLength - 1] @@ -578,34 +560,31 @@ export class MapFeature extends HTMLElement { button: 0 }); } - if (typeof this.onclick === 'function') { - this.onclick.call(this._groupEl, event); - return; - } else { - let properties = this.querySelector('map-properties'); - if (g.getAttribute('role') === 'link') { - for (let path of g.children) { - path.mousedown.call(this._featureGroup, event); - path.mouseup.call(this._featureGroup, event); - } + let properties = this.querySelector('map-properties'); + if (g.getAttribute('role') === 'link') { + for (let path of g.children) { + path.mousedown.call(this._featureGroup, event); + path.mouseup.call(this._featureGroup, event); } - // for custom projection, layer- element may disconnect and re-attach to the map after the click - // so check whether map-feature element is still connected before any further operations - if (properties && this.isConnected) { - let featureGroup = this._featureGroup, - shapes = featureGroup._layers; - // close popup if the popup is currently open - for (let id in shapes) { - if (shapes[id].isPopupOpen()) { - shapes[id].closePopup(); - } - } - if (featureGroup.isPopupOpen()) { - featureGroup.closePopup(); - } else { - featureGroup.openPopup(); + } + // dispatch click event for map-feature to allow events entered by 'addEventListener' + this.dispatchEvent(new PointerEvent('click')); + // for custom projection, layer- element may disconnect and re-attach to the map after the click + // so check whether map-feature element is still connected before any further operations + if (properties && this.isConnected) { + let featureGroup = this._featureGroup, + shapes = featureGroup._layers; + // close popup if the popup is currently open + for (let id in shapes) { + if (shapes[id].isPopupOpen()) { + shapes[id].closePopup(); } } + if (featureGroup.isPopupOpen()) { + featureGroup.closePopup(); + } else { + featureGroup.openPopup(); + } } } From 65bfe1ed4783d2a1ae9b85218691af4ac4ea4bb4 Mon Sep 17 00:00:00 2001 From: AliyanH Date: Thu, 4 May 2023 15:12:35 -0400 Subject: [PATCH 03/10] stopPropagation attempt --- index.html | 22 +++++++++++++++++++++- src/map-feature.js | 4 +++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index ddfa9d00d..7418de006 100644 --- a/index.html +++ b/index.html @@ -72,12 +72,32 @@ + A pleasing map of Canada - + diff --git a/src/map-feature.js b/src/map-feature.js index 8baec91c5..485579095 100644 --- a/src/map-feature.js +++ b/src/map-feature.js @@ -276,7 +276,9 @@ export class MapFeature extends HTMLElement { // so that only the event handlers set on HTMLFeatureElement via addEventListener method will be triggered if (name === 'click') { // dispatch a cloned mouseevent to trigger the click event handlers set on HTMLFeatureElement - this.dispatchEvent(new PointerEvent(name, { ...e })); + let clickEv = new PointerEvent(name, { cancelable: true }); + clickEv.originalEvent = e; + this.dispatchEvent(clickEv); } else { this.dispatchEvent(new FocusEvent(name, { ...e })); } From 97cd6cea27ef8b424258e17a8f3bce06fe4932e0 Mon Sep 17 00:00:00 2001 From: AliyanH Date: Fri, 5 May 2023 13:16:07 -0400 Subject: [PATCH 04/10] Finalize map-feature Event handling --- src/map-feature.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/map-feature.js b/src/map-feature.js index 485579095..5a39f352e 100644 --- a/src/map-feature.js +++ b/src/map-feature.js @@ -266,21 +266,19 @@ export class MapFeature extends HTMLElement { _setUpEvents() { ['click', 'focus', 'blur'].forEach((name) => { - // handle event handlers set via addEventlistener - // for HTMLElement // when is clicked / focused / blurred // should dispatch the click / focus / blur event listener on **linked HTMLFeatureElements** this._groupEl.addEventListener(name, (e) => { - // this === mapFeature as arrow function does not have their own "this" pointer - // store onEvent handler of mapFeature if there is any to ensure that it will not be re-triggered when the cloned mouseevent is dispatched - // so that only the event handlers set on HTMLFeatureElement via addEventListener method will be triggered if (name === 'click') { // dispatch a cloned mouseevent to trigger the click event handlers set on HTMLFeatureElement let clickEv = new PointerEvent(name, { cancelable: true }); clickEv.originalEvent = e; this.dispatchEvent(clickEv); } else { - this.dispatchEvent(new FocusEvent(name, { ...e })); + // dispatch a cloned focusevent to trigger the focus/blue event handlers set on HTMLFeatureElement + let focusEv = new FocusEvent(name, { cancelable: true }); + focusEv.originalEvent = e; + this.dispatchEvent(focusEv); } }); }); @@ -570,7 +568,9 @@ export class MapFeature extends HTMLElement { } } // dispatch click event for map-feature to allow events entered by 'addEventListener' - this.dispatchEvent(new PointerEvent('click')); + let clickEv = new PointerEvent('click', { cancelable: true }); + clickEv.originalEvent = event; + this.dispatchEvent(clickEv); // for custom projection, layer- element may disconnect and re-attach to the map after the click // so check whether map-feature element is still connected before any further operations if (properties && this.isConnected) { @@ -584,7 +584,8 @@ export class MapFeature extends HTMLElement { } if (featureGroup.isPopupOpen()) { featureGroup.closePopup(); - } else { + } else if (!clickEv.originalEvent.cancelBubble) { + // If stopPropagation is not set on originalEvent by user featureGroup.openPopup(); } } From 468e81dda7a2d71cac73193aad614aa007830b53 Mon Sep 17 00:00:00 2001 From: AliyanH Date: Fri, 5 May 2023 13:22:15 -0400 Subject: [PATCH 05/10] revert index.html --- index.html | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/index.html b/index.html index 7418de006..ddfa9d00d 100644 --- a/index.html +++ b/index.html @@ -72,32 +72,12 @@ - A pleasing map of Canada - +
From a1e7cc3a2130e6a8d43eebab7d4b690d1bdd07b5 Mon Sep 17 00:00:00 2001 From: AliyanH Date: Mon, 8 May 2023 16:28:09 -0400 Subject: [PATCH 06/10] Add test for stopPropagation --- test/e2e/core/mapFeature.test.js | 37 +++++++++++++++++ test/e2e/core/mapFeature1.html | 71 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 test/e2e/core/mapFeature1.html diff --git a/test/e2e/core/mapFeature.test.js b/test/e2e/core/mapFeature.test.js index 8f320e30f..74c307087 100644 --- a/test/e2e/core/mapFeature.test.js +++ b/test/e2e/core/mapFeature.test.js @@ -292,3 +292,40 @@ test.describe('Playwright MapFeature Custom Element Tests', () => { expect(test).toEqual(true); }); }); + +test.describe('MapFeature Events', () => { + let page, context; + test.beforeAll(async () => { + context = await chromium.launchPersistentContext(''); + page = + context.pages().find((page) => page.url() === 'about:blank') || + (await context.newPage()); + await page.goto('mapFeature1.html'); + }); + test.afterAll(async function () { + await context.close(); + }); + + test('Custom Click event - stopPropagation', async () => { + // Click on polygon + await page + .locator( + 'mapml-viewer[role="application"]:has-text("Polygon -75.5859375 45.4656690 -75.6813812 45.4533876 -75.6961441 45.4239978 -75")' + ) + .click(); + const popupCount = await page.$eval( + 'body > mapml-viewer > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', + (popupPane) => popupPane.childElementCount + ); + // expect no popup is binded + expect(popupCount).toEqual(0); + + // custom click property displaying on div + const propertyDiv = await page.$eval( + 'body > div#property', + (div) => div.firstElementChild.innerText + ); + // check custom event is displaying properties + expect(propertyDiv).toEqual('This is a Polygon'); + }); +}); diff --git a/test/e2e/core/mapFeature1.html b/test/e2e/core/mapFeature1.html new file mode 100644 index 000000000..24f1a31c1 --- /dev/null +++ b/test/e2e/core/mapFeature1.html @@ -0,0 +1,71 @@ + + + + + map-feature Event tests + + + + + + + + + + Polygon + + + -75.5859375 45.4656690 -75.6813812 45.4533876 -75.6961441 45.4239978 + -75.7249832 45.4083331 -75.7792282 45.3772317 -75.7534790 45.3294614 -75.5831909 45.3815724 + -75.6024170 45.4273712 -75.5673981 45.4639834 -75.5859375 45.4656690 + + + +

This is a Polygon

+
+
+ + + Line + + + -75.6168365 45.471929 -75.6855011 45.458445 -75.7016373 45.4391764 -75.7030106 + 45.4259255 -75.7236099 45.4208652 -75.7565689 45.4117074 -75.7833481 45.384225 -75.8197403 + 45.3714435 -75.8516693 45.377714 + + + +

This is a Line

+
+
+ + + Point + + + -75.6916809 45.4186964 + + + +

This is a Point

+
+
+
+
+ +
+ + + + + \ No newline at end of file From 9ff535a4e9cd9f47d6ee9c0a438e69f81d372e2b Mon Sep 17 00:00:00 2001 From: AliyanH Date: Mon, 15 May 2023 12:34:08 -0400 Subject: [PATCH 07/10] update observed attributes + add test for click method --- src/map-feature.js | 2 +- test/e2e/core/mapFeature.test.js | 23 +++++++++++++++++++++++ test/e2e/core/mapFeature1.html | 4 ++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/map-feature.js b/src/map-feature.js index 5a39f352e..b311dce84 100644 --- a/src/map-feature.js +++ b/src/map-feature.js @@ -1,6 +1,6 @@ export class MapFeature extends HTMLElement { static get observedAttributes() { - return ['zoom', 'onfocus', 'onclick', 'onblur']; + return ['zoom', 'min', 'max']; } get zoom() { diff --git a/test/e2e/core/mapFeature.test.js b/test/e2e/core/mapFeature.test.js index 74c307087..3e3c918e0 100644 --- a/test/e2e/core/mapFeature.test.js +++ b/test/e2e/core/mapFeature.test.js @@ -328,4 +328,27 @@ test.describe('MapFeature Events', () => { // check custom event is displaying properties expect(propertyDiv).toEqual('This is a Polygon'); }); + + test('click() method - stopPropagation', async () => { + // click() method on line feature + await page.$eval( + 'body > mapml-viewer > layer- > map-feature#line', + (line) => line.click() + ); + + const popupCount = await page.$eval( + 'body > mapml-viewer > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', + (popupPane) => popupPane.childElementCount + ); + // expect no popup is binded + expect(popupCount).toEqual(0); + + // custom click property displaying on div + const propertyDiv = await page.$eval( + 'body > div#property', + (div) => div.firstElementChild.innerText + ); + // check custom event is displaying properties + expect(propertyDiv).toEqual('This is a Line'); + }); }); diff --git a/test/e2e/core/mapFeature1.html b/test/e2e/core/mapFeature1.html index 24f1a31c1..e52b18894 100644 --- a/test/e2e/core/mapFeature1.html +++ b/test/e2e/core/mapFeature1.html @@ -25,7 +25,7 @@

This is a Polygon

- + Line @@ -68,4 +68,4 @@

This is a Point

- \ No newline at end of file + From c804bbeeaf8f1f739251f0010902afe50e5634b5 Mon Sep 17 00:00:00 2001 From: AliyanH Date: Tue, 16 May 2023 13:19:33 -0400 Subject: [PATCH 08/10] remove non-standard event parameter for click,focus,blur methods --- src/map-feature.js | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/map-feature.js b/src/map-feature.js index b311dce84..66c14fbf1 100644 --- a/src/map-feature.js +++ b/src/map-feature.js @@ -549,17 +549,14 @@ export class MapFeature extends HTMLElement { } // a method that simulates a click, or invoking the user-defined click event - // event (optional): a MouseEvent object, can be passed as an argument of the user-defined click event handlers - click(event) { + click() { let g = this._groupEl, rect = g.getBoundingClientRect(); - if (!event) { - event = new MouseEvent('click', { - clientX: rect.x + rect.width / 2, - clientY: rect.y + rect.height / 2, - button: 0 - }); - } + let event = new MouseEvent('click', { + clientX: rect.x + rect.width / 2, + clientY: rect.y + rect.height / 2, + button: 0 + }); let properties = this.querySelector('map-properties'); if (g.getAttribute('role') === 'link') { for (let path of g.children) { @@ -592,25 +589,15 @@ export class MapFeature extends HTMLElement { } // a method that sets the current focus to the element, or invoking the user-defined focus event - // event (optional): a FocusEvent object, can be passed as an argument of the user-defined focus event handlers // options (optional): as options parameter for native HTMLelemnt // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus - focus(event, options) { - let g = this._groupEl; - if (typeof this.onfocus === 'function') { - this.onfocus.call(this._groupEl, event); - return; - } else { - g.focus(options); - } + focus(options) { + this._groupEl.focus(options); } // a method that makes the element lose focus, or invoking the user-defined blur event - // event (optional): a FocusEvent object, can be passed as an argument of the user-defined blur event handlers - blur(event) { - if (typeof this.onblur === 'function') { - this.onblur.call(this._groupEl, event); - } else if ( + blur() { + if ( document.activeElement.shadowRoot?.activeElement === this._groupEl || document.activeElement.shadowRoot?.activeElement.parentNode === this._groupEl From c8ac48ebcb26fc871cbbb35b8966de442ee4f85b Mon Sep 17 00:00:00 2001 From: AliyanH Date: Tue, 16 May 2023 22:56:09 -0400 Subject: [PATCH 09/10] add support/link for keyup+keydown event --- src/map-feature.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/map-feature.js b/src/map-feature.js index 66c14fbf1..075a36633 100644 --- a/src/map-feature.js +++ b/src/map-feature.js @@ -265,7 +265,7 @@ export class MapFeature extends HTMLElement { } _setUpEvents() { - ['click', 'focus', 'blur'].forEach((name) => { + ['click', 'focus', 'blur', 'keyup', 'keydown'].forEach((name) => { // when is clicked / focused / blurred // should dispatch the click / focus / blur event listener on **linked HTMLFeatureElements** this._groupEl.addEventListener(name, (e) => { @@ -274,6 +274,10 @@ export class MapFeature extends HTMLElement { let clickEv = new PointerEvent(name, { cancelable: true }); clickEv.originalEvent = e; this.dispatchEvent(clickEv); + } else if (name === 'keyup' || name === 'keydown') { + let keyEv = new KeyboardEvent(name, { cancelable: true }); + keyEv.originalEvent = e; + this.dispatchEvent(keyEv); } else { // dispatch a cloned focusevent to trigger the focus/blue event handlers set on HTMLFeatureElement let focusEv = new FocusEvent(name, { cancelable: true }); @@ -589,7 +593,7 @@ export class MapFeature extends HTMLElement { } // a method that sets the current focus to the element, or invoking the user-defined focus event - // options (optional): as options parameter for native HTMLelemnt + // options (optional): as options parameter for native HTMLElement // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus focus(options) { this._groupEl.focus(options); From 8ae334bb31093c8b8787c88f6d632bd3ae790fd4 Mon Sep 17 00:00:00 2001 From: AliyanH Date: Fri, 19 May 2023 14:03:41 -0400 Subject: [PATCH 10/10] close popup on zoom to here link, closes #855 --- src/mapml/layers/MapMLLayer.js | 1 + test/e2e/layers/queryLink.test.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/mapml/layers/MapMLLayer.js b/src/mapml/layers/MapMLLayer.js index 91f06539a..86ff323c1 100644 --- a/src/mapml/layers/MapMLLayer.js +++ b/src/mapml/layers/MapMLLayer.js @@ -2042,6 +2042,7 @@ export var MapMLLayer = L.Layer.extend({ if (!(e instanceof MouseEvent) && e.keyCode !== 13) return; e.preventDefault(); featureEl.zoomTo(); + featureEl._map.closePopup(); }; content.insertBefore( zoomLink, diff --git a/test/e2e/layers/queryLink.test.js b/test/e2e/layers/queryLink.test.js index 51b16fa3d..ba74d85d0 100644 --- a/test/e2e/layers/queryLink.test.js +++ b/test/e2e/layers/queryLink.test.js @@ -304,6 +304,12 @@ test.describe('Playwright Query Link Tests', () => { await page.keyboard.press('Enter'); await page.waitForTimeout(200); + // zoom to here link closes popup + const popupCount = await page.evaluate( + `document.querySelector("mapml-viewer").shadowRoot.querySelector(".leaflet-popup-pane").childElementCount` + ); + expect(popupCount).toBe(0); + const endTopLeft = await page.evaluate( `document.querySelector('mapml-viewer').extent.topLeft.pcrs` );