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; +}