Add more end to end tests to increase coverage

This commit is contained in:
HarelM
2026-07-12 10:23:17 +03:00
parent 03a41cf17a
commit be11f75c9f
9 changed files with 428 additions and 20 deletions
+146
View File
@@ -212,6 +212,39 @@ describe("layer editor", () => {
layers: [{ id, type: "fill", source: "example", filter: ["all", ["!=", "name", ""], ["==", "name", ""]] }], layers: [{ id, type: "fill", source: "example", filter: ["all", ["!=", "name", ""], ["==", "name", ""]] }],
}); });
}); });
test("change the combining operator and delete a filter item", async () => {
const id = await when.modal.fillLayers({ type: "fill", layer: "example" });
await when.addFilter();
await when.addFilter();
await when.selectFilterCombiningOperator("any");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, filter: ["any", ["==", "name", ""], ["==", "name", ""]] }],
});
await when.deleteFilterItem();
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, filter: ["any", ["==", "name", ""]] }],
});
});
test("convert a filter to an expression and delete it", async () => {
const id = await when.modal.fillLayers({ type: "fill", layer: "example" });
await when.addFilter();
// A single-item "all" collapses to the bare comparison when migrated.
await when.convertFilterToExpression();
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, filter: ["==", ["get", "name"], ""] }],
});
// Deleting the expression restores the default (empty) filter.
await when.deleteFilterExpression();
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ id, filter: ["all"] }],
});
});
}); });
describe("functions", () => { describe("functions", () => {
@@ -292,6 +325,119 @@ describe("layer editor", () => {
], ],
}); });
}); });
test("edit the base and the stops of a zoom function", async () => {
await when.modal.fillLayers({ type: "circle", layer: "example" });
await when.makeZoomFunction("circle-radius");
await when.setFunctionBase("circle-radius", "2");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-radius": { base: 2, stops: [[6, 5], [10, 5]] } } }],
});
// Editing a stop's zoom re-sorts the stops by zoom.
await when.setFunctionStopValue("circle-radius", "Zoom", 0, "3");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-radius": { base: 2, stops: [[3, 5], [10, 5]] } } }],
});
await when.setFunctionStopValue("circle-radius", "Output value", 0, "9");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-radius": { base: 2, stops: [[3, 9], [10, 5]] } } }],
});
});
test("convert a zoom function to a data function and back", async () => {
await when.modal.fillLayers({ type: "circle", layer: "example" });
await when.makeZoomFunction("circle-radius");
// Picking a non-interpolate scale turns the zoom function into a data one,
// carrying the existing stops over as zoom/value pairs.
await when.selectFunctionType("circle-radius", "categorical");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [
{
paint: {
"circle-radius": {
property: "",
type: "exponential",
stops: [[{ zoom: 6, value: 0 }, 5], [{ zoom: 10, value: 0 }, 5]],
},
},
},
],
});
// Picking "interpolate" converts it back to a plain zoom function.
await when.selectFunctionType("circle-radius", "interpolate");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-radius": { stops: [[6, 5], [10, 5]] } } }],
});
});
test("edit the property, default and stops of a data function", async () => {
await when.modal.fillLayers({ type: "circle", layer: "example" });
await when.setValue("spec-field-input:circle-blur", "1");
await when.makeDataFunction("circle-blur");
await when.setFunctionProperty("circle-blur", "myprop");
await when.setFunctionDefault("circle-blur", "0.5");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-blur": { property: "myprop", default: 0.5 } } }],
});
await when.setFunctionStopValue("circle-blur", "Input value", 0, "7");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [
{
paint: {
"circle-blur": {
property: "myprop",
stops: [[{ zoom: 6, value: 7 }, 1], [{ zoom: 10, value: 0 }, 1]],
},
},
},
],
});
await when.selectFunctionType("circle-blur", "categorical");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-blur": { property: "myprop", type: "categorical" } } }],
});
});
test("convert a property to an expression, revert it and delete it", async () => {
await when.modal.fillLayers({ type: "circle", layer: "example" });
await when.setValue("spec-field-input:circle-blur", "1");
await when.makeExpression("circle-blur");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-blur": ["literal", 1] } }],
});
// Reverting an expression restores the plain value it wrapped.
await when.undoExpression("circle-blur");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-blur": 1 } }],
});
// Deleting an expression falls back to the property's spec default.
await when.makeExpression("circle-blur");
await when.deleteExpression("circle-blur");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-blur": 0 } }],
});
});
test("convert a zoom function to an expression", async () => {
await when.modal.fillLayers({ type: "circle", layer: "example" });
await when.makeZoomFunction("circle-radius");
await when.makeExpression("circle-radius");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
layers: [{ paint: { "circle-radius": ["interpolate", ["linear"], ["zoom"], 6, 5, 10, 5] } }],
});
});
}); });
describe("layout", () => { describe("layout", () => {
+79 -16
View File
@@ -133,11 +133,6 @@ export class MaputnikDriver {
await this.helper.when.click("layer-editor-group:" + title); await this.helper.when.click("layer-editor-group:" + title);
}, },
/** Adds one of the predefined public sources listed in the sources modal. */
addPublicSource: async (index = 0) => {
await this.helper.get.element(".maputnik-public-source-select").nth(index).click();
},
/** /**
* Picks a source for the selected layer from the source autocomplete. * Picks a source for the selected layer from the source autocomplete.
* The autocomplete is a controlled (downshift) input, so the value has to be * The autocomplete is a controlled (downshift) input, so the value has to be
@@ -201,6 +196,62 @@ export class MaputnikDriver {
await container.locator(".maputnik-delete-stop").first().click({ force: true }); await container.locator(".maputnik-delete-stop").first().click({ force: true });
}, },
/** Turns the property into a raw style expression. */
makeExpression: async (fieldName: string) => {
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
await container.scrollIntoViewIfNeeded();
// In the plain spec field the expression button shares the zoom-function
// class and comes first; inside a function editor it has its own test id.
const inFunctionEditor = container.locator("[data-wd-key='convert-to-expression']");
const button =
(await inFunctionEditor.count()) > 0
? inFunctionEditor
: container.locator(".maputnik-make-zoom-function").first();
await button.click({ force: true });
},
/** Reverts an expression back to a plain value. */
undoExpression: async (fieldName: string) => {
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
await container.locator("[data-wd-key='undo-expression']").click({ force: true });
},
/** Removes an expression, restoring the property's spec default. */
deleteExpression: async (fieldName: string) => {
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
await container.locator("[data-wd-key='delete-expression']").click({ force: true });
},
/** Picks the function scale (categorical/interval/exponential/identity/interpolate). */
selectFunctionType: async (fieldName: string, type: string) => {
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
await container.locator("[data-wd-key='function-type'] select").selectOption(type);
},
/** Sets the "Base" input of a zoom/data function. */
setFunctionBase: async (fieldName: string, value: string) => {
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
await container.locator("[data-wd-key='function-base'] input").fill(value);
},
/** Sets the data property a data function keys off of. */
setFunctionProperty: async (fieldName: string, value: string) => {
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
await container.locator("[data-wd-key='function-property'] input").fill(value);
},
/** Sets the fallback value used when a feature has no matching stop. */
setFunctionDefault: async (fieldName: string, value: string) => {
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
await container.locator("[data-wd-key='function-default'] input").fill(value);
},
/** Edits one cell of a function's stop table ("Zoom", "Input value" or "Output value"). */
setFunctionStopValue: async (fieldName: string, column: string, index: number, value: string) => {
const container = this.helper.get.elementByTestId("spec-field-container:" + fieldName);
await container.locator(`[aria-label="${column}"]`).nth(index).fill(value);
},
addFilter: async () => { addFilter: async () => {
const button = this.helper.get.elementByTestId("layer-filter-button"); const button = this.helper.get.elementByTestId("layer-filter-button");
await button.scrollIntoViewIfNeeded(); await button.scrollIntoViewIfNeeded();
@@ -211,8 +262,29 @@ export class MaputnikDriver {
await this.helper.get.element(".maputnik-filter-editor-operator select").first().selectOption(value); await this.helper.get.element(".maputnik-filter-editor-operator select").first().selectOption(value);
}, },
deleteFirstActiveSource: async () => { /** Chooses how the filter items combine: all / none / any. */
await this.helper.get.element(".maputnik-active-source-type-editor-header-delete").first().click(); selectFilterCombiningOperator: async (value: string) => {
await this.helper.when.selectWithin("filter-combining-operator", value);
},
deleteFilterItem: async (index = 0) => {
await this.helper.get
.element(".maputnik-filter-editor-block-action .maputnik-icon-button")
.nth(index)
.click();
},
/** Converts the simple filter editor into a raw expression editor. */
convertFilterToExpression: async () => {
await this.helper.when.click("filter-convert-to-expression");
},
/**
* Deletes the filter expression, restoring the simple filter editor.
* The filter group precedes the paint group, so its button comes first.
*/
deleteFilterExpression: async () => {
await this.helper.get.element("[data-wd-key='delete-expression']").first().click();
}, },
setColorValue: async (fieldName: string, value: string) => { setColorValue: async (fieldName: string, value: string) => {
@@ -238,15 +310,6 @@ export class MaputnikDriver {
await this.helper.when.typeText(text); await this.helper.when.typeText(text);
}, },
exportCreateHtml: async () => {
await this.helper.get.element(".maputnik-modal-export-buttons button").last().click();
},
exportSaveStyle: async () => {
await this.helper.stubSaveFilePicker();
await this.helper.get.element(".maputnik-modal-export-buttons button").first().click();
},
waitForExampleFileResponse: () => this.helper.when.waitForResponse("example-style.json"), waitForExampleFileResponse: () => this.helper.when.waitForResponse("example-style.json"),
/** Fill localStorage until we get a QuotaExceededError. */ /** Fill localStorage until we get a QuotaExceededError. */
+40
View File
@@ -34,5 +34,45 @@ export class ModalDriver {
close: async (key: string) => { close: async (key: string) => {
await this.helper.when.click(key + ".close-modal"); await this.helper.when.click(key + ".close-modal");
}, },
/**
* Adds a source of the given type from the sources modal, keeping whatever
* defaults that type's editor prefills.
*/
addSource: async (sourceId: string, sourceType: string) => {
const { when } = this.helper;
await when.setValue("modal:sources.add.source_id", sourceId);
await when.select("modal:sources.add.source_type", sourceType);
await when.click("modal:sources.add.add_source");
await when.wait(200);
},
/** Adds one of the predefined public sources listed in the sources modal. */
addPublicSource: async (index = 0) => {
await this.helper.get.element(".maputnik-public-source-select").nth(index).click();
},
deleteFirstActiveSource: async () => {
await this.helper.get.element(".maputnik-active-source-type-editor-header-delete").first().click();
},
/** Fills one number box of a coordinate pair in the image/video source editor. */
setCoordinateValue: async (index: number, value: string) => {
const input = this.helper.get
.elementByTestId("modal:sources")
.locator(".maputnik-array input")
.nth(index);
await input.fill(value);
await input.blur();
},
exportCreateHtml: async () => {
await this.helper.get.element(".maputnik-modal-export-buttons button").last().click();
},
exportSaveStyle: async () => {
await this.helper.stubSaveFilePicker();
await this.helper.get.element(".maputnik-modal-export-buttons button").first().click();
},
}; };
} }
+144 -4
View File
@@ -66,11 +66,11 @@ describe("modals", () => {
test("download HTML and save the style", async () => { test("download HTML and save the style", async () => {
// Generate the standalone HTML export (triggers a file download). // Generate the standalone HTML export (triggers a file download).
await when.exportCreateHtml(); await when.modal.exportCreateHtml();
await then(get.elementByTestId("modal:export")).shouldExist(); await then(get.elementByTestId("modal:export")).shouldExist();
// Saving the style closes the export modal. // Saving the style closes the export modal.
await when.exportSaveStyle(); await when.modal.exportSaveStyle();
await then(get.elementByTestId("modal:export")).shouldNotExist(); await then(get.elementByTestId("modal:export")).shouldNotExist();
}); });
}); });
@@ -85,14 +85,14 @@ describe("modals", () => {
await when.setStyle("both"); await when.setStyle("both");
await when.click("nav:sources"); await when.click("nav:sources");
const before = Object.keys(get.fixture("geojson-raster-style.json").sources).length; const before = Object.keys(get.fixture("geojson-raster-style.json").sources).length;
await when.deleteFirstActiveSource(); await when.modal.deleteFirstActiveSource();
await then( await then(
get.styleFromLocalStorage().then((style) => Object.keys(style.sources).length) get.styleFromLocalStorage().then((style) => Object.keys(style.sources).length)
).shouldEqual(before - 1); ).shouldEqual(before - 1);
}); });
test("public source", async () => { test("public source", async () => {
await when.addPublicSource(); await when.modal.addPublicSource();
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: { sources: {
openmaptiles: { openmaptiles: {
@@ -148,6 +148,108 @@ describe("modals", () => {
sources: { [sourceId]: { tileSize: 128 } }, sources: { [sourceId]: { tileSize: 128 } },
}); });
}); });
test("add new geojson url source", async () => {
await when.modal.addSource("geojsonurl", "geojson_url");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
geojsonurl: { type: "geojson", data: "http://localhost:3000/geojson.json" },
},
});
});
test("add new geojson json source", async () => {
await when.modal.addSource("geojsonjson", "geojson_json");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
geojsonjson: { type: "geojson", cluster: false, data: "" },
},
});
});
test("add new tilejson vector source", async () => {
await when.modal.addSource("tilejsonvector", "tilejson_vector");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
tilejsonvector: { type: "vector", url: "http://localhost:3000/tilejson.json" },
},
});
});
test("add new tilejson raster source", async () => {
await when.modal.addSource("tilejsonraster", "tilejson_raster");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
tilejsonraster: { type: "raster", url: "http://localhost:3000/tilejson.json" },
},
});
});
test("add new tilejson raster-dem source", async () => {
await when.modal.addSource("tilejsonrasterdem", "tilejson_raster-dem");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
tilejsonrasterdem: { type: "raster-dem", url: "http://localhost:3000/tilejson.json" },
},
});
});
test("add new tile xyz raster-dem source", async () => {
await when.modal.addSource("tilexyzrasterdem", "tilexyz_raster-dem");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
tilexyzrasterdem: {
type: "raster-dem",
tiles: ["http://localhost:3000/{x}/{y}/{z}.png"],
minzoom: 0,
maxzoom: 14,
tileSize: 512,
},
},
});
});
test("add new image source", async () => {
await when.modal.addSource("imagesource", "image");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
imagesource: {
type: "image",
url: "http://localhost:3000/image.png",
coordinates: [[0, 0], [0, 0], [0, 0], [0, 0]],
},
},
});
});
test("add new video source", async () => {
await when.modal.addSource("videosource", "video");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
videosource: {
type: "video",
urls: ["http://localhost:3000/movie.mp4"],
coordinates: [[0, 0], [0, 0], [0, 0], [0, 0]],
},
},
});
});
test("edit the corner coordinates of an image source", async () => {
const sourceId = "imagecoords";
await when.setValue("modal:sources.add.source_id", sourceId);
await when.select("modal:sources.add.source_type", "image");
// The first corner is the first two number boxes of the coordinate arrays.
await when.modal.setCoordinateValue(0, "1");
await when.modal.setCoordinateValue(1, "2");
await when.click("modal:sources.add.add_source");
await when.wait(200);
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
sources: {
[sourceId]: { type: "image", coordinates: [[1, 2], [0, 0], [0, 0], [0, 0]] },
},
});
});
}); });
describe("inspect", () => { describe("inspect", () => {
@@ -262,6 +364,44 @@ describe("modals", () => {
}); });
}); });
test("map view defaults", async () => {
await when.setValue("modal:settings.zoom", "4");
await when.setValue("modal:settings.bearing", "12");
await when.setValue("modal:settings.pitch", "30");
await when.click("modal:settings.name");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
zoom: 4,
bearing: 12,
pitch: 30,
});
});
test("light intensity", async () => {
await when.setValue("modal:settings.light-intensity", "0.7");
await when.click("modal:settings.name");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
light: { intensity: 0.7 },
});
});
test("terrain source and exaggeration", async () => {
await when.setValue("modal:settings.maputnik:terrain_source", "terrain");
await when.setValue("modal:settings.terrain-exaggeration", "1.5");
await when.click("modal:settings.name");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
terrain: { source: "terrain", exaggeration: 1.5 },
});
});
test("transition delay and duration", async () => {
await when.setValue("modal:settings.transition-delay", "100");
await when.setValue("modal:settings.transition-duration", "500");
await when.click("modal:settings.name");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
transition: { delay: 100, duration: 500 },
});
});
test("style projection mercator", async () => { test("style projection mercator", async () => {
await when.select("modal:settings.projection", "mercator"); await when.select("modal:settings.projection", "mercator");
await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({ await then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
+2
View File
@@ -215,6 +215,7 @@ class FilterEditorInternal extends React.Component<FilterEditorInternalProps, Fi
onClick={this.makeExpression} onClick={this.makeExpression}
title={t("Convert to expression")} title={t("Convert to expression")}
className="maputnik-make-zoom-function" className="maputnik-make-zoom-function"
data-wd-key="filter-convert-to-expression"
> >
<TbMathFunction /> <TbMathFunction />
</InputButton> </InputButton>
@@ -248,6 +249,7 @@ class FilterEditorInternal extends React.Component<FilterEditorInternalProps, Fi
fieldSpec={fieldSpec} fieldSpec={fieldSpec}
label={t("Filter")} label={t("Filter")}
action={actions} action={actions}
data-wd-key="filter-combining-operator"
> >
<InputSelect <InputSelect
value={combiningOp} value={combiningOp}
+5
View File
@@ -289,6 +289,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
<Block <Block
label={t("Function")} label={t("Function")}
key="function" key="function"
data-wd-key="function-type"
> >
<div className="maputnik-data-spec-property-input"> <div className="maputnik-data-spec-property-input">
<InputSelect <InputSelect
@@ -303,6 +304,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
<Block <Block
label={t("Base")} label={t("Base")}
key="base" key="base"
data-wd-key="function-base"
> >
<div className="maputnik-data-spec-property-input"> <div className="maputnik-data-spec-property-input">
<InputSpec <InputSpec
@@ -317,6 +319,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
<Block <Block
label={"Property"} label={"Property"}
key="property" key="property"
data-wd-key="function-property"
> >
<div className="maputnik-data-spec-property-input"> <div className="maputnik-data-spec-property-input">
<InputString <InputString
@@ -330,6 +333,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
<Block <Block
label={t("Default")} label={t("Default")}
key="default" key="default"
data-wd-key="function-default"
> >
<InputSpec <InputSpec
fieldName={this.props.fieldName} fieldName={this.props.fieldName}
@@ -368,6 +372,7 @@ class DataPropertyInternal extends React.Component<DataPropertyInternalProps, Da
} }
<InputButton <InputButton
className="maputnik-add-stop" className="maputnik-add-stop"
data-wd-key="convert-to-expression"
onClick={this.props.onExpressionClick?.bind(this)} onClick={this.props.onExpressionClick?.bind(this)}
> >
<TbMathFunction style={{ verticalAlign: "text-bottom" }} /> <TbMathFunction style={{ verticalAlign: "text-bottom" }} />
+2
View File
@@ -50,6 +50,7 @@ class ExpressionPropertyInternal extends React.Component<ExpressionPropertyInter
onClick={this.props.onUndo} onClick={this.props.onUndo}
disabled={undoDisabled} disabled={undoDisabled}
className="maputnik-delete-stop" className="maputnik-delete-stop"
data-wd-key="undo-expression"
title={t("Revert from expression")} title={t("Revert from expression")}
> >
<MdUndo /> <MdUndo />
@@ -59,6 +60,7 @@ class ExpressionPropertyInternal extends React.Component<ExpressionPropertyInter
key="delete_action" key="delete_action"
onClick={this.props.onDelete} onClick={this.props.onDelete}
className="maputnik-delete-stop" className="maputnik-delete-stop"
data-wd-key="delete-expression"
title={t("Delete expression")} title={t("Delete expression")}
> >
<MdDelete /> <MdDelete />
+3
View File
@@ -194,6 +194,7 @@ class ZoomPropertyInternal extends React.Component<ZoomPropertyInternalProps, Zo
<div className="maputnik-data-fieldset-inner"> <div className="maputnik-data-fieldset-inner">
<Block <Block
label={t("Function")} label={t("Function")}
data-wd-key="function-type"
> >
<div className="maputnik-data-spec-property-input"> <div className="maputnik-data-spec-property-input">
<InputSelect <InputSelect
@@ -206,6 +207,7 @@ class ZoomPropertyInternal extends React.Component<ZoomPropertyInternalProps, Zo
</Block> </Block>
<Block <Block
label={t("Base")} label={t("Base")}
data-wd-key="function-base"
> >
<div className="maputnik-data-spec-property-input"> <div className="maputnik-data-spec-property-input">
<InputSpec <InputSpec
@@ -240,6 +242,7 @@ class ZoomPropertyInternal extends React.Component<ZoomPropertyInternalProps, Zo
</InputButton> </InputButton>
<InputButton <InputButton
className="maputnik-add-stop" className="maputnik-add-stop"
data-wd-key="convert-to-expression"
onClick={this.props.onExpressionClick?.bind(this)} onClick={this.props.onExpressionClick?.bind(this)}
> >
<TbMathFunction style={{ verticalAlign: "text-bottom" }} /> <TbMathFunction style={{ verticalAlign: "text-bottom" }} />
+7
View File
@@ -206,6 +206,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
<FieldNumber <FieldNumber
label={t("Zoom")} label={t("Zoom")}
data-wd-key="modal:settings.zoom"
fieldSpec={latest.$root.zoom} fieldSpec={latest.$root.zoom}
value={mapStyle.zoom} value={mapStyle.zoom}
default={0} default={0}
@@ -214,6 +215,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
<FieldNumber <FieldNumber
label={t("Bearing")} label={t("Bearing")}
data-wd-key="modal:settings.bearing"
fieldSpec={latest.$root.bearing} fieldSpec={latest.$root.bearing}
value={mapStyle.bearing} value={mapStyle.bearing}
default={latest.$root.bearing.default} default={latest.$root.bearing.default}
@@ -222,6 +224,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
<FieldNumber <FieldNumber
label={t("Pitch")} label={t("Pitch")}
data-wd-key="modal:settings.pitch"
fieldSpec={latest.$root.pitch} fieldSpec={latest.$root.pitch}
value={mapStyle.pitch} value={mapStyle.pitch}
default={latest.$root.pitch.default} default={latest.$root.pitch.default}
@@ -248,6 +251,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
<FieldNumber <FieldNumber
label={t("Light intensity")} label={t("Light intensity")}
data-wd-key="modal:settings.light-intensity"
fieldSpec={latest.light.intensity} fieldSpec={latest.light.intensity}
value={light.intensity as number} value={light.intensity as number}
default={latest.light.intensity.default} default={latest.light.intensity.default}
@@ -274,6 +278,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
<FieldNumber <FieldNumber
label={t("Terrain exaggeration")} label={t("Terrain exaggeration")}
data-wd-key="modal:settings.terrain-exaggeration"
fieldSpec={latest.terrain.exaggeration} fieldSpec={latest.terrain.exaggeration}
value={terrain.exaggeration} value={terrain.exaggeration}
default={latest.terrain.exaggeration.default} default={latest.terrain.exaggeration.default}
@@ -282,6 +287,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
<FieldNumber <FieldNumber
label={t("Transition delay")} label={t("Transition delay")}
data-wd-key="modal:settings.transition-delay"
fieldSpec={latest.transition.delay} fieldSpec={latest.transition.delay}
value={transition.delay} value={transition.delay}
default={latest.transition.delay.default} default={latest.transition.delay.default}
@@ -290,6 +296,7 @@ class ModalSettingsInternal extends React.Component<ModalSettingsInternalProps>
<FieldNumber <FieldNumber
label={t("Transition duration")} label={t("Transition duration")}
data-wd-key="modal:settings.transition-duration"
fieldSpec={latest.transition.duration} fieldSpec={latest.transition.duration}
value={transition.duration} value={transition.duration}
default={latest.transition.duration.default} default={latest.transition.duration.default}