diff --git a/index.html b/index.html index c2cd34860..18f6bf4a3 100644 --- a/index.html +++ b/index.html @@ -139,7 +139,7 @@ Italian Mexican - + diff --git a/src/map-link.js b/src/map-link.js index ed25f4d76..bcc1fa7d6 100644 --- a/src/map-link.js +++ b/src/map-link.js @@ -113,10 +113,7 @@ export class HTMLLinkElement extends HTMLElement { } } get media() { - // return the content of media attribute as an object - // maybe memoizing the object to avoid repeated formatting - // the Util function may need to be renamed? - return Util._metaContentToObject(this.getAttribute('media')); + return this.getAttribute('media'); } set media(val) { this.setAttribute('media', val); @@ -237,6 +234,9 @@ export class HTMLLinkElement extends HTMLElement { } break; case 'media': + if (oldValue !== newValue) { + this._registerMediaQuery(newValue); + } break; case 'tms': // rel = tile @@ -305,6 +305,7 @@ export class HTMLLinkElement extends HTMLElement { // this._createLicenseLink(); break; } + 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) @@ -354,18 +355,48 @@ export class HTMLLinkElement extends HTMLElement { break; } } - enableLink() { + async enableLink() { switch (this.rel.toLowerCase()) { case 'tile': case 'image': case 'features': case 'query': - case 'stylesheet': - this.connectedCallback().then(() => { - // ensures that the layer control is updated, if applicable + if (!this.disabled) { + this._initTemplateVars(); + await this._createTemplatedLink(); this.getLayerEl()._validateDisabled(); - }); + } break; + case 'stylesheet': + this._createStylesheetLink(); + break; + } + } + _registerMediaQuery(mq) { + if (!this._changeHandler) { + // Define and bind the change handler once + this._changeHandler = () => { + this.disabled = !this._mql.matches; + }; + } + + if (mq) { + 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); + } + + // Set up the new media query and listener + this._mql = map.matchMedia(mq); + this._changeHandler(); // Initial evaluation + this._mql.addEventListener('change', this._changeHandler); + } else if (this._mql) { + // Clean up the existing listener + this._mql.removeEventListener('change', this._changeHandler); + delete this._mql; } } _createAlternateLink(mapml) { diff --git a/test/e2e/elements/map-link/map-link-media.html b/test/e2e/elements/map-link/map-link-media.html new file mode 100644 index 000000000..7f1b4919e --- /dev/null +++ b/test/e2e/elements/map-link/map-link-media.html @@ -0,0 +1,125 @@ + + + + + + map-link-media.html + + + + + + + + + + + + All cuisines + African + Asian + Cajun + Indian + 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 new file mode 100644 index 000000000..ec6ed6be1 --- /dev/null +++ b/test/e2e/elements/map-link/map-link-media.test.js @@ -0,0 +1,101 @@ +import { test, expect, chromium } from '@playwright/test'; + +test.describe('map-link media attribute', () => { + let page; + let context; + 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-media.html'); + }); + + test.afterAll(async function () { + await context.close(); + }); + + test('map-link is disabled when media attribute does not match', async () => { + // const map = page.locator('mapml-viewer'); + const layer = page.locator('map-layer'); + const mapLink = page.locator('map-link').first(); + await expect(layer).not.toHaveAttribute('disabled'); + await expect(mapLink).not.toHaveAttribute('disabled'); + + // zoom out so that media attribute no longer matches, features should be disabled + await page.evaluate(() => { + const map = document.querySelector('mapml-viewer'); + map.zoomTo(map.lat, map.lon, 10); + }); + await expect(layer).toHaveAttribute('disabled'); + await expect(mapLink).toHaveAttribute('disabled'); + + // zoom in so that media attribute matches, features should not be disabled + await page.evaluate(() => { + const map = document.querySelector('mapml-viewer'); + map.zoomTo(map.lat, map.lon, 15); + }); + await expect(layer).not.toHaveAttribute('disabled'); + await expect(mapLink).not.toHaveAttribute('disabled'); + }); + + test('remove media attribute works', async () => { + const layer = page.locator('map-layer'); + const mapLink = page.locator('map-link').first(); + await mapLink.evaluate((l) => { + l.media = ''; + }); + + // zooming out no longer disables features + await page.evaluate(() => { + const map = document.querySelector('mapml-viewer'); + map.zoomTo(map.lat, map.lon, 10); + }); + await expect(layer).not.toHaveAttribute('disabled'); + await expect(mapLink).not.toHaveAttribute('disabled'); + }); + + test('set media attribute works', async () => { + const layer = page.locator('map-layer'); + const mapLink = page.locator('map-link').first(); + await mapLink.evaluate((l) => { + l.setAttribute('media', '(11 < map-zoom <= 18)'); + }); + + // zoom out so that media attribute no longer matches, features should be disabled + await page.evaluate(() => { + const map = document.querySelector('mapml-viewer'); + map.zoomTo(map.lat, map.lon, 10); + }); + await expect(layer).toHaveAttribute('disabled'); + await expect(mapLink).toHaveAttribute('disabled'); + + // zoom in so that media attribute matches, features should not be disabled + await page.evaluate(() => { + const map = document.querySelector('mapml-viewer'); + map.zoomTo(map.lat, map.lon, 15); + }); + await expect(layer).not.toHaveAttribute('disabled'); + await expect(mapLink).not.toHaveAttribute('disabled'); + }); + + test('modify media attribute works', async () => { + const layer = page.locator('map-layer'); + const mapLink = page.locator('map-link').first(); + await mapLink.evaluate((l) => { + l.setAttribute('media', '(15 < map-zoom <= 18)'); + }); + + // the media attribute no longer matches, features should be disabled + await expect(layer).toHaveAttribute('disabled'); + await expect(mapLink).toHaveAttribute('disabled'); + + // zoom in so that media attribute matches, features should not be disabled + await page.evaluate(() => { + const map = document.querySelector('mapml-viewer'); + map.zoomTo(map.lat, map.lon, 16); + }); + await expect(layer).not.toHaveAttribute('disabled'); + await expect(mapLink).not.toHaveAttribute('disabled'); + }); +});