mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Maps] auto-fit to data bounds (#72129)
* [Maps] auto-fit to data bounds * update jest snapshot * add buffer to fit to bounds * sync join layers prior to fitting to bounds * clean-up comment * better names * fix tslint errors * update functional test expect * add functional tests * clean-up * change test run location * fix test expect Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
b9413cf3c8
commit
9947c671ec
17 changed files with 249 additions and 22 deletions
|
@ -37,8 +37,12 @@ import {
|
|||
UPDATE_SOURCE_DATA_REQUEST,
|
||||
} from './map_action_constants';
|
||||
import { ILayer } from '../classes/layers/layer';
|
||||
import { IVectorLayer } from '../classes/layers/vector_layer/vector_layer';
|
||||
import { DataMeta, MapExtent, MapFilters } from '../../common/descriptor_types';
|
||||
import { DataRequestAbortError } from '../classes/util/data_request';
|
||||
import { scaleBounds } from '../elasticsearch_geo_utils';
|
||||
|
||||
const FIT_TO_BOUNDS_SCALE_FACTOR = 0.1;
|
||||
|
||||
export type DataRequestContext = {
|
||||
startLoading(dataId: string, requestToken: symbol, meta: DataMeta): void;
|
||||
|
@ -122,13 +126,26 @@ function getDataRequestContext(
|
|||
|
||||
export function syncDataForAllLayers() {
|
||||
return async (dispatch: Dispatch, getState: () => MapStoreState) => {
|
||||
const syncPromises = getLayerList(getState()).map(async (layer) => {
|
||||
const syncPromises = getLayerList(getState()).map((layer) => {
|
||||
return dispatch<any>(syncDataForLayer(layer));
|
||||
});
|
||||
await Promise.all(syncPromises);
|
||||
};
|
||||
}
|
||||
|
||||
export function syncDataForAllJoinLayers() {
|
||||
return async (dispatch: Dispatch, getState: () => MapStoreState) => {
|
||||
const syncPromises = getLayerList(getState())
|
||||
.filter((layer) => {
|
||||
return 'hasJoins' in layer ? (layer as IVectorLayer).hasJoins() : false;
|
||||
})
|
||||
.map((layer) => {
|
||||
return dispatch<any>(syncDataForLayer(layer));
|
||||
});
|
||||
await Promise.all(syncPromises);
|
||||
};
|
||||
}
|
||||
|
||||
export function syncDataForLayer(layer: ILayer) {
|
||||
return async (dispatch: Dispatch, getState: () => MapStoreState) => {
|
||||
const dataRequestContext = getDataRequestContext(dispatch, getState, layer.getId());
|
||||
|
@ -284,7 +301,7 @@ export function fitToLayerExtent(layerId: string) {
|
|||
getDataRequestContext(dispatch, getState, layerId)
|
||||
);
|
||||
if (bounds) {
|
||||
await dispatch(setGotoWithBounds(bounds));
|
||||
await dispatch(setGotoWithBounds(scaleBounds(bounds, FIT_TO_BOUNDS_SCALE_FACTOR)));
|
||||
}
|
||||
} catch (error) {
|
||||
if (!(error instanceof DataRequestAbortError)) {
|
||||
|
@ -359,7 +376,7 @@ export function fitToDataBounds() {
|
|||
maxLat: turfUnionBbox[3],
|
||||
};
|
||||
|
||||
dispatch(setGotoWithBounds(dataBounds));
|
||||
dispatch(setGotoWithBounds(scaleBounds(dataBounds, FIT_TO_BOUNDS_SCALE_FACTOR)));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,13 @@
|
|||
import { Dispatch } from 'redux';
|
||||
// @ts-ignore
|
||||
import turf from 'turf';
|
||||
import uuid from 'uuid/v4';
|
||||
import turfBooleanContains from '@turf/boolean-contains';
|
||||
import { Filter, Query, TimeRange } from 'src/plugins/data/public';
|
||||
import { MapStoreState } from '../reducers/store';
|
||||
import {
|
||||
getDataFilters,
|
||||
getMapSettings,
|
||||
getWaitingForMapReadyLayerListRaw,
|
||||
getQuery,
|
||||
} from '../selectors/map_selectors';
|
||||
|
@ -42,7 +44,11 @@ import {
|
|||
UPDATE_DRAW_STATE,
|
||||
UPDATE_MAP_SETTING,
|
||||
} from './map_action_constants';
|
||||
import { syncDataForAllLayers } from './data_request_actions';
|
||||
import {
|
||||
fitToDataBounds,
|
||||
syncDataForAllJoinLayers,
|
||||
syncDataForAllLayers,
|
||||
} from './data_request_actions';
|
||||
import { addLayer } from './layer_actions';
|
||||
import { MapSettings } from '../reducers/map';
|
||||
import {
|
||||
|
@ -51,6 +57,7 @@ import {
|
|||
MapExtent,
|
||||
MapRefreshConfig,
|
||||
} from '../../common/descriptor_types';
|
||||
import { scaleBounds } from '../elasticsearch_geo_utils';
|
||||
|
||||
export function setMapInitError(errorMessage: string) {
|
||||
return {
|
||||
|
@ -134,15 +141,7 @@ export function mapExtentChanged(newMapConstants: { zoom: number; extent: MapExt
|
|||
}
|
||||
|
||||
if (!doesBufferContainExtent || currentZoom !== newZoom) {
|
||||
const scaleFactor = 0.5; // TODO put scale factor in store and fetch with selector
|
||||
const width = extent.maxLon - extent.minLon;
|
||||
const height = extent.maxLat - extent.minLat;
|
||||
dataFilters.buffer = {
|
||||
minLon: extent.minLon - width * scaleFactor,
|
||||
minLat: extent.minLat - height * scaleFactor,
|
||||
maxLon: extent.maxLon + width * scaleFactor,
|
||||
maxLat: extent.maxLat + height * scaleFactor,
|
||||
};
|
||||
dataFilters.buffer = scaleBounds(extent, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,6 +196,7 @@ function generateQueryTimestamp() {
|
|||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
let lastSetQueryCallId: string = '';
|
||||
export function setQuery({
|
||||
query,
|
||||
timeFilters,
|
||||
|
@ -226,7 +226,22 @@ export function setQuery({
|
|||
filters,
|
||||
});
|
||||
|
||||
await dispatch<any>(syncDataForAllLayers());
|
||||
if (getMapSettings(getState()).autoFitToDataBounds) {
|
||||
// Joins are performed on the client.
|
||||
// As a result, bounds for join layers must also be performed on the client.
|
||||
// Therefore join layers need to fetch data prior to auto fitting bounds.
|
||||
const localSetQueryCallId = uuid();
|
||||
lastSetQueryCallId = localSetQueryCallId;
|
||||
await dispatch<any>(syncDataForAllJoinLayers());
|
||||
|
||||
// setQuery can be triggered before async data fetching completes
|
||||
// Only continue execution path if setQuery has not been re-triggered.
|
||||
if (localSetQueryCallId === lastSetQueryCallId) {
|
||||
dispatch<any>(fitToDataBounds());
|
||||
}
|
||||
} else {
|
||||
await dispatch<any>(syncDataForAllLayers());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -237,6 +237,10 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
|
|||
return [];
|
||||
}
|
||||
|
||||
hasJoins() {
|
||||
return false;
|
||||
}
|
||||
|
||||
getSource() {
|
||||
return this._isClustered ? this._clusterSource : this._documentSource;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ export interface IVectorLayer extends ILayer {
|
|||
getStyle(): IVectorStyle;
|
||||
getFeatureById(id: string | number): Feature | null;
|
||||
getPropertiesForTooltip(properties: GeoJsonProperties): Promise<ITooltipProperty[]>;
|
||||
hasJoins(): boolean;
|
||||
}
|
||||
|
||||
export class VectorLayer extends AbstractLayer implements IVectorLayer {
|
||||
|
@ -81,4 +82,5 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
|
|||
getStyle(): IVectorStyle;
|
||||
getFeatureById(id: string | number): Feature | null;
|
||||
getPropertiesForTooltip(properties: GeoJsonProperties): Promise<ITooltipProperty[]>;
|
||||
hasJoins(): boolean;
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ export class VectorLayer extends AbstractLayer {
|
|||
});
|
||||
}
|
||||
|
||||
_hasJoins() {
|
||||
hasJoins() {
|
||||
return this.getValidJoins().length > 0;
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ export class VectorLayer extends AbstractLayer {
|
|||
async getBounds({ startLoading, stopLoading, registerCancelCallback, dataFilters }) {
|
||||
const isStaticLayer = !this.getSource().isBoundsAware();
|
||||
if (isStaticLayer) {
|
||||
return getFeatureCollectionBounds(this._getSourceFeatureCollection(), this._hasJoins());
|
||||
return getFeatureCollectionBounds(this._getSourceFeatureCollection(), this.hasJoins());
|
||||
}
|
||||
|
||||
const requestToken = Symbol(`${SOURCE_BOUNDS_DATA_REQUEST_ID}-${this.getId()}`);
|
||||
|
@ -193,6 +193,11 @@ export class VectorLayer extends AbstractLayer {
|
|||
return bounds;
|
||||
}
|
||||
|
||||
isLoadingBounds() {
|
||||
const boundsDataRequest = this.getDataRequest(SOURCE_BOUNDS_DATA_REQUEST_ID);
|
||||
return !!boundsDataRequest && boundsDataRequest.isLoading();
|
||||
}
|
||||
|
||||
async getLeftJoinFields() {
|
||||
return await this.getSource().getLeftJoinFields();
|
||||
}
|
||||
|
@ -583,7 +588,7 @@ export class VectorLayer extends AbstractLayer {
|
|||
}
|
||||
|
||||
async syncData(syncContext) {
|
||||
this._syncData(syncContext, this.getSource(), this.getCurrentStyle());
|
||||
await this._syncData(syncContext, this.getSource(), this.getCurrentStyle());
|
||||
}
|
||||
|
||||
// TLDR: Do not call getSource or getCurrentStyle in syncData flow. Use 'source' and 'style' arguments instead.
|
||||
|
@ -597,13 +602,16 @@ export class VectorLayer extends AbstractLayer {
|
|||
// Given 2 above, which source/style to use can not be pulled from data request state.
|
||||
// Therefore, source and style are provided as arugments and must be used instead of calling getSource or getCurrentStyle.
|
||||
async _syncData(syncContext, source, style) {
|
||||
if (this.isLoadingBounds()) {
|
||||
return;
|
||||
}
|
||||
await this._syncSourceStyleMeta(syncContext, source, style);
|
||||
await this._syncSourceFormatters(syncContext, source, style);
|
||||
const sourceResult = await this._syncSource(syncContext, source, style);
|
||||
if (
|
||||
!sourceResult.featureCollection ||
|
||||
!sourceResult.featureCollection.features.length ||
|
||||
!this._hasJoins()
|
||||
!this.hasJoins()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@ -711,7 +719,7 @@ export class VectorLayer extends AbstractLayer {
|
|||
mbMap.addLayer(mbLayer);
|
||||
}
|
||||
|
||||
const filterExpr = getPointFilterExpression(this._hasJoins());
|
||||
const filterExpr = getPointFilterExpression(this.hasJoins());
|
||||
if (filterExpr !== mbMap.getFilter(pointLayerId)) {
|
||||
mbMap.setFilter(pointLayerId, filterExpr);
|
||||
mbMap.setFilter(textLayerId, filterExpr);
|
||||
|
@ -747,7 +755,7 @@ export class VectorLayer extends AbstractLayer {
|
|||
mbMap.addLayer(mbLayer);
|
||||
}
|
||||
|
||||
const filterExpr = getPointFilterExpression(this._hasJoins());
|
||||
const filterExpr = getPointFilterExpression(this.hasJoins());
|
||||
if (filterExpr !== mbMap.getFilter(symbolLayerId)) {
|
||||
mbMap.setFilter(symbolLayerId, filterExpr);
|
||||
}
|
||||
|
@ -769,7 +777,7 @@ export class VectorLayer extends AbstractLayer {
|
|||
const sourceId = this.getId();
|
||||
const fillLayerId = this._getMbPolygonLayerId();
|
||||
const lineLayerId = this._getMbLineLayerId();
|
||||
const hasJoins = this._hasJoins();
|
||||
const hasJoins = this.hasJoins();
|
||||
if (!mbMap.getLayer(fillLayerId)) {
|
||||
const mbLayer = {
|
||||
id: fillLayerId,
|
||||
|
|
|
@ -16,6 +16,25 @@ exports[`should render 1`] = `
|
|||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
display="row"
|
||||
fullWidth={false}
|
||||
hasChildLabel={true}
|
||||
hasEmptyLabelSpace={false}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={false}
|
||||
compressed={true}
|
||||
data-test-subj="autoFitToDataBoundsSwitch"
|
||||
label="Auto fit map to data bounds"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<ValidatedDualRange
|
||||
allowEmptyRange={false}
|
||||
compressed={true}
|
||||
|
@ -35,6 +54,9 @@ exports[`should render 1`] = `
|
|||
]
|
||||
}
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
display="row"
|
||||
|
@ -84,6 +106,25 @@ exports[`should render browser location form when initialLocation is BROWSER_LOC
|
|||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
display="row"
|
||||
fullWidth={false}
|
||||
hasChildLabel={true}
|
||||
hasEmptyLabelSpace={false}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={false}
|
||||
compressed={true}
|
||||
data-test-subj="autoFitToDataBoundsSwitch"
|
||||
label="Auto fit map to data bounds"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<ValidatedDualRange
|
||||
allowEmptyRange={false}
|
||||
compressed={true}
|
||||
|
@ -103,6 +144,9 @@ exports[`should render browser location form when initialLocation is BROWSER_LOC
|
|||
]
|
||||
}
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
display="row"
|
||||
|
@ -172,6 +216,25 @@ exports[`should render fixed location form when initialLocation is FIXED_LOCATIO
|
|||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
display="row"
|
||||
fullWidth={false}
|
||||
hasChildLabel={true}
|
||||
hasEmptyLabelSpace={false}
|
||||
labelType="label"
|
||||
>
|
||||
<EuiSwitch
|
||||
checked={false}
|
||||
compressed={true}
|
||||
data-test-subj="autoFitToDataBoundsSwitch"
|
||||
label="Auto fit map to data bounds"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<ValidatedDualRange
|
||||
allowEmptyRange={false}
|
||||
compressed={true}
|
||||
|
@ -191,6 +254,9 @@ exports[`should render fixed location form when initialLocation is FIXED_LOCATIO
|
|||
]
|
||||
}
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
display="row"
|
||||
|
|
|
@ -96,6 +96,7 @@ export function MapSettingsPanel({
|
|||
iconType="check"
|
||||
onClick={keepChanges}
|
||||
fill
|
||||
data-test-subj="mapSettingSubmitButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.maps.mapSettingsPanel.keepChangesButtonLabel"
|
||||
|
|
|
@ -14,6 +14,8 @@ import {
|
|||
EuiPanel,
|
||||
EuiRadioGroup,
|
||||
EuiSpacer,
|
||||
EuiSwitch,
|
||||
EuiSwitchEvent,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -54,6 +56,10 @@ const initialLocationOptions = [
|
|||
];
|
||||
|
||||
export function NavigationPanel({ center, settings, updateMapSetting, zoom }: Props) {
|
||||
const onAutoFitToDataBoundsChange = (event: EuiSwitchEvent) => {
|
||||
updateMapSetting('autoFitToDataBounds', event.target.checked);
|
||||
};
|
||||
|
||||
const onZoomChange = (value: Value) => {
|
||||
const minZoom = Math.max(MIN_ZOOM, parseInt(value[0] as string, 10));
|
||||
const maxZoom = Math.min(MAX_ZOOM, parseInt(value[1] as string, 10));
|
||||
|
@ -207,6 +213,19 @@ export function NavigationPanel({ center, settings, updateMapSetting, zoom }: Pr
|
|||
</h5>
|
||||
</EuiTitle>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFormRow>
|
||||
<EuiSwitch
|
||||
label={i18n.translate('xpack.maps.mapSettingsPanel.autoFitToDataBoundsLabel', {
|
||||
defaultMessage: 'Auto fit map to data bounds',
|
||||
})}
|
||||
checked={settings.autoFitToDataBounds}
|
||||
onChange={onAutoFitToDataBoundsChange}
|
||||
compressed
|
||||
data-test-subj="autoFitToDataBoundsSwitch"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
<ValidatedDualRange
|
||||
label={i18n.translate('xpack.maps.mapSettingsPanel.zoomRangeLabel', {
|
||||
|
@ -224,6 +243,7 @@ export function NavigationPanel({ center, settings, updateMapSetting, zoom }: Pr
|
|||
compressed
|
||||
/>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.maps.source.mapSettingsPanel.initialLocationLabel', {
|
||||
defaultMessage: 'Initial map location',
|
||||
|
|
9
x-pack/plugins/maps/public/elasticsearch_geo_utils.d.ts
vendored
Normal file
9
x-pack/plugins/maps/public/elasticsearch_geo_utils.d.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { MapExtent } from '../common/descriptor_types';
|
||||
|
||||
export function scaleBounds(bounds: MapExtent, scaleFactor: number): MapExtent;
|
|
@ -469,3 +469,14 @@ export function extractFeaturesFromFilters(filters) {
|
|||
|
||||
return features;
|
||||
}
|
||||
|
||||
export function scaleBounds(bounds, scaleFactor) {
|
||||
const width = bounds.maxLon - bounds.minLon;
|
||||
const height = bounds.maxLat - bounds.minLat;
|
||||
return {
|
||||
minLon: bounds.minLon - width * scaleFactor,
|
||||
minLat: bounds.minLat - height * scaleFactor,
|
||||
maxLon: bounds.maxLon + width * scaleFactor,
|
||||
maxLat: bounds.maxLat + height * scaleFactor,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
roundCoordinates,
|
||||
extractFeaturesFromFilters,
|
||||
makeESBbox,
|
||||
scaleBounds,
|
||||
} from './elasticsearch_geo_utils';
|
||||
import { indexPatterns } from '../../../../src/plugins/data/public';
|
||||
|
||||
|
@ -687,3 +688,20 @@ describe('makeESBbox', () => {
|
|||
expect(bbox).toEqual({ bottom_right: [-170, -89], top_left: [-175, 89] });
|
||||
});
|
||||
});
|
||||
|
||||
describe('scaleBounds', () => {
|
||||
it('Should scale bounds', () => {
|
||||
const bounds = {
|
||||
maxLat: 10,
|
||||
maxLon: 100,
|
||||
minLat: 5,
|
||||
minLon: 95,
|
||||
};
|
||||
expect(scaleBounds(bounds, 0.5)).toEqual({
|
||||
maxLat: 12.5,
|
||||
maxLon: 102.5,
|
||||
minLat: 2.5,
|
||||
minLon: 92.5,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import { MapSettings } from './map';
|
|||
|
||||
export function getDefaultMapSettings(): MapSettings {
|
||||
return {
|
||||
autoFitToDataBounds: false,
|
||||
initialLocation: INITIAL_LOCATION.LAST_SAVED_LOCATION,
|
||||
fixedLocation: { lat: 0, lon: 0, zoom: 2 },
|
||||
browserLocation: { zoom: 2 },
|
||||
|
|
1
x-pack/plugins/maps/public/reducers/map.d.ts
vendored
1
x-pack/plugins/maps/public/reducers/map.d.ts
vendored
|
@ -42,6 +42,7 @@ export type MapContext = {
|
|||
};
|
||||
|
||||
export type MapSettings = {
|
||||
autoFitToDataBounds: boolean;
|
||||
initialLocation: INITIAL_LOCATION;
|
||||
fixedLocation: {
|
||||
lat: number;
|
||||
|
|
35
x-pack/test/functional/apps/maps/auto_fit_to_bounds.js
Normal file
35
x-pack/test/functional/apps/maps/auto_fit_to_bounds.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
export default function ({ getPageObjects }) {
|
||||
const PageObjects = getPageObjects(['maps']);
|
||||
|
||||
describe('auto fit map to bounds', () => {
|
||||
describe('without joins', () => {
|
||||
before(async () => {
|
||||
await PageObjects.maps.loadSavedMap('document example');
|
||||
await PageObjects.maps.enableAutoFitToBounds();
|
||||
});
|
||||
|
||||
it('should automatically fit to bounds when query is applied', async () => {
|
||||
// Set view to other side of world so no matching results
|
||||
await PageObjects.maps.setView(-15, -100, 6);
|
||||
|
||||
// Setting query should trigger fit to bounds and move map
|
||||
const origView = await PageObjects.maps.getView();
|
||||
await PageObjects.maps.setAndSubmitQuery('machine.os.raw : "ios"');
|
||||
await PageObjects.maps.waitForMapPanAndZoom(origView);
|
||||
|
||||
const { lat, lon, zoom } = await PageObjects.maps.getView();
|
||||
expect(Math.round(lat)).to.equal(43);
|
||||
expect(Math.round(lon)).to.equal(-102);
|
||||
expect(Math.round(zoom)).to.equal(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -103,7 +103,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
await PageObjects.maps.setView(-15, -100, 6);
|
||||
await PageObjects.maps.clickFitToBounds('logstash');
|
||||
const { lat, lon, zoom } = await PageObjects.maps.getView();
|
||||
expect(Math.round(lat)).to.equal(42);
|
||||
expect(Math.round(lat)).to.equal(43);
|
||||
expect(Math.round(lon)).to.equal(-102);
|
||||
expect(Math.round(zoom)).to.equal(5);
|
||||
});
|
||||
|
|
|
@ -34,6 +34,7 @@ export default function ({ loadTestFile, getService }) {
|
|||
loadTestFile(require.resolve('./vector_styling'));
|
||||
loadTestFile(require.resolve('./saved_object_management'));
|
||||
loadTestFile(require.resolve('./sample_data'));
|
||||
loadTestFile(require.resolve('./auto_fit_to_bounds'));
|
||||
loadTestFile(require.resolve('./feature_controls/maps_security'));
|
||||
loadTestFile(require.resolve('./feature_controls/maps_spaces'));
|
||||
loadTestFile(require.resolve('./full_screen_mode'));
|
||||
|
|
|
@ -656,6 +656,24 @@ export function GisPageProvider({ getService, getPageObjects }) {
|
|||
async getCategorySuggestions() {
|
||||
return await comboBox.getOptionsList(`colorStopInput1`);
|
||||
}
|
||||
|
||||
async enableAutoFitToBounds() {
|
||||
await testSubjects.click('openSettingsButton');
|
||||
const isEnabled = await testSubjects.getAttribute('autoFitToDataBoundsSwitch', 'checked');
|
||||
if (!isEnabled) {
|
||||
await retry.try(async () => {
|
||||
await testSubjects.click('autoFitToDataBoundsSwitch');
|
||||
const ensureEnabled = await testSubjects.getAttribute(
|
||||
'autoFitToDataBoundsSwitch',
|
||||
'checked'
|
||||
);
|
||||
if (!ensureEnabled) {
|
||||
throw new Error('autoFitToDataBoundsSwitch is not enabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
await testSubjects.click('mapSettingSubmitButton');
|
||||
}
|
||||
}
|
||||
return new GisPage();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue