mirror of
https://github.com/maputnik/editor.git
synced 2026-06-22 23:27:26 +00:00
set correct map view if opened stylefile provides a map view and the current map is empty (#1552)
## Launch Checklist closes https://github.com/maplibre/maputnik/issues/1546 - [x] Link to related issues. https://github.com/maplibre/maputnik/issues/1546 - [x] Write tests for all new functionality. - [x] Add an entry to `CHANGELOG.md` under the `## main` section. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Harel M <harel.mazor@gmail.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
- Upgraded codemirror from version 5 to version 6
|
- Upgraded codemirror from version 5 to version 6
|
||||||
- Add code editor to allow editing the entire style
|
- Add code editor to allow editing the entire style
|
||||||
- Add support for sprite object in setting modal
|
- Add support for sprite object in setting modal
|
||||||
|
- Set the correct map view when opening a new style on an empty map
|
||||||
- Allow root-relative urls in the stylefile
|
- Allow root-relative urls in the stylefile
|
||||||
- _...Add new stuff here..._
|
- _...Add new stuff here..._
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,21 @@ describe("map", () => {
|
|||||||
"Zoom: " + (zoomLevel + 1)
|
"Zoom: " + (zoomLevel + 1)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("via style file definition", () => {
|
||||||
|
when.setStyle("zoom_7_center_0_51");
|
||||||
|
then(get.elementByTestId("maplibre:ctrl-zoom")).shouldBeVisible();
|
||||||
|
then(get.elementByTestId("maplibre:ctrl-zoom")).shouldContainText(
|
||||||
|
"Zoom: " + (7)
|
||||||
|
);
|
||||||
|
then(get.locationHash().should("contain", "#7/51/0"));
|
||||||
|
|
||||||
|
// opening another stylefile does not update the map view again
|
||||||
|
// as discussed in https://github.com/maplibre/maputnik/issues/1546
|
||||||
|
when.openASecondStyleWithDifferentZoomAndCenter();
|
||||||
|
then(get.locationHash().should("contain", "#7/51/0"));
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("search", () => {
|
describe("search", () => {
|
||||||
@@ -33,6 +48,7 @@ describe("map", () => {
|
|||||||
describe("popup", () => {
|
describe("popup", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
when.setStyle("rectangles");
|
when.setStyle("rectangles");
|
||||||
|
then(get.locationHash().should("exist"));
|
||||||
});
|
});
|
||||||
it("should open on feature click", () => {
|
it("should open on feature click", () => {
|
||||||
when.clickCenter("maplibre:map");
|
when.clickCenter("maplibre:map");
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export default class MaputnikCypressHelper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public get = {
|
public get = {
|
||||||
|
locationHash: (): Cypress.Chainable<string> => cy.location("hash"),
|
||||||
...this.helper.get,
|
...this.helper.get,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,20 @@ export class MaputnikDriver {
|
|||||||
fixture: "example-style-with-fonts.json",
|
fixture: "example-style-with-fonts.json",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
this.helper.given.interceptAndMockResponse({
|
||||||
|
method: "GET",
|
||||||
|
url: baseUrl + "example-style-with-zoom-7-and-center-0-51.json",
|
||||||
|
response: {
|
||||||
|
fixture: "example-style-with-zoom-7-and-center-0-51.json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.helper.given.interceptAndMockResponse({
|
||||||
|
method: "GET",
|
||||||
|
url: baseUrl + "example-style-with-zoom-5-and-center-50-50.json",
|
||||||
|
response: {
|
||||||
|
fixture: "example-style-with-zoom-5-and-center-50-50.json",
|
||||||
|
},
|
||||||
|
});
|
||||||
this.helper.given.interceptAndMockResponse({
|
this.helper.given.interceptAndMockResponse({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "*example.local/*",
|
url: "*example.local/*",
|
||||||
@@ -120,13 +134,20 @@ export class MaputnikDriver {
|
|||||||
waitForExampleFileResponse: () => {
|
waitForExampleFileResponse: () => {
|
||||||
this.helper.when.waitForResponse("example-style.json");
|
this.helper.when.waitForResponse("example-style.json");
|
||||||
},
|
},
|
||||||
|
openASecondStyleWithDifferentZoomAndCenter: () => {
|
||||||
|
cy.contains("button", "Open").click();
|
||||||
|
cy.get('[data-wd-key="modal:open.url.input"]')
|
||||||
|
.should("be.enabled")
|
||||||
|
.clear()
|
||||||
|
.type("http://localhost:8888/example-style-with-zoom-5-and-center-50-50.json{enter}");
|
||||||
|
},
|
||||||
chooseExampleFile: () => {
|
chooseExampleFile: () => {
|
||||||
this.helper.given.fixture("example-style.json", "example-style.json");
|
this.helper.given.fixture("example-style.json", "example-style.json");
|
||||||
this.helper.when.openFileByFixture("example-style.json", "modal:open.file.button", "modal:open.file.input");
|
this.helper.when.openFileByFixture("example-style.json", "modal:open.file.button", "modal:open.file.input");
|
||||||
this.helper.when.wait(200);
|
this.helper.when.wait(200);
|
||||||
},
|
},
|
||||||
setStyle: (
|
setStyle: (
|
||||||
styleProperties: "geojson" | "raster" | "both" | "layer" | "rectangles" | "font" | "",
|
styleProperties: "geojson" | "raster" | "both" | "layer" | "rectangles" | "font" | "zoom_7_center_0_51" | "",
|
||||||
zoom?: number
|
zoom?: number
|
||||||
) => {
|
) => {
|
||||||
const url = new URL(baseUrl);
|
const url = new URL(baseUrl);
|
||||||
@@ -149,6 +170,9 @@ export class MaputnikDriver {
|
|||||||
case "font":
|
case "font":
|
||||||
url.searchParams.set("style", baseUrl + "example-style-with-fonts.json");
|
url.searchParams.set("style", baseUrl + "example-style-with-fonts.json");
|
||||||
break;
|
break;
|
||||||
|
case "zoom_7_center_0_51":
|
||||||
|
url.searchParams.set("style", baseUrl + "example-style-with-zoom-7-and-center-0-51.json");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zoom) {
|
if (zoom) {
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"id": "test-style",
|
||||||
|
"center": [50,50],
|
||||||
|
"zoom": 5,
|
||||||
|
"version": 8,
|
||||||
|
"name": "Test Style",
|
||||||
|
"sources": {
|
||||||
|
"rectangles": {
|
||||||
|
"type": "geojson",
|
||||||
|
"data": {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"layers": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"id": "test-style",
|
||||||
|
"center": [0,51],
|
||||||
|
"zoom": 7,
|
||||||
|
"version": 8,
|
||||||
|
"name": "Test Style",
|
||||||
|
"sources": {
|
||||||
|
"rectangles": {
|
||||||
|
"type": "geojson",
|
||||||
|
"data": {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": []
|
||||||
|
}
|
||||||
+22
-1
@@ -99,6 +99,7 @@ type AppState = {
|
|||||||
lng: number,
|
lng: number,
|
||||||
lat: number,
|
lat: number,
|
||||||
},
|
},
|
||||||
|
_from: "map" | "app"
|
||||||
},
|
},
|
||||||
maplibreGlDebugOptions: Partial<MapOptions> & {
|
maplibreGlDebugOptions: Partial<MapOptions> & {
|
||||||
showTileBoundaries: boolean,
|
showTileBoundaries: boolean,
|
||||||
@@ -148,6 +149,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
lng: 0,
|
lng: 0,
|
||||||
lat: 0,
|
lat: 0,
|
||||||
},
|
},
|
||||||
|
_from: "app"
|
||||||
},
|
},
|
||||||
isOpen: {
|
isOpen: {
|
||||||
settings: false,
|
settings: false,
|
||||||
@@ -335,6 +337,13 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
...opts,
|
...opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Detect empty style
|
||||||
|
const oldStyle = this.state.mapStyle;
|
||||||
|
const isEmptySources = !oldStyle.sources || Object.keys(oldStyle.sources).length === 0;
|
||||||
|
const isEmptyLayers = !oldStyle.layers || oldStyle.layers.length === 0;
|
||||||
|
const isEmptyStyle = isEmptySources && isEmptyLayers;
|
||||||
|
|
||||||
// For the style object, find the urls that has "{key}" and insert the correct API keys
|
// For the style object, find the urls that has "{key}" and insert the correct API keys
|
||||||
// Without this, going from e.g. MapTiler to OpenLayers and back will lose the maptlier key.
|
// Without this, going from e.g. MapTiler to OpenLayers and back will lose the maptlier key.
|
||||||
|
|
||||||
@@ -466,15 +475,25 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
this.saveStyle(newStyle);
|
this.saveStyle(newStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const zoom = newStyle?.zoom;
|
||||||
|
const center = newStyle?.center;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
mapStyle: newStyle,
|
mapStyle: newStyle,
|
||||||
dirtyMapStyle: dirtyMapStyle,
|
dirtyMapStyle: dirtyMapStyle,
|
||||||
|
mapView: isEmptyStyle && zoom && center ? {
|
||||||
|
zoom: zoom,
|
||||||
|
center: {
|
||||||
|
lng: center[0],
|
||||||
|
lat: center[1],
|
||||||
|
},
|
||||||
|
_from: "app"
|
||||||
|
} : this.state.mapView,
|
||||||
errors: mappedErrors,
|
errors: mappedErrors,
|
||||||
}, () => {
|
}, () => {
|
||||||
this.fetchSources();
|
this.fetchSources();
|
||||||
this.setStateInUrl();
|
this.setStateInUrl();
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onUndo = () => {
|
onUndo = () => {
|
||||||
@@ -665,6 +684,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
lng: number,
|
lng: number,
|
||||||
lat: number,
|
lat: number,
|
||||||
},
|
},
|
||||||
|
_from: "map" | "app"
|
||||||
}) => {
|
}) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
mapView,
|
mapView,
|
||||||
@@ -676,6 +696,7 @@ export default class App extends React.Component<any, AppState> {
|
|||||||
|
|
||||||
const mapProps = {
|
const mapProps = {
|
||||||
mapStyle: (dirtyMapStyle || mapStyle),
|
mapStyle: (dirtyMapStyle || mapStyle),
|
||||||
|
mapView: this.state.mapView,
|
||||||
replaceAccessTokens: (mapStyle: StyleSpecification) => {
|
replaceAccessTokens: (mapStyle: StyleSpecification) => {
|
||||||
return style.replaceAccessTokens(mapStyle, {
|
return style.replaceAccessTokens(mapStyle, {
|
||||||
allowFallback: true
|
allowFallback: true
|
||||||
|
|||||||
@@ -52,6 +52,14 @@ type MapMaplibreGlInternalProps = {
|
|||||||
onDataChange?(event: {map: Map | null}): unknown
|
onDataChange?(event: {map: Map | null}): unknown
|
||||||
onLayerSelect(index: number): void
|
onLayerSelect(index: number): void
|
||||||
mapStyle: StyleSpecification
|
mapStyle: StyleSpecification
|
||||||
|
mapView: {
|
||||||
|
zoom: number,
|
||||||
|
center: {
|
||||||
|
lng: number,
|
||||||
|
lat: number,
|
||||||
|
},
|
||||||
|
_from: "map" | "app"
|
||||||
|
};
|
||||||
inspectModeEnabled: boolean
|
inspectModeEnabled: boolean
|
||||||
highlightedLayer?: HighlightedLayer
|
highlightedLayer?: HighlightedLayer
|
||||||
options?: Partial<MapOptions> & {
|
options?: Partial<MapOptions> & {
|
||||||
@@ -60,7 +68,7 @@ type MapMaplibreGlInternalProps = {
|
|||||||
showOverdrawInspector?: boolean
|
showOverdrawInspector?: boolean
|
||||||
}
|
}
|
||||||
replaceAccessTokens(mapStyle: StyleSpecification): StyleSpecification
|
replaceAccessTokens(mapStyle: StyleSpecification): StyleSpecification
|
||||||
onChange(value: {center: LngLat, zoom: number}): unknown
|
onChange(value: {center: LngLat, zoom: number, _from: "map" | "app"}): unknown
|
||||||
} & WithTranslation;
|
} & WithTranslation;
|
||||||
|
|
||||||
type MapMaplibreGlState = {
|
type MapMaplibreGlState = {
|
||||||
@@ -117,6 +125,11 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
|
|||||||
map.showTileBoundaries = this.props.options?.showTileBoundaries!;
|
map.showTileBoundaries = this.props.options?.showTileBoundaries!;
|
||||||
map.showCollisionBoxes = this.props.options?.showCollisionBoxes!;
|
map.showCollisionBoxes = this.props.options?.showCollisionBoxes!;
|
||||||
map.showOverdrawInspector = this.props.options?.showOverdrawInspector!;
|
map.showOverdrawInspector = this.props.options?.showOverdrawInspector!;
|
||||||
|
|
||||||
|
// set the map view when the prop was updated from outside
|
||||||
|
if (this.props.mapView._from === "app") {
|
||||||
|
map.jumpTo(this.props.mapView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.state.inspect && this.props.inspectModeEnabled !== this.state.inspect._showInspectMap) {
|
if(this.state.inspect && this.props.inspectModeEnabled !== this.state.inspect._showInspectMap) {
|
||||||
@@ -160,7 +173,7 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
|
|||||||
const mapViewChange = () => {
|
const mapViewChange = () => {
|
||||||
const center = map.getCenter();
|
const center = map.getCenter();
|
||||||
const zoom = map.getZoom();
|
const zoom = map.getZoom();
|
||||||
this.props.onChange({center, zoom});
|
this.props.onChange({center, zoom, _from: "map"});
|
||||||
};
|
};
|
||||||
mapViewChange();
|
mapViewChange();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user