mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Enh/zoom level message (#26267)
Adds zoom messaging guidance for both Region & Coordinate maps upon reaching zoom level 10. Previous behavior: user reaches zoom level 10 and zoom is disabled. Current behavior: user reaches zoom level 10, zoom is still disabled however the user is also presented a pop-up (toast) with links for guidance on using EMS or their own custom map services * Add logic to support showing zoom message for ems tms at zoom level 10 * Add user notification/warning on reaching zoom level 10 * Determine whether or not to show zoom message in service settings and pass through * Add/remove oss zoom behavior as layer is added/removed to leaflet * Allow user to suppress zoom warning for this session * Functional tests for zoom warning * A few requested changes to the toast format, wording and method of suppression (now a link) * Update functional zoom tests extensively accounting for toast timing and strange zoom in/out behavior * Revise zoom msg bool logic to use origin leveraged in service settings * Update zoom message for i18n compliance * Open zoom warning links in new tab * Update i18n defaultMessage to use single-quote string * Set warning toggle flush left * String instead of curly brace string? * Update messaging to include more guidance around custom map services. Move zoom messaging over to separate file
This commit is contained in:
parent
a3793ecac5
commit
d769722d30
7 changed files with 251 additions and 39 deletions
|
@ -25,8 +25,8 @@ import 'ui/vis/map/service_settings';
|
|||
import { toastNotifications } from 'ui/notify';
|
||||
import { uiModules } from 'ui/modules';
|
||||
|
||||
const MINZOOM = 0;
|
||||
const MAXZOOM = 22;//increase this to 22. Better for WMS
|
||||
const WMS_MINZOOM = 0;
|
||||
const WMS_MAXZOOM = 22;//increase this to 22. Better for WMS
|
||||
|
||||
const emsServiceSettings = new Promise((resolve) => {
|
||||
uiModules.get('kibana').run(($injector) => {
|
||||
|
@ -110,8 +110,8 @@ export function BaseMapsVisualizationProvider(serviceSettings, i18n) {
|
|||
options.center = centerFromUIState ? centerFromUIState : this.vis.params.mapCenter;
|
||||
|
||||
this._kibanaMap = new KibanaMap(this._container, options);
|
||||
this._kibanaMap.setMinZoom(MINZOOM);//use a default
|
||||
this._kibanaMap.setMaxZoom(MAXZOOM);//use a default
|
||||
this._kibanaMap.setMinZoom(WMS_MINZOOM);//use a default
|
||||
this._kibanaMap.setMaxZoom(WMS_MAXZOOM);//use a default
|
||||
|
||||
this._kibanaMap.addLegendControl();
|
||||
this._kibanaMap.addFitControl();
|
||||
|
@ -127,9 +127,18 @@ export function BaseMapsVisualizationProvider(serviceSettings, i18n) {
|
|||
}
|
||||
|
||||
|
||||
_baseLayerConfigured() {
|
||||
const mapParams = this._getMapsParams();
|
||||
return mapParams.wms.selectedTmsLayer;
|
||||
_tmsConfigured() {
|
||||
const { wms } = this._getMapsParams();
|
||||
const hasTmsBaseLayer = !!wms.selectedTmsLayer;
|
||||
|
||||
return hasTmsBaseLayer;
|
||||
}
|
||||
|
||||
_wmsConfigured() {
|
||||
const { wms } = this._getMapsParams();
|
||||
const hasWmsBaseLayer = !!wms.enabled;
|
||||
|
||||
return hasWmsBaseLayer;
|
||||
}
|
||||
|
||||
async _updateBaseLayer() {
|
||||
|
@ -139,7 +148,7 @@ export function BaseMapsVisualizationProvider(serviceSettings, i18n) {
|
|||
}
|
||||
|
||||
const mapParams = this._getMapsParams();
|
||||
if (!this._baseLayerConfigured()) {
|
||||
if (!this._tmsConfigured()) {
|
||||
try {
|
||||
const tmsServices = await serviceSettings.getTMSServices();
|
||||
const firstRoadMapLayer = tmsServices.find((s) => {
|
||||
|
@ -157,33 +166,28 @@ export function BaseMapsVisualizationProvider(serviceSettings, i18n) {
|
|||
}
|
||||
|
||||
try {
|
||||
|
||||
if (mapParams.wms.enabled) {
|
||||
|
||||
if (MINZOOM > this._kibanaMap.getMaxZoomLevel()) {
|
||||
this._kibanaMap.setMinZoom(MINZOOM);
|
||||
this._kibanaMap.setMaxZoom(MAXZOOM);
|
||||
if (this._wmsConfigured()) {
|
||||
if (WMS_MINZOOM > this._kibanaMap.getMaxZoomLevel()) {
|
||||
this._kibanaMap.setMinZoom(WMS_MINZOOM);
|
||||
this._kibanaMap.setMaxZoom(WMS_MAXZOOM);
|
||||
}
|
||||
|
||||
this._kibanaMap.setBaseLayer({
|
||||
baseLayerType: 'wms',
|
||||
options: {
|
||||
minZoom: MINZOOM,
|
||||
maxZoom: MAXZOOM,
|
||||
minZoom: WMS_MINZOOM,
|
||||
maxZoom: WMS_MAXZOOM,
|
||||
url: mapParams.wms.url,
|
||||
...mapParams.wms.options
|
||||
}
|
||||
});
|
||||
} else if (mapParams.wms.selectedTmsLayer) {
|
||||
} else if (this._tmsConfigured()) {
|
||||
const selectedTmsLayer = mapParams.wms.selectedTmsLayer;
|
||||
this._setTmsLayer(selectedTmsLayer);
|
||||
|
||||
}
|
||||
} catch (tmsLoadingError) {
|
||||
toastNotifications.addWarning(tmsLoadingError.message);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
async _setTmsLayer(tmsLayer) {
|
||||
|
@ -192,14 +196,14 @@ export function BaseMapsVisualizationProvider(serviceSettings, i18n) {
|
|||
if (this._kibanaMap.getZoomLevel() > tmsLayer.maxZoom) {
|
||||
this._kibanaMap.setZoomLevel(tmsLayer.maxZoom);
|
||||
}
|
||||
// const url = tmsLayer.url;
|
||||
const url = await (await emsServiceSettings).getUrlTemplateForTMSLayer(tmsLayer);
|
||||
const showZoomMessage = serviceSettings.shouldShowZoomMessage(tmsLayer);
|
||||
const options = _.cloneDeep(tmsLayer);
|
||||
delete options.id;
|
||||
delete options.url;
|
||||
this._kibanaMap.setBaseLayer({
|
||||
baseLayerType: 'tms',
|
||||
options: { url, ...options }
|
||||
options: { url, showZoomMessage, ...options }
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -235,7 +239,7 @@ export function BaseMapsVisualizationProvider(serviceSettings, i18n) {
|
|||
|
||||
_whenBaseLayerIsLoaded() {
|
||||
|
||||
if (!this._baseLayerConfigured()) {
|
||||
if (!this._tmsConfigured()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { createZoomWarningMsg } from './map_messages';
|
||||
import L from 'leaflet';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
|
@ -345,11 +346,11 @@ export class KibanaMap extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
getZoomLevel() {
|
||||
getZoomLevel = () => {
|
||||
return this._leafletMap.getZoom();
|
||||
}
|
||||
|
||||
getMaxZoomLevel() {
|
||||
getMaxZoomLevel = () => {
|
||||
return this._leafletMap.getMaxZoom();
|
||||
}
|
||||
|
||||
|
@ -483,6 +484,18 @@ export class KibanaMap extends EventEmitter {
|
|||
this._updateLegend();
|
||||
}
|
||||
|
||||
_addMaxZoomMessage = layer => {
|
||||
const zoomWarningMsg = createZoomWarningMsg(this.getZoomLevel, this.getMaxZoomLevel);
|
||||
|
||||
this._leafletMap.on('zoomend', zoomWarningMsg);
|
||||
this._containerNode.setAttribute('data-test-subj', 'zoomWarningEnabled');
|
||||
|
||||
layer.on('remove', () => {
|
||||
this._leafletMap.off('zoomend', zoomWarningMsg);
|
||||
this._containerNode.removeAttribute('data-test-subj');
|
||||
});
|
||||
}
|
||||
|
||||
setLegendPosition(position) {
|
||||
if (this._legendPosition === position) {
|
||||
if (!this._leafletLegendControl) {
|
||||
|
@ -562,6 +575,11 @@ export class KibanaMap extends EventEmitter {
|
|||
});
|
||||
|
||||
this._leafletBaseLayer = baseLayer;
|
||||
if (settings.options.showZoomMessage) {
|
||||
baseLayer.on('add', () => {
|
||||
this._addMaxZoomMessage(baseLayer);
|
||||
});
|
||||
}
|
||||
this._leafletBaseLayer.addTo(this._leafletMap);
|
||||
this._leafletBaseLayer.bringToBack();
|
||||
if (settings.options.minZoom > this._leafletMap.getZoom()) {
|
||||
|
|
123
src/ui/public/vis/map/map_messages.js
Normal file
123
src/ui/public/vis/map/map_messages.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
EuiSpacer,
|
||||
EuiButtonEmpty
|
||||
} from '@elastic/eui';
|
||||
|
||||
export const createZoomWarningMsg = (function () {
|
||||
let disableZoomMsg = false;
|
||||
const setZoomMsg = boolDisableMsg => disableZoomMsg = boolDisableMsg;
|
||||
|
||||
class ZoomWarning extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
disabled: false
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="common.ui.vis.kibanaMap.zoomWarning"
|
||||
defaultMessage="You've reached the maximum number of zoom
|
||||
levels. To zoom all the way in, upgrade to the
|
||||
{defaultDistribution} of Elasticsearch and Kibana. You'll get
|
||||
access to additional zoom levels for free through the {ems}.
|
||||
Or, you can configure your own map server. Please go to
|
||||
{ wms } or { configSettings} for more information."
|
||||
values={{
|
||||
defaultDistribution: (
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://www.elastic.co/downloads/kibana"
|
||||
>
|
||||
{`default distribution `}
|
||||
</a>
|
||||
),
|
||||
ems: (
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://www.elastic.co/elastic-maps-service"
|
||||
>
|
||||
{`Elastic Maps Service`}
|
||||
</a>
|
||||
),
|
||||
wms: (
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://www.elastic.co/guide/en/kibana/current/tilemap.html"
|
||||
>
|
||||
{`Custom WMS Configuration`}
|
||||
</a>
|
||||
),
|
||||
configSettings: (
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://www.elastic.co/guide/en/kibana/current/settings.html"
|
||||
>
|
||||
{`Custom TMS Using Config Settings`}
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<EuiSpacer size="xs"/>
|
||||
<EuiButtonEmpty
|
||||
size="s"
|
||||
flush="left"
|
||||
isDisabled={this.state.disabled}
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
disabled: true
|
||||
}, () => this.props.onChange(this.state.disabled));
|
||||
}}
|
||||
data-test-subj="suppressZoomWarnings"
|
||||
>
|
||||
{`Don't show again`}
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const zoomToast = ({
|
||||
title: 'No additional zoom levels',
|
||||
text: <ZoomWarning onChange={setZoomMsg}/>,
|
||||
'data-test-subj': 'maxZoomWarning',
|
||||
});
|
||||
|
||||
return (getZoomLevel, getMaxZoomLevel) => {
|
||||
return () => {
|
||||
const zoomLevel = getZoomLevel();
|
||||
const maxMapZoom = getMaxZoomLevel();
|
||||
if (!disableZoomMsg && zoomLevel === maxMapZoom) {
|
||||
toastNotifications.addDanger(zoomToast);
|
||||
}
|
||||
};
|
||||
};
|
||||
}());
|
|
@ -40,6 +40,7 @@ uiModules.get('kibana')
|
|||
|
||||
constructor() {
|
||||
|
||||
this._showZoomMessage = true;
|
||||
this._emsClient = new EMSClientV66({
|
||||
kbnVersion: kbnVersion,
|
||||
manifestServiceUrl: mapConfig.manifestServiceUrl,
|
||||
|
@ -49,6 +50,13 @@ uiModules.get('kibana')
|
|||
|
||||
}
|
||||
|
||||
shouldShowZoomMessage({ origin }) {
|
||||
return origin === ORIGIN.EMS && this._showZoomMessage;
|
||||
}
|
||||
|
||||
disableZoomMessage() {
|
||||
this._showZoomMessage = false;
|
||||
}
|
||||
|
||||
__debugStubManifestCalls(manifestRetrieval) {
|
||||
const oldGetManifest = this._emsClient._getManifest;
|
||||
|
@ -122,8 +130,6 @@ uiModules.get('kibana')
|
|||
allServices = allServices.concat(strippedServiceFromManifest);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return allServices;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,16 +22,16 @@ import expect from 'expect.js';
|
|||
export default function ({ getService, getPageObjects }) {
|
||||
const log = getService('log');
|
||||
const retry = getService('retry');
|
||||
const find = getService('find');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings']);
|
||||
|
||||
|
||||
describe('tile map visualize app', function () {
|
||||
|
||||
|
||||
describe('incomplete config', function describeIndexTests() {
|
||||
|
||||
|
||||
before(async function () {
|
||||
browser.setWindowSize(1280, 1000);
|
||||
|
||||
|
@ -45,7 +45,6 @@ export default function ({ getService, getPageObjects }) {
|
|||
await PageObjects.visualize.clickNewSearch();
|
||||
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
|
||||
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
|
||||
|
||||
//do not configure aggs
|
||||
});
|
||||
|
||||
|
@ -55,12 +54,9 @@ export default function ({ getService, getPageObjects }) {
|
|||
await PageObjects.visualize.clickMapZoomIn();
|
||||
await PageObjects.visualize.clickMapZoomIn();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('complete config', function describeIndexTests() {
|
||||
before(async function () {
|
||||
browser.setWindowSize(1280, 1000);
|
||||
|
@ -224,5 +220,67 @@ export default function ({ getService, getPageObjects }) {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('zoom warning behavior', function describeIndexTests() {
|
||||
|
||||
const waitForLoading = false;
|
||||
let zoomWarningEnabled;
|
||||
let last = false;
|
||||
const toastDefaultLife = 6000;
|
||||
|
||||
before(async function () {
|
||||
browser.setWindowSize(1280, 1000);
|
||||
|
||||
log.debug('navigateToApp visualize');
|
||||
await PageObjects.visualize.navigateToNewVisualization();
|
||||
log.debug('clickTileMap');
|
||||
await PageObjects.visualize.clickTileMap();
|
||||
await PageObjects.visualize.clickNewSearch();
|
||||
|
||||
zoomWarningEnabled = await testSubjects.exists('zoomWarningEnabled');
|
||||
log.debug(`Zoom warning enabled: ${zoomWarningEnabled}`);
|
||||
|
||||
const zoomLevel = 9;
|
||||
for (let i = 0; i < zoomLevel; i++) {
|
||||
await PageObjects.visualize.clickMapZoomIn();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
beforeEach(async function () {
|
||||
await PageObjects.common.sleep(2000);
|
||||
await PageObjects.visualize.clickMapZoomIn(waitForLoading);
|
||||
});
|
||||
|
||||
afterEach(async function () {
|
||||
if (!last) {
|
||||
await PageObjects.common.sleep(toastDefaultLife);
|
||||
await PageObjects.visualize.clickMapZoomOut(waitForLoading);
|
||||
}
|
||||
});
|
||||
|
||||
it('should show warning at zoom 10',
|
||||
async () => {
|
||||
testSubjects.existOrFail('maxZoomWarning');
|
||||
});
|
||||
|
||||
it('should continue providing zoom warning if left alone',
|
||||
async () => {
|
||||
testSubjects.existOrFail('maxZoomWarning');
|
||||
});
|
||||
|
||||
it('should suppress zoom warning if suppress warnings button clicked',
|
||||
async () => {
|
||||
last = true;
|
||||
await find.clickByCssSelector('[data-test-subj="suppressZoomWarnings"]');
|
||||
|
||||
await PageObjects.common.sleep(toastDefaultLife + 2000);
|
||||
await PageObjects.visualize.clickMapZoomOut(waitForLoading);
|
||||
await PageObjects.common.sleep(1000);
|
||||
await PageObjects.visualize.clickMapZoomIn(waitForLoading);
|
||||
|
||||
testSubjects.missingOrFail('maxZoomWarning');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -996,11 +996,13 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
|
|||
return await find.allByCssSelector(zoomSelector);
|
||||
}
|
||||
|
||||
async clickMapButton(zoomSelector) {
|
||||
async clickMapButton(zoomSelector, waitForLoading) {
|
||||
await retry.try(async () => {
|
||||
const zooms = await this.getZoomSelectors(zoomSelector);
|
||||
await Promise.all(zooms.map(async zoom => await zoom.click()));
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
if (waitForLoading) {
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1028,12 +1030,12 @@ export function VisualizePageProvider({ getService, getPageObjects }) {
|
|||
return requestObject.aggs.filter_agg.filter.geo_bounding_box['geo.coordinates'];
|
||||
}
|
||||
|
||||
async clickMapZoomIn() {
|
||||
await this.clickMapButton('a.leaflet-control-zoom-in');
|
||||
async clickMapZoomIn(waitForLoading = true) {
|
||||
await this.clickMapButton('a.leaflet-control-zoom-in', waitForLoading);
|
||||
}
|
||||
|
||||
async clickMapZoomOut() {
|
||||
await this.clickMapButton('a.leaflet-control-zoom-out');
|
||||
async clickMapZoomOut(waitForLoading = true) {
|
||||
await this.clickMapButton('a.leaflet-control-zoom-out', waitForLoading);
|
||||
}
|
||||
|
||||
async getMapZoomEnabled(zoomSelector) {
|
||||
|
|
|
@ -21,5 +21,6 @@ uiRoutes.addSetupWork(function (Private, serviceSettings) {
|
|||
return;
|
||||
}
|
||||
serviceSettings.addQueryParams({ license: tileMapPluginInfo.license.uid });
|
||||
serviceSettings.disableZoomMessage();
|
||||
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue