diff --git a/src/layer.js b/src/layer.js
index 2ce4c1f50..defa1932b 100644
--- a/src/layer.js
+++ b/src/layer.js
@@ -6,7 +6,7 @@ import { createLayerControlHTML } from './mapml/elementSupport/layers/createLaye
export class BaseLayerElement extends HTMLElement {
static get observedAttributes() {
- return ['src', 'label', 'checked', 'hidden', 'opacity'];
+ return ['src', 'label', 'checked', 'hidden', 'opacity', 'media'];
}
/* jshint ignore:start */
#hasConnected;
@@ -53,6 +53,13 @@ export class BaseLayerElement extends HTMLElement {
}
}
+ get media() {
+ return this.getAttribute('media');
+ }
+ set media(val) {
+ this.setAttribute('media', val);
+ }
+
get opacity() {
// use ?? since 0 is falsy, || would return rhs in that case
return +(this._opacity ?? this.getAttribute('opacity'));
@@ -114,17 +121,59 @@ export class BaseLayerElement extends HTMLElement {
this._onAdd();
}
}
+ break;
+ case 'media':
+ if (oldValue !== newValue) {
+ this._registerMediaQuery(newValue);
+ }
+ break;
}
}
}
+ _registerMediaQuery(mq) {
+ if (!this._changeHandler) {
+ this._changeHandler = () => {
+ this._onRemove();
+ if (this._mql.matches) {
+ this._onAdd();
+ }
+ // set the disabled 'read-only' attribute indirectly, via _validateDisabled
+ this._validateDisabled();
+ };
+ }
+
+ if (mq) {
+ // a new media query is being established
+ let map = this.getMapEl();
+ if (!map) return;
+ // Remove listener from the old media query (if it exists)
+ if (this._mql) {
+ this._mql.removeEventListener('change', this._changeHandler);
+ }
+
+ this._mql = map.matchMedia(mq);
+ this._changeHandler();
+ this._mql.addEventListener('change', this._changeHandler);
+ } else if (this._mql) {
+ // the media attribute removed or query set to ''
+ this._mql.removeEventListener('change', this._changeHandler);
+ delete this._mql;
+ // effectively, no / empty media attribute matches, do what changeHandler does
+ this._onRemove();
+ this._onAdd();
+ this._validateDisabled();
+ }
+ }
+ getMapEl() {
+ return Util.getClosest(this, 'mapml-viewer,map[is=web-map]');
+ }
constructor() {
// Always call super first in constructor
super();
// this._opacity is used to record the current opacity value (with or without updates),
// the initial value of this._opacity should be set as opacity attribute value, if exists, or the default value 1.0
this._opacity = this.opacity || 1.0;
- this._renderingMapContent = M.options.contentPreference;
this.attachShadow({ mode: 'open' });
}
disconnectedCallback() {
@@ -132,6 +181,13 @@ export class BaseLayerElement extends HTMLElement {
// removed from the map and the layer control
if (this.hasAttribute('data-moving')) return;
this._onRemove();
+
+ if (this._mql) {
+ if (this._changeHandler) {
+ this._mql.removeEventListener('change', this._changeHandler);
+ }
+ delete this._mql;
+ }
}
_onRemove() {
@@ -141,13 +197,6 @@ export class BaseLayerElement extends HTMLElement {
let l = this._layer,
lc = this._layerControl,
lchtml = this._layerControlHTML;
- // remove properties of layer involved in whenReady() logic
- delete this._layer;
- delete this._layerControl;
- delete this._layerControlHTML;
- delete this._fetchError;
- this.shadowRoot.innerHTML = '';
- if (this.src) this.innerHTML = '';
if (l) {
l.off();
@@ -158,8 +207,16 @@ export class BaseLayerElement extends HTMLElement {
}
if (lc && !this.hidden) {
+ // lc.removeLayer depends on this._layerControlHTML, can't delete it until after
lc.removeLayer(l);
}
+ // remove properties of layer involved in whenReady() logic
+ delete this._layer;
+ delete this._layerControl;
+ delete this._layerControlHTML;
+ delete this._fetchError;
+ this.shadowRoot.innerHTML = '';
+ if (this.src) this.innerHTML = '';
}
connectedCallback() {
@@ -170,11 +227,17 @@ export class BaseLayerElement extends HTMLElement {
this._createLayerControlHTML = createLayerControlHTML.bind(this);
const doConnected = this._onAdd.bind(this);
const doRemove = this._onRemove.bind(this);
+ const registerMediaQuery = this._registerMediaQuery.bind(this);
+ let mq = this.media;
this.parentElement
.whenReady()
.then(() => {
doRemove();
- doConnected();
+ if (mq) {
+ registerMediaQuery(mq);
+ } else {
+ doConnected();
+ }
})
.catch((error) => {
throw new Error('Map never became ready: ' + error);
@@ -189,20 +252,11 @@ export class BaseLayerElement extends HTMLElement {
e.stopPropagation();
// if user changes the style in layer control
if (e.detail) {
- this._renderingMapContent = e.detail._renderingMapContent;
this.src = e.detail.src;
}
},
{ once: true }
);
- this.addEventListener(
- 'zoomchangesrc',
- function (e) {
- e.stopPropagation();
- this.src = e.detail.href;
- },
- { once: true }
- );
let base = this.baseURI ? this.baseURI : document.baseURI;
const headers = new Headers();
headers.append('Accept', 'text/mapml');
@@ -240,7 +294,6 @@ export class BaseLayerElement extends HTMLElement {
.then(() => {
// may throw:
this.selectAlternateOrChangeProjection();
- this.checkForPreferredContent();
})
.then(() => {
this._layer = mapMLLayer(new URL(this.src, base).href, this, {
@@ -278,7 +331,6 @@ export class BaseLayerElement extends HTMLElement {
.then(() => {
// may throw:
this.selectAlternateOrChangeProjection();
- this.checkForPreferredContent();
})
.then(() => {
this._layer = mapMLLayer(null, this, {
@@ -317,13 +369,6 @@ export class BaseLayerElement extends HTMLElement {
);
this.parentElement.projection = e.cause.mapprojection;
}
- } else if (e.message === 'findmatchingpreferredcontent') {
- if (e.cause.href) {
- console.log(
- 'Changing layer to matching preferred content at: ' + e.cause.href
- );
- this.src = e.cause.href;
- }
} else if (e.message === 'Failed to fetch') {
// cut short whenReady with the _fetchError property
this._fetchError = true;
@@ -372,23 +417,6 @@ export class BaseLayerElement extends HTMLElement {
}
}
- checkForPreferredContent() {
- let mapml = this.src ? this.shadowRoot : this;
- let availablePreferMapContents = mapml.querySelector(
- `map-link[rel="style"][media="prefers-map-content=${this._renderingMapContent}"][href]`
- );
- if (availablePreferMapContents) {
- // resolve href
- let url = new URL(
- availablePreferMapContents.getAttribute('href'),
- availablePreferMapContents.getBase()
- ).href;
- throw new Error('findmatchingpreferredcontent', {
- cause: { href: url }
- });
- }
- }
-
copyRemoteContentToShadowRoot(mapml) {
let shadowRoot = this.shadowRoot;
// get the map-meta[name=projection/cs/extent/zoom] from map-head of remote mapml, attach them to the shadowroot
@@ -610,8 +638,13 @@ export class BaseLayerElement extends HTMLElement {
setTimeout(() => {
let layer = this._layer,
map = layer?._map;
+ // if there's a media query in play, check it early
+ if (this._mql && !this._mql.matches) {
+ this.setAttribute('disabled', '');
+ this.disabled = true;
+ return;
+ }
if (map) {
- this._validateLayerZoom({ zoom: map.getZoom() });
// prerequisite: no inline and remote mapml elements exists at the same time
const mapExtents = this.src
? this.shadowRoot.querySelectorAll('map-extent')
@@ -664,35 +697,6 @@ export class BaseLayerElement extends HTMLElement {
}
}, 0);
}
- _validateLayerZoom(e) {
- // get the min and max zooms from all extents
- let toZoom = e.zoom;
- let min = this.extent.zoom.minZoom;
- let max = this.extent.zoom.maxZoom;
- let inLink = this.src
- ? this.shadowRoot.querySelector('map-link[rel=zoomin]')
- : this.querySelector('map-link[rel=zoomin]'),
- outLink = this.src
- ? this.shadowRoot.querySelector('map-link[rel=zoomout]')
- : this.querySelector('map-link[rel=zoomout]');
- let targetURL;
- if (!(min <= toZoom && toZoom <= max)) {
- if (inLink && toZoom > max) {
- targetURL = inLink.href;
- } else if (outLink && toZoom < min) {
- targetURL = outLink.href;
- }
- if (targetURL) {
- this.dispatchEvent(
- new CustomEvent('zoomchangesrc', {
- detail: {
- href: targetURL
- }
- })
- );
- }
- }
- }
// disable/italicize layer control elements based on the map-layer.disabled property
toggleLayerControlDisabled() {
let input = this._layerControlCheckbox,
diff --git a/src/map-extent.js b/src/map-extent.js
index b94b34271..e34267bef 100644
--- a/src/map-extent.js
+++ b/src/map-extent.js
@@ -431,7 +431,7 @@ export class HTMLExtentElement extends HTMLElement {
_handleChange() {
// add _extentLayer to map if map-extent is checked, otherwise remove it
- if (this.checked && !this.disabled) {
+ if (this.checked && !this.disabled && this.parentLayer._layer) {
// can be added to mapmllayer layerGroup no matter map-layer is checked or not
this._extentLayer.addTo(this.parentLayer._layer);
this._extentLayer.setZIndex(
diff --git a/src/map-link.js b/src/map-link.js
index bcc1fa7d6..f2811b4f5 100644
--- a/src/map-link.js
+++ b/src/map-link.js
@@ -277,6 +277,9 @@ export class HTMLLinkElement extends HTMLElement {
case 'image':
case 'features':
case 'query':
+ // because we skip the attributeChangedCallback for initialization,
+ // respect the disabled attribute which can be set by the author prior
+ // to initialization
if (!this.disabled) {
this._initTemplateVars();
await this._createTemplatedLink();
@@ -296,7 +299,9 @@ export class HTMLLinkElement extends HTMLElement {
//this._createLegendLink();
break;
case 'stylesheet':
- this._createStylesheetLink();
+ if (!this.disabled) {
+ this._createStylesheetLink();
+ }
break;
case 'alternate':
this._createAlternateLink(); // add media attribute
@@ -305,7 +310,10 @@ export class HTMLLinkElement extends HTMLElement {
// this._createLicenseLink();
break;
}
- this._registerMediaQuery(this.media);
+ // the media attribute uses / overrides the disabled attribute to enable or
+ // disable the link, so at this point the #hasConnected must be true so
+ // that the disabled attributeChangedCallback can have its desired side effect
+ await this._registerMediaQuery(this.media);
// create the type of templated leaflet layer appropriate to the rel value
// image/map/features = templated(Image/Feature), tile=templatedTile,
// this._tempatedTileLayer = Util.templatedTile(pane: this.extentElement._leafletLayer._container)
@@ -361,18 +369,16 @@ export class HTMLLinkElement extends HTMLElement {
case 'image':
case 'features':
case 'query':
- if (!this.disabled) {
- this._initTemplateVars();
- await this._createTemplatedLink();
- this.getLayerEl()._validateDisabled();
- }
+ this._initTemplateVars();
+ await this._createTemplatedLink();
+ this.getLayerEl()._validateDisabled();
break;
case 'stylesheet':
this._createStylesheetLink();
break;
}
}
- _registerMediaQuery(mq) {
+ async _registerMediaQuery(mq) {
if (!this._changeHandler) {
// Define and bind the change handler once
this._changeHandler = () => {
@@ -383,6 +389,9 @@ export class HTMLLinkElement extends HTMLElement {
if (mq) {
let map = this.getMapEl();
if (!map) return;
+ // have to wait until map has an extent i.e. is ready, because the
+ // matchMedia function below relies on it for map related queries
+ await map.whenReady();
// Remove listener from the old media query (if it exists)
if (this._mql) {
@@ -397,6 +406,8 @@ export class HTMLLinkElement extends HTMLElement {
// Clean up the existing listener
this._mql.removeEventListener('change', this._changeHandler);
delete this._mql;
+ // unlike map-layer.disabled, map-link.disabled is an observed attribute
+ this.disabled = false;
}
}
_createAlternateLink(mapml) {
@@ -451,7 +462,7 @@ export class HTMLLinkElement extends HTMLElement {
function copyAttributes(source, target) {
return Array.from(source.attributes).forEach((attribute) => {
- if (attribute.nodeName !== 'href')
+ if (attribute.nodeName !== 'href' && attribute.nodeName !== 'media')
target.setAttribute(attribute.nodeName, attribute.nodeValue);
});
}
@@ -989,8 +1000,7 @@ export class HTMLLinkElement extends HTMLElement {
layerEl.dispatchEvent(
new CustomEvent('changestyle', {
detail: {
- src: e.target.getAttribute('data-href'),
- preference: this.media['prefers-map-content']
+ src: e.target.getAttribute('data-href')
}
})
);
diff --git a/test/e2e/data/restaurants/restaurants.mapml b/test/e2e/data/restaurants/restaurants.mapml
index 50616d7d8..07e9ab57f 100644
--- a/test/e2e/data/restaurants/restaurants.mapml
+++ b/test/e2e/data/restaurants/restaurants.mapml
@@ -10,7 +10,7 @@
-
+
Hung Sum Restaurant
@@ -50,7 +50,7 @@
-
+
Big Daddy's
@@ -90,7 +90,7 @@
-
+
Hareg Cafe & Variety
@@ -130,7 +130,7 @@
-
+
Phucket Royal
@@ -170,7 +170,7 @@
-
+
Sushi 88
@@ -242,7 +242,7 @@
-
+
Banditos
@@ -286,7 +286,7 @@
-
+
India Palace
@@ -338,7 +338,7 @@
-
+
The Prescott
diff --git a/test/e2e/elements/map-layer/map-layer-media.html b/test/e2e/elements/map-layer/map-layer-media.html
new file mode 100644
index 000000000..61d4d5989
--- /dev/null
+++ b/test/e2e/elements/map-layer/map-layer-media.html
@@ -0,0 +1,44 @@
+
+
+
+ <map-layer> media attribute test
+
+
+
+
+
+
+
+
+ media query: (0 <= map-zoom <=3)
+
+
+
+ -87 68
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/e2e/elements/map-layer/map-layer-media.test.js b/test/e2e/elements/map-layer/map-layer-media.test.js
new file mode 100644
index 000000000..29b785db5
--- /dev/null
+++ b/test/e2e/elements/map-layer/map-layer-media.test.js
@@ -0,0 +1,90 @@
+import { test, expect, chromium } from '@playwright/test';
+
+test.describe('map-layer media attribute', () => {
+ let page;
+ let context;
+ let viewer;
+ test.beforeAll(async function () {
+ context = await chromium.launchPersistentContext('');
+ page =
+ context.pages().find((page) => page.url() === 'about:blank') ||
+ (await context.newPage());
+ await page.goto('map-layer-media.html');
+ await page.waitForTimeout(1000);
+ viewer = page.getByTestId('viewer');
+ });
+ test('On initial load, a matching media queried layer is enabled', async () => {
+ const matchedQueryLayer = page.getByTestId('initial-mq');
+ // map loads at z=2, query matches 0 <= z <= 3
+ await expect(matchedQueryLayer).not.toHaveAttribute('disabled', '');
+ });
+ test(`A visible (enabled) map-layer with no media query should remain enabled \
+when a matching mq is added`, async () => {
+ const noInitialQueryLayer = page.getByTestId('no-initial-mq');
+ await expect(noInitialQueryLayer).not.toHaveAttribute('disabled', '');
+ await viewer.evaluate((v) => v.zoomTo(v.lat, v.lon, 4));
+ await page.waitForTimeout(200);
+ // should still be enabled:
+ await expect(noInitialQueryLayer).not.toHaveAttribute('disabled', '');
+ });
+ test(`A visible (enabled) map-layer with no media query should be disabled \
+when a non-matching media query attribute is set`, async () => {
+ await expect(viewer).toHaveAttribute('zoom', '4');
+ const presentInLayerControl = await viewer.evaluate((v) => {
+ let lc = v._layerControl;
+ let layers = lc._layers.map((e) => e.layer._layerEl);
+ let noInitialQueryLayer = v.querySelector('[data-testid=no-initial-mq]');
+ return layers.some((e) => e === noInitialQueryLayer);
+ });
+ expect(presentInLayerControl).toBe(true);
+ const noInitialQueryLayer = page.getByTestId('no-initial-mq');
+ await expect(noInitialQueryLayer).not.toHaveAttribute('disabled', '');
+ await noInitialQueryLayer.evaluate(
+ (l) => (l.media = '(0 <= map-zoom <=3)')
+ );
+ await expect(noInitialQueryLayer).toHaveAttribute('disabled', '');
+ });
+ test(`A mq-disabled layer is removed from the layer control`, async () => {
+ const noInitialQueryLayer = page.getByTestId('no-initial-mq');
+ await expect(noInitialQueryLayer).toHaveAttribute(
+ 'media',
+ '(0 <= map-zoom <=3)'
+ );
+ await expect(noInitialQueryLayer).toHaveAttribute('disabled', '');
+ const presentInLayerControl = await viewer.evaluate((v) => {
+ let lc = v._layerControl;
+ let layers = lc._layers.map((e) => e.layer._layerEl);
+ let noInitialQueryLayer = v.querySelector('[data-testid=no-initial-mq]');
+ return layers.some((e) => e === noInitialQueryLayer);
+ });
+ expect(presentInLayerControl).toBe(false);
+ });
+ test(`A layer disabled due to mq would otherwise be enabled is \
+enabled and added to the layer control when mq removed`, async () => {
+ const noInitialQueryLayer = page.getByTestId('no-initial-mq');
+ await expect(noInitialQueryLayer).toHaveAttribute(
+ 'media',
+ '(0 <= map-zoom <=3)'
+ );
+ await expect(noInitialQueryLayer).toHaveAttribute('disabled', '');
+ await noInitialQueryLayer.evaluate((l) => l.removeAttribute('media'));
+ await expect(noInitialQueryLayer).not.toHaveAttribute('disabled', '');
+ const presentInLayerControl = await viewer.evaluate((v) => {
+ let lc = v._layerControl;
+ let layers = lc._layers.map((e) => e.layer._layerEl);
+ let noInitialQueryLayer = v.querySelector('[data-testid=no-initial-mq]');
+ return layers.some((e) => e === noInitialQueryLayer);
+ });
+ expect(presentInLayerControl).toBe(true);
+ });
+ test(`An empty media query is the same as no media query`, async () => {
+ const noInitialQueryLayer = page.getByTestId('no-initial-mq');
+ await noInitialQueryLayer.evaluate((l) => l.setAttribute('media', ' '));
+ await expect(noInitialQueryLayer).not.toHaveAttribute('disabled', '');
+ });
+ test(`An invalid media query is the same as a non-matching media query`, async () => {
+ const noInitialQueryLayer = page.getByTestId('no-initial-mq');
+ await noInitialQueryLayer.evaluate((l) => l.setAttribute('media', '(foo '));
+ await expect(noInitialQueryLayer).toHaveAttribute('disabled', '');
+ });
+});
diff --git a/test/e2e/elements/map-link/map-link-disabled.test.js b/test/e2e/elements/map-link/map-link-disabled.test.js
index a577fdbd7..5799c1c6d 100644
--- a/test/e2e/elements/map-link/map-link-disabled.test.js
+++ b/test/e2e/elements/map-link/map-link-disabled.test.js
@@ -15,10 +15,6 @@ test.describe('map-link disabled', () => {
});
test('rel=stylesheet disabled attribute', async () => {
const viewer = page.getByTestId('viewer');
- // there's a problem when attempting to select this link by testid. The map-link
- // code copies all the attributes of the map-link element onto the generated
- // element it uses to render the content, including the data-testid,
- // resulting in duplicate ids that mess up the getByTestId algorithm.
const featuresLink = page.getByTestId('restaurants_templated_link');
// test that a templated content link can be disabled by the HTML author at
// page load, and the bounds of the extent do not include the disabled link
@@ -29,6 +25,11 @@ test.describe('map-link disabled', () => {
maxDiffPixels: 20
});
await featuresLink.evaluate((fl) => (fl.disabled = false));
+ // there's a problem when attempting to select this link by testid. The map-link
+ // code copies all the attributes of the map-link element onto the generated
+ // element it uses to render the content, including the data-testid,
+ // resulting in duplicate ids that mess up the getByTestId algorithm.
+ //
// selecting it this way seems unambiguous at least
const stylesheetLink = page.locator(
'map-link[rel=stylesheet][href="restaurants/restaurants.css"]'
diff --git a/test/e2e/elements/map-link/map-link-media.html b/test/e2e/elements/map-link/map-link-media.html
index 7f1b4919e..21dd8dd79 100644
--- a/test/e2e/elements/map-link/map-link-media.html
+++ b/test/e2e/elements/map-link/map-link-media.html
@@ -104,7 +104,7 @@
-
+
@@ -117,7 +117,7 @@
Italian
Mexican
-
+
diff --git a/test/e2e/elements/map-link/map-link-media.test.js b/test/e2e/elements/map-link/map-link-media.test.js
index ec6ed6be1..e6e9b6ee8 100644
--- a/test/e2e/elements/map-link/map-link-media.test.js
+++ b/test/e2e/elements/map-link/map-link-media.test.js
@@ -1,3 +1,7 @@
+/* to do: test that map-link rel=features is re-enabled when the media attribute
+ * is removed
+ */
+
import { test, expect, chromium } from '@playwright/test';
test.describe('map-link media attribute', () => {
@@ -98,4 +102,16 @@ test.describe('map-link media attribute', () => {
await expect(layer).not.toHaveAttribute('disabled');
await expect(mapLink).not.toHaveAttribute('disabled');
});
+ test('map-link rel=features is enabled when non-matching media attribute removed', async () => {
+ const viewer = page.getByTestId('viewer');
+ const featuresLink = page.getByTestId('features-link');
+ await featuresLink.evaluate((l) => (l.media = '(16 < map-zoom <= 18)'));
+ await expect(featuresLink).toHaveAttribute('disabled');
+ await featuresLink.evaluate((l) => l.removeAttribute('media'));
+ await expect(featuresLink).not.toHaveAttribute('disabled');
+ await page.waitForTimeout(500);
+ await expect(viewer).toHaveScreenshot('default_styled_markers.png', {
+ maxDiffPixels: 100
+ });
+ });
});
diff --git a/test/e2e/elements/map-link/map-link-media.test.js-snapshots/default-styled-markers-linux.png b/test/e2e/elements/map-link/map-link-media.test.js-snapshots/default-styled-markers-linux.png
new file mode 100644
index 000000000..306346c49
Binary files /dev/null and b/test/e2e/elements/map-link/map-link-media.test.js-snapshots/default-styled-markers-linux.png differ
diff --git a/test/e2e/elements/map-link/map-link-media.test.js-snapshots/default-styled-markers-win32.png b/test/e2e/elements/map-link/map-link-media.test.js-snapshots/default-styled-markers-win32.png
new file mode 100644
index 000000000..1411dc1f8
Binary files /dev/null and b/test/e2e/elements/map-link/map-link-media.test.js-snapshots/default-styled-markers-win32.png differ
diff --git a/test/e2e/elements/map-link/map-link-stylesheet-media.html b/test/e2e/elements/map-link/map-link-stylesheet-media.html
new file mode 100644
index 000000000..4c83e3ca1
--- /dev/null
+++ b/test/e2e/elements/map-link/map-link-stylesheet-media.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+ map-link-stylesheet-media.html
+
+
+
+
+
+
+
+
+
+
+
+
+ All cuisines
+ African
+ Asian
+ Cajun
+ Indian
+ Italian
+ Mexican
+
+
+
+
+
+
+
diff --git a/test/e2e/elements/map-link/map-link-stylesheet-media.test.js b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js
new file mode 100644
index 000000000..9652b4360
--- /dev/null
+++ b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js
@@ -0,0 +1,69 @@
+import { test, expect, chromium } from '@playwright/test';
+
+test.describe('map-layer rel=stylesheet media attribute', () => {
+ let page;
+ let context;
+ let viewer;
+ let stylesheetLink;
+ test.beforeAll(async function () {
+ context = await chromium.launchPersistentContext('');
+ page =
+ context.pages().find((page) => page.url() === 'about:blank') ||
+ (await context.newPage());
+ await page.goto('map-link-stylesheet-media.html');
+ await page.waitForTimeout(1000);
+ viewer = page.getByTestId('viewer');
+ stylesheetLink = page.locator('map-link[rel=stylesheet][href="red.css"]');
+ });
+ test(`when a map-link disables due to a media query, the styles\
+ should be removed`, async () => {
+ // map starts off at
+ await expect(viewer).toHaveScreenshot('red_styled_markers.png', {
+ maxDiffPixels: 20
+ });
+ await stylesheetLink.evaluate((l) => (l.media = '(14 < map-zoom <= 18)'));
+ await page.waitForTimeout(500);
+ await expect(viewer).toHaveScreenshot('default_styled_markers.png', {
+ maxDiffPixels: 20
+ });
+ });
+ test(`when a map-link enables due to a mq being removed, the \
+ styles should apply`, async () => {
+ await stylesheetLink.evaluate((l) => l.removeAttribute('media'));
+ await expect(viewer).toHaveScreenshot('red_styled_markers.png', {
+ maxDiffPixels: 20
+ });
+ });
+
+ test(`when a map-link enables due to disabled attribute removed\
+ ensure styles change`, async () => {
+ await stylesheetLink.evaluate((l) => (l.disabled = true));
+ await page.waitForTimeout(500);
+ await expect(viewer).toHaveScreenshot('default_styled_markers.png', {
+ maxDiffPixels: 20
+ });
+ await stylesheetLink.evaluate((l) => (l.disabled = false));
+ await page.waitForTimeout(500);
+ await expect(viewer).toHaveScreenshot('red_styled_markers.png', {
+ maxDiffPixels: 20
+ });
+ });
+ test(`when a map-link does not enable due to disabled attribute \
+removed while a non-matching media query is present, ensure that style does not \
+change`, async () => {
+ await stylesheetLink.evaluate((l) => (l.disabled = true));
+ await page.waitForTimeout(500);
+ await expect(viewer).toHaveScreenshot('default_styled_markers.png', {
+ maxDiffPixels: 20
+ });
+ // non-matching query (map z=14)
+ await stylesheetLink.evaluate((l) => (l.media = '(14 < map-zoom <= 18)'));
+ await page.waitForTimeout(500);
+ // disabled overrides the media query because they compete to change it
+ await stylesheetLink.evaluate((l) => (l.disabled = false));
+ await page.waitForTimeout(500);
+ await expect(viewer).toHaveScreenshot('red_styled_markers.png', {
+ maxDiffPixels: 20
+ });
+ });
+});
diff --git a/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/default-styled-markers-linux.png b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/default-styled-markers-linux.png
new file mode 100644
index 000000000..3f7a30a23
Binary files /dev/null and b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/default-styled-markers-linux.png differ
diff --git a/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/default-styled-markers-win32.png b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/default-styled-markers-win32.png
new file mode 100644
index 000000000..3f7a30a23
Binary files /dev/null and b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/default-styled-markers-win32.png differ
diff --git a/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/red-styled-markers-linux.png b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/red-styled-markers-linux.png
new file mode 100644
index 000000000..80bf5288a
Binary files /dev/null and b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/red-styled-markers-linux.png differ
diff --git a/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/red-styled-markers-win32.png b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/red-styled-markers-win32.png
new file mode 100644
index 000000000..80bf5288a
Binary files /dev/null and b/test/e2e/elements/map-link/map-link-stylesheet-media.test.js-snapshots/red-styled-markers-win32.png differ
diff --git a/test/e2e/elements/map-link/red.css b/test/e2e/elements/map-link/red.css
new file mode 100644
index 000000000..2ed8d7315
--- /dev/null
+++ b/test/e2e/elements/map-link/red.css
@@ -0,0 +1,4 @@
+.restaurant {
+ fill: red;
+ stroke: red;
+}