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:
Aaron Caldwell 2018-12-18 13:44:19 -07:00 committed by GitHub
parent a3793ecac5
commit d769722d30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 251 additions and 39 deletions

View file

@ -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;
}

View file

@ -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()) {

View 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);
}
};
};
}());

View file

@ -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;
}

View file

@ -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');
});
});
});
}

View file

@ -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) {

View file

@ -21,5 +21,6 @@ uiRoutes.addSetupWork(function (Private, serviceSettings) {
return;
}
serviceSettings.addQueryParams({ license: tileMapPluginInfo.license.uid });
serviceSettings.disableZoomMessage();
});