diff --git a/cypress/e2e/layer-editor.cy.ts b/cypress/e2e/layer-editor.cy.ts index a855b8e1..a7906b8c 100644 --- a/cypress/e2e/layer-editor.cy.ts +++ b/cypress/e2e/layer-editor.cy.ts @@ -207,6 +207,20 @@ describe("layer editor", () => { it("compound filter"); }); + describe("layout", () => { + it("text-font", () => { + when.setStyle("font"); + when.collapseGroupInLayerEditor(); + when.collapseGroupInLayerEditor(1); + when.collapseGroupInLayerEditor(2); + when.doWithin("spec-field:text-font", () => { + get.element(".maputnik-autocomplete input").first().click(); + }); + then(get.element(".maputnik-autocomplete-menu-item")).shouldBeVisible(); + then(get.element(".maputnik-autocomplete-menu-item")).shouldHaveLength(3); + }); + }); + describe("paint", () => { it("expand/collapse"); it("color"); diff --git a/cypress/e2e/maputnik-driver.ts b/cypress/e2e/maputnik-driver.ts index cca696b7..72fdf89e 100644 --- a/cypress/e2e/maputnik-driver.ts +++ b/cypress/e2e/maputnik-driver.ts @@ -85,6 +85,13 @@ export class MaputnikDriver { fixture: "rectangles-style.json", }, }); + this.helper.given.interceptAndMockResponse({ + method: "GET", + url: baseUrl + "example-style-with-fonts.json", + response: { + fixture: "example-style-with-fonts.json", + }, + }); this.helper.given.interceptAndMockResponse({ method: "GET", url: "*example.local/*", @@ -95,6 +102,11 @@ export class MaputnikDriver { url: "*example.com/*", response: [], }); + this.helper.given.interceptAndMockResponse({ + method: "GET", + url: "https://www.glyph-server.com/*", + response: ["Font 1", "Font 2", "Font 3"], + }); }, }; @@ -114,7 +126,7 @@ export class MaputnikDriver { this.helper.when.wait(200); }, setStyle: ( - styleProperties: "geojson" | "raster" | "both" | "layer" | "rectangles" | "", + styleProperties: "geojson" | "raster" | "both" | "layer" | "rectangles" | "font" | "", zoom?: number ) => { const url = new URL(baseUrl); @@ -134,6 +146,9 @@ export class MaputnikDriver { case "rectangles": url.searchParams.set("style", baseUrl + "rectangles-style.json"); break; + case "font": + url.searchParams.set("style", baseUrl + "example-style-with-fonts.json"); + break; } if (zoom) { diff --git a/cypress/fixtures/example-style-with-fonts.json b/cypress/fixtures/example-style-with-fonts.json new file mode 100644 index 00000000..673a95dc --- /dev/null +++ b/cypress/fixtures/example-style-with-fonts.json @@ -0,0 +1,38 @@ +{ + "id": "test-style", + "version": 8, + "name": "Test Style", + "metadata": { + "maputnik:renderer": "mlgljs" + }, + "sources": { + "example": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features":[{ + "type": "Feature", + "properties": { + "name": "Dinagat Islands" + }, + "geometry":{ + "type": "Point", + "coordinates": [125.6, 10.1] + } + }] + } + } + }, + "glyphs": "https://www.glyph-server.com/fonts/{fontstack}/{range}.pbf", + "sprites": "https://example.local/fonts/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "label", + "type": "symbol", + "source": "example", + "layout": { + "text-font": ["Font"] + } + } + ] +} diff --git a/src/components/App.tsx b/src/components/App.tsx index 42601762..d78823a1 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -294,13 +294,13 @@ export default class App extends React.Component { const accessToken = metadata["maputnik:openmaptiles_access_token"] || tokens.openmaptiles; const glyphUrl = (typeof urlTemplate === "string")? urlTemplate.replace("{key}", accessToken): urlTemplate; - downloadGlyphsMetadata(glyphUrl, fonts => { + downloadGlyphsMetadata(glyphUrl).then(fonts => { this.setState({ spec: updateRootSpec(this.state.spec, "glyphs", fonts)}); }); } updateIcons(baseUrl: string) { - downloadSpriteMetadata(baseUrl, icons => { + downloadSpriteMetadata(baseUrl).then(icons => { this.setState({ spec: updateRootSpec(this.state.spec, "sprite", icons)}); }); } diff --git a/src/libs/metadata.ts b/src/libs/metadata.ts index fde41f74..8eba10fc 100644 --- a/src/libs/metadata.ts +++ b/src/libs/metadata.ts @@ -1,44 +1,39 @@ -import npmurl from "url"; - -function loadJSON(url: string, defaultValue: any, cb: (...args: any[]) => void) { - fetch(url, { - mode: "cors", - credentials: "same-origin" - }) - .then((response) => { - if (!response.ok) { - throw new Error("Failed to load metadata for " + url); - } - return response.json(); - }) - .then((body) => { - cb(body); - }) - .catch(() => { - console.warn("Can not load metadata for " + url + ", using default value " + defaultValue); - cb(defaultValue); +async function loadJSON(url: string, defaultValue: T): Promise { + try { + const response = await fetch(url, { + mode: "cors", + credentials: "same-origin" }); + if (!response.ok) { + throw new Error("Failed to load metadata for " + url); + } + return await response.json(); + } catch { + console.warn("Can not load metadata for " + url + ", using default value " + defaultValue); + return defaultValue; + } } -export function downloadGlyphsMetadata(urlTemplate: string, cb: (...args: any[]) => void) { - if(!urlTemplate) return cb([]); +export async function downloadGlyphsMetadata(urlTemplate: string): Promise { + if(!urlTemplate) return []; // Special handling because Tileserver GL serves the fontstacks metadata differently // https://github.com/klokantech/tileserver-gl/pull/104#issuecomment-274444087 - const urlObj = npmurl.parse(urlTemplate); - const normPathPart = "/%7Bfontstack%7D/%7Brange%7D.pbf"; + const urlObj = new URL(urlTemplate); + const normPathPart = "/" + encodeURIComponent("{fontstack}") + "/" + encodeURIComponent("{range}") + ".pbf"; if(urlObj.pathname === normPathPart) { urlObj.pathname = "/fontstacks.json"; } else { urlObj.pathname = urlObj.pathname!.replace(normPathPart, ".json"); } - const url = npmurl.format(urlObj); - - loadJSON(url, [], cb); + const url = urlObj.toString(); + const fonts = await loadJSON(url, [] as string[]); + return [...new Set(fonts)]; } -export function downloadSpriteMetadata(baseUrl: string, cb: (...args: any[]) => void) { - if(!baseUrl) return cb([]); +export async function downloadSpriteMetadata(baseUrl: string): Promise { + if(!baseUrl) return []; const url = baseUrl + ".json"; - loadJSON(url, {}, glyphs => cb(Object.keys(glyphs))); + const glyphs = await loadJSON(url, {} as Record); + return Object.keys(glyphs); } diff --git a/src/styles/_input.scss b/src/styles/_input.scss index 180833a2..7a5493a1 100644 --- a/src/styles/_input.scss +++ b/src/styles/_input.scss @@ -198,7 +198,7 @@ border: none; padding: 2px 0; margin-right: 10px; - position: absolute; + position: absolute !important; overflow: auto; max-height: 50%; background: vars.$color-gray;