Skip to content

Commit

Permalink
Add map-link.disabled for rel=tile|image|features|query|stylesheet
Browse files Browse the repository at this point in the history
- Rename map-link._validateDisabled() to map-link.isVisible()

- Add short circuit check for this.disabled in map-link.isVisible
- Add map-link.disabled as criteria for whenReady() to resolve for rel=
  tile,image,features,query.

- Add await to call to _createTemplatedLink which was missing, possibly
  causing lack of rendering when invoking map-link.enable().

- Do map-link.getLayerEl()._validateDisabled() after enable/disable of
  link - updates layer control if applicable

- Refactor connectedCallback to consolidate stylesheet code into
  _createStylesheetLink method.  Update whenReady criteria for both types
  of stylesheet link.

- Ensure that protomaps stylesheet link selected by templated link is not
  prior disabled

- Add tests for map-link.disabled for rel=tile,image,features,query
- Update layer so that disabled query links aren't queryable
- Add css, class values for test restaurants

- Add waitForTimeout(500) to de-flake the tests

- fixed test i headful mode

- Update map-extent to ignore disabled links for bounds calculations
- Update tests to include logic for initially-disabled templated link

- Replace waitForTimeout(500) with { sloMo: 500 }
  • Loading branch information
prushforth committed Dec 12, 2024
1 parent 9875afc commit 178acd9
Show file tree
Hide file tree
Showing 24 changed files with 437 additions and 71 deletions.
4 changes: 3 additions & 1 deletion src/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,9 @@ export class BaseLayerElement extends HTMLElement {
queryable() {
let content = this.src ? this.shadowRoot : this;
return (
content.querySelector('map-extent[checked] > map-link[rel=query]') &&
content.querySelector(
'map-extent[checked] > map-link[rel=query]:not([disabled])'
) &&
this.checked &&
this._layer &&
!this.hidden
Expand Down
4 changes: 2 additions & 2 deletions src/map-extent.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ export class HTMLExtentElement extends HTMLElement {
let totalTemplateCount = templates.length,
disabledTemplateCount = 0;
for (let j = 0; j < totalTemplateCount; j++) {
if (!templates[j]._validateDisabled()) {
if (!templates[j].isVisible()) {
disabledTemplateCount++;
}
}
Expand Down Expand Up @@ -494,7 +494,7 @@ export class HTMLExtentElement extends HTMLElement {
maxNativeZoom = -Infinity,
minNativeZoom = Infinity,
templates = this.querySelectorAll(
'map-link[rel=image],map-link[rel=tile],map-link[rel=features],map-link[rel=query]'
'map-link[rel=image]:not([disabled]),map-link[rel=tile]:not([disabled]),map-link[rel=features]:not([disabled]),map-link[rel=query]:not([disabled])'
);

// initialize bounds from this.scope > map-meta
Expand Down
178 changes: 126 additions & 52 deletions src/map-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export class HTMLLinkElement extends HTMLElement {
'hreflang',
'tref',
'tms',
'projection'
'projection',
'disabled'
];
}
/* jshint ignore:start */
Expand Down Expand Up @@ -174,6 +175,16 @@ export class HTMLLinkElement extends HTMLElement {
getLayerEl() {
return Util.getClosest(this, 'map-layer,layer-');
}
get disabled() {
return this.hasAttribute('disabled');
}
set disabled(val) {
if (val) {
this.setAttribute('disabled', '');
} else {
this.removeAttribute('disabled');
}
}

attributeChangedCallback(name, oldValue, newValue) {
//['type','rel','href','hreflang','tref','tms','projection'];
Expand Down Expand Up @@ -239,14 +250,21 @@ export class HTMLLinkElement extends HTMLElement {
// handle side effects
}
break;
case 'disabled':
if (typeof newValue === 'string') {
this.disableLink();
} else {
this.enableLink();
}
break;
}
}
}
constructor() {
// Always call super first in constructor
super();
}
connectedCallback() {
async connectedCallback() {
this.#hasConnected = true; /* jshint ignore:line */
if (
this.getLayerEl().hasAttribute('data-moving') ||
Expand All @@ -259,8 +277,10 @@ export class HTMLLinkElement extends HTMLElement {
case 'image':
case 'features':
case 'query':
this._initTemplateVars();
this._createTemplatedLink();
if (!this.disabled) {
this._initTemplateVars();
await this._createTemplatedLink();
}
break;
case 'style':
case 'self':
Expand All @@ -276,25 +296,7 @@ export class HTMLLinkElement extends HTMLElement {
//this._createLegendLink();
break;
case 'stylesheet':
// MIME type application/pmtiles+stylesheet is an invention of the requirement to get
// closer to loading style rules as CSS does, via link / (map-link)
// we could probably do something similar with map-style i.e. treat the
// content of map-style as though it was a stylesheet tbd caveat CSP
if (this.type === 'application/pmtiles+stylesheet') {
const pmtilesStyles = new URL(this.href, this.getBase()).href;
import(pmtilesStyles)
.then((module) => module.pmtilesRulesReady)
.then((initializedRules) => {
this._pmtilesRules = initializedRules;
})
.catch((reason) => {
console.error(
'Error importing pmtiles symbolizer rules or theme: \n' + reason
);
});
} else {
this._createStylesheetLink();
}
this._createStylesheetLink();
break;
case 'alternate':
this._createAlternateLink(); // add media attribute
Expand All @@ -320,35 +322,100 @@ export class HTMLLinkElement extends HTMLElement {
break;
}
}
disableLink() {
switch (this.rel.toLowerCase()) {
case 'tile':
case 'image':
case 'features':
// tile, image, features
if (
this._templatedLayer &&
this.parentExtent?._extentLayer?.hasLayer(this._templatedLayer)
) {
this.parentExtent._extentLayer.removeLayer(this._templatedLayer);
delete this._templatedLayer;
this.getLayerEl()._validateDisabled();
}
break;
case 'query':
delete this._templateVars;
if (this.shadowRoot) {
this.shadowRoot.innerHTML = '';
}
this.getLayerEl()._validateDisabled();
break;
case 'stylesheet':
delete this._pmtilesRules;
delete this._stylesheetHost;
if (this.link) {
this.link.remove();
delete this.link;
}
break;
}
}
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
this.getLayerEl()._validateDisabled();
});
break;
}
}
_createAlternateLink(mapml) {
if (this.href && this.projection) this._alternate = true;
}
_createStylesheetLink() {
// if the parent element is a map-link, the stylesheet is a link that should
// be loaded as part of a templated layer processing i.e. on moveend
// and the generated <link> that implements this <map-link> should be located
// in the parent <map-link>._templatedLayer.container root node if
// the _templatedLayer is an instance of TemplatedTileLayer or TemplatedFeaturesLayer
//
// if the parent node (or the host of the shadow root parent node) is map-layer, the link should be created in the _layer
// container
this._stylesheetHost =
this.getRootNode() instanceof ShadowRoot
? this.getRootNode().host
: this.parentElement;
if (this._stylesheetHost === undefined) return;
// MIME type application/pmtiles+stylesheet is an invention of the requirement to get
// closer to loading style rules as CSS does, via link / (map-link)
// we could probably do something similar with map-style i.e. treat the
// content of map-style as though it was a stylesheet tbd caveat CSP
if (this.type === 'application/pmtiles+stylesheet') {
const pmtilesStyles = new URL(this.href, this.getBase()).href;
import(pmtilesStyles)
.then((module) => module.pmtilesRulesReady)
.then((initializedRules) => {
this._pmtilesRules = initializedRules;
})
.catch((reason) => {
console.error(
'Error importing pmtiles symbolizer rules or theme: \n' + reason
);
});
} else {
// a CSS stylesheet
// if the parent element is a map-link, the stylesheet is a link that should
// be loaded as part of a templated layer processing i.e. on moveend
// and the generated <link> that implements this <map-link> should be located
// in the parent <map-link>._templatedLayer.container root node if
// the _templatedLayer is an instance of TemplatedTileLayer or TemplatedFeaturesLayer
//
// if the parent node (or the host of the shadow root parent node) is map-layer, the link should be created in the _layer
// container
this._stylesheetHost =
this.getRootNode() instanceof ShadowRoot
? this.getRootNode().host
: this.parentElement;
if (this._stylesheetHost === undefined) return;

this.link = document.createElement('link');
this.link.mapLink = this;
this.link.setAttribute('href', new URL(this.href, this.getBase()).href);
copyAttributes(this, this.link);
this.link = document.createElement('link');
this.link.mapLink = this;
this.link.setAttribute('href', new URL(this.href, this.getBase()).href);
copyAttributes(this, this.link);

if (this._stylesheetHost._layer) {
this._stylesheetHost._layer.appendStyleLink(this);
} else if (this._stylesheetHost._templatedLayer) {
this._stylesheetHost._templatedLayer.appendStyleLink(this);
} else if (this._stylesheetHost._extentLayer) {
this._stylesheetHost._extentLayer.appendStyleLink(this);
if (this._stylesheetHost._layer) {
this._stylesheetHost._layer.appendStyleLink(this);
} else if (this._stylesheetHost._templatedLayer) {
this._stylesheetHost._templatedLayer.appendStyleLink(this);
} else if (this._stylesheetHost._extentLayer) {
this._stylesheetHost._extentLayer.appendStyleLink(this);
}
}

function copyAttributes(source, target) {
Expand All @@ -366,7 +433,7 @@ export class HTMLLinkElement extends HTMLElement {
this.parentNode.nodeName.toUpperCase() === 'MAP-EXTENT'
? this.parentNode
: this.parentNode.host;
if (!this.tref || !this.parentExtent) return;
if (this.disabled || !this.tref || !this.parentExtent) return;
try {
await this.parentExtent.whenReady();
await this._templateVars.inputsReady;
Expand All @@ -386,7 +453,7 @@ export class HTMLLinkElement extends HTMLElement {
this.type === 'application/vnd.mapbox-vector-tile'
) {
let s =
'map-link[rel="stylesheet"][type="application/pmtiles+stylesheet"]';
'map-link[rel="stylesheet"][type="application/pmtiles+stylesheet"]:not([disabled])';
let pmtilesStylesheetLink = this.getLayerEl().src
? this.closest('map-extent')?.querySelector(s) ??
this.getRootNode().querySelector(':host > ' + s)
Expand Down Expand Up @@ -439,7 +506,9 @@ export class HTMLLinkElement extends HTMLElement {
}).addTo(this.parentExtent._extentLayer);
} else if (this.rel === 'features') {
// map-feature retrieved by link will be stored in shadowRoot owned by link
this.attachShadow({ mode: 'open' });
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' });
}
this._templatedLayer = templatedFeaturesLayer(this._templateVars, {
zoomBounds: this.getZoomBounds(),
extentBounds: this.getBounds(),
Expand All @@ -448,7 +517,9 @@ export class HTMLLinkElement extends HTMLElement {
linkEl: this
}).addTo(this.parentExtent._extentLayer);
} else if (this.rel === 'query') {
this.attachShadow({ mode: 'open' });
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' });
}
extend(this._templateVars, this._setupQueryVars(this._templateVars));
extend(this._templateVars, { extentBounds: this.getBounds() });
}
Expand Down Expand Up @@ -853,7 +924,8 @@ export class HTMLLinkElement extends HTMLElement {

return zoomBounds;
}
_validateDisabled() {
isVisible() {
if (this.disabled) return false;
let isVisible = false,
map = this.getMapEl(),
mapZoom = map.zoom,
Expand Down Expand Up @@ -958,6 +1030,7 @@ export class HTMLLinkElement extends HTMLElement {
case 'image':
case 'features':
ready = '_templatedLayer';
if (this.disabled) resolve();
break;
case 'style':
case 'self':
Expand All @@ -967,6 +1040,7 @@ export class HTMLLinkElement extends HTMLElement {
break;
case 'query':
ready = 'shadowRoot';
if (this.disabled) resolve();
break;
case 'alternate':
ready = '_alternate';
Expand All @@ -975,7 +1049,7 @@ export class HTMLLinkElement extends HTMLElement {
if (this.type === 'application/pmtiles+stylesheet') {
ready = '_pmtilesRules';
} else {
resolve();
ready = '_stylesheetHost';
}
break;
case 'zoomin':
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/data/restaurants/african.mapml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<map-meta name="extent" content="top-left-easting=1509080.1964270622,top-left-northing=-169400.558801122,bottom-right-easting=1512094.3357886747,bottom-right-northing=-172702.5654051304" ></map-meta>
</map-head>
<map-body>
<map-feature data-testid="hareg" id="restaurant_map-point.9" class="restaurant_map-point">
<map-feature data-testid="hareg" id="restaurant_map-point.9" class="restaurant_map-point african">
<map-featurecaption>Hareg Cafe &amp; Variety</map-featurecaption>
<map-geometry>
<map-point>
Expand Down
6 changes: 3 additions & 3 deletions test/e2e/data/restaurants/asian.mapml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<map-meta name="extent" content="top-left-easting=1509080.1964270622,top-left-northing=-169400.558801122,bottom-right-easting=1512094.3357886747,bottom-right-northing=-172702.5654051304" ></map-meta>
</map-head>
<map-body>
<map-feature id="restaurant_map-point.77" class="restaurant_map-point">
<map-feature id="restaurant_map-point.77" class="restaurant_map-point asian chinese">
<map-featurecaption>Hung Sum Restaurant</map-featurecaption>
<map-geometry>
<map-point>
Expand Down Expand Up @@ -50,7 +50,7 @@
</table>
</map-properties>
</map-feature>
<map-feature id="restaurant_map-point.13" class="restaurant_map-point">
<map-feature id="restaurant_map-point.13" class="restaurant_map-point asian thai">
<map-featurecaption>Phucket Royal</map-featurecaption>
<map-geometry>
<map-point>
Expand Down Expand Up @@ -90,7 +90,7 @@
</table>
</map-properties>
</map-feature>
<map-feature id="restaurant_map-point.18" class="restaurant_map-point">
<map-feature id="restaurant_map-point.18" class="restaurant_map-point asian japanese">
<map-featurecaption>Sushi 88</map-featurecaption>
<map-geometry>
<map-point>
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/data/restaurants/cajun.mapml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<map-meta name="extent" content="top-left-easting=1509080.1964270622,top-left-northing=-169400.558801122,bottom-right-easting=1512094.3357886747,bottom-right-northing=-172702.5654051304" ></map-meta>
</map-head>
<map-body>
<map-feature id="restaurant_map-point.2" class="restaurant_map-point">
<map-feature id="restaurant_map-point.2" class="restaurant_map-point cajun">
<map-featurecaption>Big Daddy's</map-featurecaption>
<map-geometry>
<map-point>
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/data/restaurants/indian.mapml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<map-meta name="extent" content="top-left-easting=1509080.1964270622,top-left-northing=-169400.558801122,bottom-right-easting=1512094.3357886747,bottom-right-northing=-172702.5654051304" ></map-meta>
</map-head>
<map-body>
<map-feature id="restaurant_polygon.7" class="restaurant_polygon">
<map-feature id="restaurant_polygon.7" class="restaurant_polygon asian indian">
<map-featurecaption>India Palace</map-featurecaption>
<map-geometry>
<map-point>
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/data/restaurants/italian.mapml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<map-meta name="extent" content="top-left-easting=1509080.1964270622,top-left-northing=-169400.558801122,bottom-right-easting=1512094.3357886747,bottom-right-northing=-172702.5654051304" ></map-meta>
</map-head>
<map-body>
<map-feature id="restaurant_polygon.20" class="restaurant_polygon">
<map-feature id="restaurant_polygon.20" class="restaurant_polygon italian">
<map-featurecaption>The Prescott</map-featurecaption>
<map-geometry>
<map-point>
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/data/restaurants/mexican.mapml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<map-meta name="extent" content="top-left-easting=1509080.1964270622,top-left-northing=-169400.558801122,bottom-right-easting=1512094.3357886747,bottom-right-northing=-172702.5654051304" ></map-meta>
</map-head>
<map-body>
<map-feature id="restaurant_polygon.2" class="restaurant_polygon">
<map-feature id="restaurant_polygon.2" class="restaurant_polygon mexican">
<map-featurecaption>Banditos</map-featurecaption>
<map-geometry>
<map-point>
Expand Down
Loading

0 comments on commit 178acd9

Please sign in to comment.