From 24582f8f605879c46950d16efe2153e8b08e29c7 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 17 Jun 2020 13:52:08 -0600 Subject: [PATCH 01/15] [Maps] show vector tile labels on top --- .../maps/public/classes/layers/layer.tsx | 5 +++ .../vector_tile_layer/vector_tile_layer.js | 4 ++ .../connected_components/map/mb/utils.js | 42 +++++++++++++++++++ .../connected_components/map/mb/view.js | 2 + 4 files changed, 53 insertions(+) diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index e122d1cda3ed9..c16e952b3c701 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -76,6 +76,7 @@ export interface ILayer { getPrevRequestToken(dataId: string): symbol | undefined; destroy: () => void; isPreviewLayer: () => boolean; + bubbleLabelsToTop: () => boolean; } export type Footnote = { icon: ReactElement; @@ -483,4 +484,8 @@ export class AbstractLayer implements ILayer { getType(): string | undefined { return this._descriptor.type; } + + bubbleLabelsToTop(): boolean { + return false; + } } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js index 61ec02e72adf2..d41b48cb74605 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js @@ -277,4 +277,8 @@ export class VectorTileLayer extends TileLayer { this._setOpacityForType(mbMap, mbLayer, mbLayerId); }); } + + bubbleLabelsToTop() { + return true; + } } diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/utils.js b/x-pack/plugins/maps/public/connected_components/map/mb/utils.js index a5934038f83df..58f6b00c58238 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/utils.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/utils.js @@ -60,6 +60,48 @@ export function moveLayerToTop(mbMap, layer) { }); } +function isTextLayer(mbLayer) { + if (mbLayer.type !== 'symbol') { + return false; + } + + const styleNames = []; + if (mbLayer.paint) { + styleNames.push(...Object.keys(mbLayer.paint)); + } + if (mbLayer.layout) { + styleNames.push(...Object.keys(mbLayer.layout)); + } + return styleNames.some((styleName) => { + return styleName.startsWith('text-'); + }); +} + +export function moveLabelsToTop(mbMap, layerList, spatialFiltersLayer) { + const mbStyle = mbMap.getStyle(); + if (!mbStyle.layers || mbStyle.layers.length === 0) { + return; + } + const reversedMbStyleLayers = mbStyle.layers.reverse(); + + // Start placing layers beneath spatial filters layer (which is always the layer on top) + let beneathLayerId = spatialFiltersLayer.getMbLayerIds()[0]; + + layerList + .filter((layer) => { + return layer.bubbleLabelsToTop(); + }) + .forEach((layer) => { + reversedMbStyleLayers.forEach((mbLayer) => { + if (layer.ownsMbLayerId(mbLayer.id) && isTextLayer(mbLayer)) { + mbMap.moveLayer(mbLayer.id, beneathLayerId); + // advance beneathLayerId so next moved layer will be beneath previously moved layer. + beneathLayerId = mbLayer.id; + } + }); + }); +} + /** * This is function assumes only a single layer moved in the layerList, compared to mbMap * It is optimized to minimize the amount of mbMap.moveLayer calls. diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/plugins/maps/public/connected_components/map/mb/view.js index 42235bfd5442e..f49f41ac88aea 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/view.js @@ -12,6 +12,7 @@ import { removeOrphanedSourcesAndLayers, addSpritesheetToMap, moveLayerToTop, + moveLabelsToTop, } from './utils'; import { getGlyphUrl, isRetina } from '../../../meta'; import { DECIMAL_DEGREES_PRECISION, ZOOM_PRECISION } from '../../../../common/constants'; @@ -267,6 +268,7 @@ export class MBMapContainer extends React.Component { this.props.layerList.forEach((layer) => layer.syncLayerWithMB(this.state.mbMap)); syncLayerOrderForSingleLayer(this.state.mbMap, this.props.layerList); moveLayerToTop(this.state.mbMap, this.props.spatialFiltersLayer); + moveLabelsToTop(this.state.mbMap, this.props.layerList, this.props.spatialFiltersLayer); }; _syncMbMapWithInspector = () => { From a51539e260d98684970dc370b3b783213374eddd Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 26 Jun 2020 11:47:38 -0600 Subject: [PATCH 02/15] experiment with new sort algorithm --- .../maps/public/classes/layers/layer.tsx | 2 +- .../map/mb/sort_layers.ts | 135 ++++++++++++++++++ .../connected_components/map/mb/view.js | 8 +- 3 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index c16e952b3c701..4a0a9eb14b4bb 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -190,7 +190,7 @@ export class AbstractLayer implements ILayer { async getDisplayName(source?: ISource): Promise { if (this._descriptor.label) { - return this._descriptor.label; + return `${this._descriptor.label} ${this._descriptor.id}`; } const sourceDisplayName = source diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts new file mode 100644 index 0000000000000..7688e6044159d --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts @@ -0,0 +1,135 @@ +/* + * 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 { Map as MbMap } from 'mapbox-gl'; +import { ILayer } from '../../../classes/layers/layer'; + +// "Layer" is overloaded. +// 1) Map layer: ILayer implemenation. A single map layer consists of one to many mapbox layers. +// 2) Mapbox layer: Individual unit of rendering such as text, circles, polygons, or lines. + +function getIsTextLayer(mbLayer) { + if (mbLayer.type !== 'symbol') { + return false; + } + + const styleNames = []; + if (mbLayer.paint) { + styleNames.push(...Object.keys(mbLayer.paint)); + } + if (mbLayer.layout) { + styleNames.push(...Object.keys(mbLayer.layout)); + } + return styleNames.some((styleName) => { + return styleName.startsWith('text-'); + }); +} + +function doesMbLayerBelongToMapLayerAndClass(mapLayer: ILayer, mbLayer, layerClass: LAYER_CLASS) { + if (!mapLayer.ownsMbLayerId(mbLayer.id)) { + return false; + } + + // mb layer belongs to mapLayer, now filter by layer class + if (layerClass === LAYER_CLASS.ANY) { + return true; + } + const isTextLayer = getIsTextLayer(mbLayer); + return layerClass.LABEL ? isTextLayer : !isTextLayer; +} + +enum LAYER_CLASS { + ANY = 'ANY', + LABEL = 'LABEL', + NON_LABEL = 'NON_LABEL', +} + +export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerList: ILayer) { + const mbStyle = mbMap.getStyle(); + if (!mbStyle.layers || mbStyle.layers.length === 0) { + return; + } + + console.log('before', mbMap.getStyle()); + + // mapbox stores layers in draw order so layers with higher index are drawn on-top of layers with lower index + // Need to reverse order because layer ordering starts from the top and work its way down. + const reversedMbLayers = [...mbStyle.layers].reverse(); + let index = 0; + let beneathLayerId; + // track sorted mbLayers so when/if they are encounted later, they can be skipped + const processedMbLayerIds = new Map(); + + function moveMapLayer(mapLayer: ILayer, layerClass: LAYER_CLASS) { + mbStyle.layers + .filter((mbLayer) => { + return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass); + }) + .forEach((mbLayer) => { + mbMap.moveLayer(mbLayer.id, beneathLayerId); + processedMbLayerIds.set(mbLayer.id, true); + }); + } + + function advanceIndexToNextMapLayer(mapLayer: ILayer, layerClass: LAYER_CLASS) { + while ( + index < reversedMbLayers.length - 1 && + (doesMbLayerBelongToMapLayerAndClass(mapLayer, reversedMbLayers[index], layerClass) || + processedMbLayerIds.has(reversedMbLayers[index].id)) + ) { + index++; + } + } + + function getBottomMbLayerIdForMapLayer(mapLayer: ILayer, layerClass: LAYER_CLASS) { + const mbLayer = mbStyle.layers.find((mbLayer) => { + return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass); + }); + return mbLayer ? mbLayer.id : null; + } + + // Ensure spatial filters layer is the top layer. + if ( + !doesMbLayerBelongToMapLayerAndClass( + spatialFiltersLayer, + reversedMbLayers[index], + LAYER_CLASS.ANY + ) + ) { + moveMapLayer(spatialFiltersLayer, LAYER_CLASS.ANY); + } else { + advanceIndexToNextMapLayer(spatialFiltersLayer, LAYER_CLASS.ANY); + } + beneathLayerId = getBottomMbLayerIdForMapLayer(spatialFiltersLayer, LAYER_CLASS.ANY); + + // Move map layer labels to top + /* [...layerList].reverse() + .filter((mapLayer => { + return mapLayer.bubbleLabelsToTop(); + })) + .forEach((mapLayer: ILayer) => { + if (!doesMbLayerBelongToMapLayerAndClass(mapLayer, reversedMbLayers[index], LAYER_CLASS.LABEL)) { + moveMapLayer(mapLayer, LAYER_CLASS.LABEL); + } else { + advanceIndexToNextMapLayer(mapLayer, LAYER_CLASS.LABEL); + } + beneathLayerId = getBottomMbLayerIdForMapLayer(mapLayer, LAYER_CLASS.LABEL); + });*/ + + // Move map layers to top + [...layerList].reverse().forEach((mapLayer: ILayer) => { + // const layerClass = mapLayer.bubbleLabelsToTop() ? LAYER_CLASS.NON_LABEL : LAYER_CLASS.ANY; + const layerClass = LAYER_CLASS.ANY; + if (!doesMbLayerBelongToMapLayerAndClass(mapLayer, reversedMbLayers[index], layerClass)) { + moveMapLayer(mapLayer, layerClass); + } else { + advanceIndexToNextMapLayer(mapLayer, layerClass); + } + beneathLayerId = getBottomMbLayerIdForMapLayer(mapLayer, layerClass); + }); + + console.log('after', mbMap.getStyle()); +} diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/plugins/maps/public/connected_components/map/mb/view.js index f49f41ac88aea..24d43bc61ff1d 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/view.js @@ -14,6 +14,7 @@ import { moveLayerToTop, moveLabelsToTop, } from './utils'; +import { syncLayerOrder } from './sort_layers'; import { getGlyphUrl, isRetina } from '../../../meta'; import { DECIMAL_DEGREES_PRECISION, ZOOM_PRECISION } from '../../../../common/constants'; import mapboxgl from 'mapbox-gl/dist/mapbox-gl-csp'; @@ -266,9 +267,10 @@ export class MBMapContainer extends React.Component { this.props.spatialFiltersLayer ); this.props.layerList.forEach((layer) => layer.syncLayerWithMB(this.state.mbMap)); - syncLayerOrderForSingleLayer(this.state.mbMap, this.props.layerList); - moveLayerToTop(this.state.mbMap, this.props.spatialFiltersLayer); - moveLabelsToTop(this.state.mbMap, this.props.layerList, this.props.spatialFiltersLayer); + syncLayerOrder(this.state.mbMap, this.props.spatialFiltersLayer, this.props.layerList); + //syncLayerOrderForSingleLayer(this.state.mbMap, this.props.layerList); + //moveLayerToTop(this.state.mbMap, this.props.spatialFiltersLayer); + //moveLabelsToTop(this.state.mbMap, this.props.layerList, this.props.spatialFiltersLayer); }; _syncMbMapWithInspector = () => { From 2af9ae39984a0cf96ac6546763001f5734117606 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 26 Jun 2020 13:11:44 -0600 Subject: [PATCH 03/15] clean up --- .../map/mb/sort_layers.ts | 143 +++++++++--------- 1 file changed, 73 insertions(+), 70 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts index 7688e6044159d..2864736ab54fc 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts @@ -38,7 +38,7 @@ function doesMbLayerBelongToMapLayerAndClass(mapLayer: ILayer, mbLayer, layerCla return true; } const isTextLayer = getIsTextLayer(mbLayer); - return layerClass.LABEL ? isTextLayer : !isTextLayer; + return layerClass === LAYER_CLASS.LABEL ? isTextLayer : !isTextLayer; } enum LAYER_CLASS { @@ -47,89 +47,92 @@ enum LAYER_CLASS { NON_LABEL = 'NON_LABEL', } -export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerList: ILayer) { - const mbStyle = mbMap.getStyle(); - if (!mbStyle.layers || mbStyle.layers.length === 0) { - return; - } +function moveMapLayer( + mbMap: MbMap, + mapLayer: ILayer, + layerClass: LAYER_CLASS, + beneathLayerId: string | null +) { + const mbLayers = mbMap.getStyle().layers; + mbLayers + .filter((mbLayer) => { + if (!doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass)) { + return false; + } - console.log('before', mbMap.getStyle()); - - // mapbox stores layers in draw order so layers with higher index are drawn on-top of layers with lower index - // Need to reverse order because layer ordering starts from the top and work its way down. - const reversedMbLayers = [...mbStyle.layers].reverse(); - let index = 0; - let beneathLayerId; - // track sorted mbLayers so when/if they are encounted later, they can be skipped - const processedMbLayerIds = new Map(); - - function moveMapLayer(mapLayer: ILayer, layerClass: LAYER_CLASS) { - mbStyle.layers - .filter((mbLayer) => { - return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass); - }) - .forEach((mbLayer) => { - mbMap.moveLayer(mbLayer.id, beneathLayerId); - processedMbLayerIds.set(mbLayer.id, true); - }); - } + // TODO handle case where beneathLayerId is not provided. Then check if layer is top layer and skip if it is - function advanceIndexToNextMapLayer(mapLayer: ILayer, layerClass: LAYER_CLASS) { - while ( - index < reversedMbLayers.length - 1 && - (doesMbLayerBelongToMapLayerAndClass(mapLayer, reversedMbLayers[index], layerClass) || - processedMbLayerIds.has(reversedMbLayers[index].id)) - ) { - index++; + // Only move layer when its not in correct order. + const nextMbLayerId = getNextMapLayerBottomMbLayerId(mbLayers, mapLayer, layerClass); + return nextMbLayerId !== beneathLayerId; + }) + .forEach((mbLayer) => { + console.log(`Move mbLayer: ${mbLayer.id}, beneathLayerId: ${beneathLayerId}`); + mbMap.moveLayer(mbLayer.id, beneathLayerId); + }); +} + +function getBottomMbLayerId(mbLayers: unknown[], mapLayer: ILayer, layerClass: LAYER_CLASS) { + const mbLayer = mbLayers.find((mbLayer) => { + return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass); + }); + return mbLayer ? mbLayer.id : null; +} + +function getNextMapLayerBottomMbLayerId( + mbLayers: unknown[], + mapLayer: ILayer, + layerClass: LAYER_CLASS +) { + let bottomMbLayerFound = false; + let nextMbLayerId = null; + for (let i = 0; i < mbLayers.length; i++) { + if (!bottomMbLayerFound) { + if (doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayers[i], layerClass)) { + bottomMbLayerFound = true; + } + } else { + // Next mbLayer not belonging to map layer is the mapbox layer we are looking for + if (!doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayers[i], layerClass)) { + nextMbLayerId = mbLayers[i].id; + break; + } } } - function getBottomMbLayerIdForMapLayer(mapLayer: ILayer, layerClass: LAYER_CLASS) { - const mbLayer = mbStyle.layers.find((mbLayer) => { - return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass); - }); - return mbLayer ? mbLayer.id : null; + return nextMbLayerId; +} + +export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerList: ILayer) { + const mbStyle = mbMap.getStyle(); + if (!mbStyle.layers || mbStyle.layers.length === 0) { + return; } // Ensure spatial filters layer is the top layer. - if ( - !doesMbLayerBelongToMapLayerAndClass( - spatialFiltersLayer, - reversedMbLayers[index], - LAYER_CLASS.ANY - ) - ) { - moveMapLayer(spatialFiltersLayer, LAYER_CLASS.ANY); - } else { - advanceIndexToNextMapLayer(spatialFiltersLayer, LAYER_CLASS.ANY); - } - beneathLayerId = getBottomMbLayerIdForMapLayer(spatialFiltersLayer, LAYER_CLASS.ANY); + moveMapLayer(mbMap, spatialFiltersLayer, LAYER_CLASS.ANY); + let beneathLayerId = getBottomMbLayerId(mbStyle.layers, spatialFiltersLayer, LAYER_CLASS.ANY); // Move map layer labels to top - /* [...layerList].reverse() - .filter((mapLayer => { + [...layerList] + .reverse() + .filter((mapLayer) => { return mapLayer.bubbleLabelsToTop(); - })) + }) .forEach((mapLayer: ILayer) => { - if (!doesMbLayerBelongToMapLayerAndClass(mapLayer, reversedMbLayers[index], LAYER_CLASS.LABEL)) { - moveMapLayer(mapLayer, LAYER_CLASS.LABEL); - } else { - advanceIndexToNextMapLayer(mapLayer, LAYER_CLASS.LABEL); - } - beneathLayerId = getBottomMbLayerIdForMapLayer(mapLayer, LAYER_CLASS.LABEL); - });*/ + moveMapLayer(mbMap, mapLayer, LAYER_CLASS.LABEL, beneathLayerId); + beneathLayerId = getBottomMbLayerId( + mbStyle.layers, + mapLayer, + LAYER_CLASS.LABEL, + beneathLayerId + ); + }); // Move map layers to top [...layerList].reverse().forEach((mapLayer: ILayer) => { - // const layerClass = mapLayer.bubbleLabelsToTop() ? LAYER_CLASS.NON_LABEL : LAYER_CLASS.ANY; - const layerClass = LAYER_CLASS.ANY; - if (!doesMbLayerBelongToMapLayerAndClass(mapLayer, reversedMbLayers[index], layerClass)) { - moveMapLayer(mapLayer, layerClass); - } else { - advanceIndexToNextMapLayer(mapLayer, layerClass); - } - beneathLayerId = getBottomMbLayerIdForMapLayer(mapLayer, layerClass); + const layerClass = mapLayer.bubbleLabelsToTop() ? LAYER_CLASS.NON_LABEL : LAYER_CLASS.ANY; + moveMapLayer(mbMap, mapLayer, layerClass, beneathLayerId); + beneathLayerId = getBottomMbLayerId(mbStyle.layers, mapLayer, layerClass, beneathLayerId); }); - - console.log('after', mbMap.getStyle()); } From b5c59c07f73385724184307a73a7760a4643d8e2 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 26 Jun 2020 16:59:27 -0600 Subject: [PATCH 04/15] remove old sort method --- .../map/mb/sort_layers.ts | 69 +++++----- .../connected_components/map/mb/utils.js | 121 ------------------ .../connected_components/map/mb/view.js | 11 +- 3 files changed, 38 insertions(+), 163 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts index 2864736ab54fc..87a1009e02fad 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts @@ -49,50 +49,49 @@ enum LAYER_CLASS { function moveMapLayer( mbMap: MbMap, + mbLayers: unknown[], mapLayer: ILayer, layerClass: LAYER_CLASS, - beneathLayerId: string | null + beneathMbLayerId: string | null ) { - const mbLayers = mbMap.getStyle().layers; mbLayers .filter((mbLayer) => { - if (!doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass)) { - return false; - } - - // TODO handle case where beneathLayerId is not provided. Then check if layer is top layer and skip if it is - - // Only move layer when its not in correct order. - const nextMbLayerId = getNextMapLayerBottomMbLayerId(mbLayers, mapLayer, layerClass); - return nextMbLayerId !== beneathLayerId; + return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass); }) .forEach((mbLayer) => { - console.log(`Move mbLayer: ${mbLayer.id}, beneathLayerId: ${beneathLayerId}`); - mbMap.moveLayer(mbLayer.id, beneathLayerId); + mbMap.moveLayer(mbLayer.id, beneathMbLayerId); }); } function getBottomMbLayerId(mbLayers: unknown[], mapLayer: ILayer, layerClass: LAYER_CLASS) { - const mbLayer = mbLayers.find((mbLayer) => { + const bottomMbLayer = mbLayers.find((mbLayer) => { return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass); }); - return mbLayer ? mbLayer.id : null; + return bottomMbLayer ? bottomMbLayer.id : null; } -function getNextMapLayerBottomMbLayerId( - mbLayers: unknown[], +function isLayerInOrder( + mbMap: MbMap, mapLayer: ILayer, - layerClass: LAYER_CLASS + layerClass: LAYER_CLASS, + beneathMbLayerId: string | null ) { - let bottomMbLayerFound = false; + const mbLayers = mbMap.getStyle().layers; // check ordering against mapbox to account for any upstream moves. + + if (!beneathMbLayerId) { + // Check that map layer is top layer + return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayers[mbLayers.length - 1], layerClass); + } + + let inMapLayerBlock = false; let nextMbLayerId = null; for (let i = 0; i < mbLayers.length; i++) { - if (!bottomMbLayerFound) { + if (!inMapLayerBlock) { if (doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayers[i], layerClass)) { - bottomMbLayerFound = true; + inMapLayerBlock = true; } } else { - // Next mbLayer not belonging to map layer is the mapbox layer we are looking for + // Next mbLayer not belonging to this map layer is the bottom mb layer for the next map layer if (!doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayers[i], layerClass)) { nextMbLayerId = mbLayers[i].id; break; @@ -100,7 +99,7 @@ function getNextMapLayerBottomMbLayerId( } } - return nextMbLayerId; + return nextMbLayerId === beneathMbLayerId; } export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerList: ILayer) { @@ -110,29 +109,35 @@ export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerL } // Ensure spatial filters layer is the top layer. - moveMapLayer(mbMap, spatialFiltersLayer, LAYER_CLASS.ANY); - let beneathLayerId = getBottomMbLayerId(mbStyle.layers, spatialFiltersLayer, LAYER_CLASS.ANY); + if (!isLayerInOrder(mbMap, spatialFiltersLayer, LAYER_CLASS.ANY, null)) { + moveMapLayer(mbMap, mbStyle.layers, spatialFiltersLayer, LAYER_CLASS.ANY); + } + let beneathMbLayerId = getBottomMbLayerId(mbStyle.layers, spatialFiltersLayer, LAYER_CLASS.ANY); - // Move map layer labels to top + // Sort map layer labels [...layerList] .reverse() .filter((mapLayer) => { return mapLayer.bubbleLabelsToTop(); }) .forEach((mapLayer: ILayer) => { - moveMapLayer(mbMap, mapLayer, LAYER_CLASS.LABEL, beneathLayerId); - beneathLayerId = getBottomMbLayerId( + if (!isLayerInOrder(mbMap, mapLayer, LAYER_CLASS.LABEL, beneathMbLayerId)) { + moveMapLayer(mbMap, mbStyle.layers, mapLayer, LAYER_CLASS.LABEL, beneathMbLayerId); + } + beneathMbLayerId = getBottomMbLayerId( mbStyle.layers, mapLayer, LAYER_CLASS.LABEL, - beneathLayerId + beneathMbLayerId ); }); - // Move map layers to top + // Sort map layers [...layerList].reverse().forEach((mapLayer: ILayer) => { const layerClass = mapLayer.bubbleLabelsToTop() ? LAYER_CLASS.NON_LABEL : LAYER_CLASS.ANY; - moveMapLayer(mbMap, mapLayer, layerClass, beneathLayerId); - beneathLayerId = getBottomMbLayerId(mbStyle.layers, mapLayer, layerClass, beneathLayerId); + if (!isLayerInOrder(mbMap, mapLayer, layerClass, beneathMbLayerId)) { + moveMapLayer(mbMap, mbStyle.layers, mapLayer, layerClass, beneathMbLayerId); + } + beneathMbLayerId = getBottomMbLayerId(mbStyle.layers, mapLayer, layerClass, beneathMbLayerId); }); } diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/utils.js b/x-pack/plugins/maps/public/connected_components/map/mb/utils.js index 58f6b00c58238..e5801afd5b601 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/utils.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/utils.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; import { RGBAImage } from './image_utils'; export function removeOrphanedSourcesAndLayers(mbMap, layerList, spatialFilterLayer) { @@ -45,126 +44,6 @@ export function removeOrphanedSourcesAndLayers(mbMap, layerList, spatialFilterLa mbSourcesToRemove.forEach((mbSourceId) => mbMap.removeSource(mbSourceId)); } -export function moveLayerToTop(mbMap, layer) { - const mbStyle = mbMap.getStyle(); - - if (!mbStyle.layers || mbStyle.layers.length === 0) { - return; - } - - layer.getMbLayerIds().forEach((mbLayerId) => { - const mbLayer = mbMap.getLayer(mbLayerId); - if (mbLayer) { - mbMap.moveLayer(mbLayerId); - } - }); -} - -function isTextLayer(mbLayer) { - if (mbLayer.type !== 'symbol') { - return false; - } - - const styleNames = []; - if (mbLayer.paint) { - styleNames.push(...Object.keys(mbLayer.paint)); - } - if (mbLayer.layout) { - styleNames.push(...Object.keys(mbLayer.layout)); - } - return styleNames.some((styleName) => { - return styleName.startsWith('text-'); - }); -} - -export function moveLabelsToTop(mbMap, layerList, spatialFiltersLayer) { - const mbStyle = mbMap.getStyle(); - if (!mbStyle.layers || mbStyle.layers.length === 0) { - return; - } - const reversedMbStyleLayers = mbStyle.layers.reverse(); - - // Start placing layers beneath spatial filters layer (which is always the layer on top) - let beneathLayerId = spatialFiltersLayer.getMbLayerIds()[0]; - - layerList - .filter((layer) => { - return layer.bubbleLabelsToTop(); - }) - .forEach((layer) => { - reversedMbStyleLayers.forEach((mbLayer) => { - if (layer.ownsMbLayerId(mbLayer.id) && isTextLayer(mbLayer)) { - mbMap.moveLayer(mbLayer.id, beneathLayerId); - // advance beneathLayerId so next moved layer will be beneath previously moved layer. - beneathLayerId = mbLayer.id; - } - }); - }); -} - -/** - * This is function assumes only a single layer moved in the layerList, compared to mbMap - * It is optimized to minimize the amount of mbMap.moveLayer calls. - * @param mbMap - * @param layerList - */ -export function syncLayerOrderForSingleLayer(mbMap, layerList) { - if (!layerList || layerList.length === 0) { - return; - } - - const mbLayers = mbMap.getStyle().layers.slice(); - const layerIds = []; - mbLayers.forEach((mbLayer) => { - const layer = layerList.find((layer) => layer.ownsMbLayerId(mbLayer.id)); - if (layer) { - layerIds.push(layer.getId()); - } - }); - - const currentLayerOrderLayerIds = _.uniq(layerIds); - - const newLayerOrderLayerIdsUnfiltered = layerList.map((l) => l.getId()); - const newLayerOrderLayerIds = newLayerOrderLayerIdsUnfiltered.filter((layerId) => - currentLayerOrderLayerIds.includes(layerId) - ); - - let netPos = 0; - let netNeg = 0; - const movementArr = currentLayerOrderLayerIds.reduce((accu, id, idx) => { - const movement = newLayerOrderLayerIds.findIndex((newOId) => newOId === id) - idx; - movement > 0 ? netPos++ : movement < 0 && netNeg++; - accu.push({ id, movement }); - return accu; - }, []); - if (netPos === 0 && netNeg === 0) { - return; - } - const movedLayerId = - (netPos >= netNeg && movementArr.find((l) => l.movement < 0).id) || - (netPos < netNeg && movementArr.find((l) => l.movement > 0).id); - const nextLayerIdx = newLayerOrderLayerIds.findIndex((layerId) => layerId === movedLayerId) + 1; - - let nextMbLayerId; - if (nextLayerIdx === newLayerOrderLayerIds.length) { - nextMbLayerId = null; - } else { - const foundLayer = mbLayers.find(({ id: mbLayerId }) => { - const layerId = newLayerOrderLayerIds[nextLayerIdx]; - const layer = layerList.find((layer) => layer.getId() === layerId); - return layer.ownsMbLayerId(mbLayerId); - }); - nextMbLayerId = foundLayer.id; - } - - const movedLayer = layerList.find((layer) => layer.getId() === movedLayerId); - mbLayers.forEach(({ id: mbLayerId }) => { - if (movedLayer.ownsMbLayerId(mbLayerId)) { - mbMap.moveLayer(mbLayerId, nextMbLayerId); - } - }); -} - export async function addSpritesheetToMap(json, imgUrl, mbMap) { const imgData = await loadSpriteSheetImageData(imgUrl); addSpriteSheetToMapFromImageData(json, imgData, mbMap); diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/plugins/maps/public/connected_components/map/mb/view.js index 24d43bc61ff1d..d96deb226744b 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/view.js @@ -7,13 +7,7 @@ import _ from 'lodash'; import React from 'react'; import { ResizeChecker } from '../../../../../../../src/plugins/kibana_utils/public'; -import { - syncLayerOrderForSingleLayer, - removeOrphanedSourcesAndLayers, - addSpritesheetToMap, - moveLayerToTop, - moveLabelsToTop, -} from './utils'; +import { removeOrphanedSourcesAndLayers, addSpritesheetToMap } from './utils'; import { syncLayerOrder } from './sort_layers'; import { getGlyphUrl, isRetina } from '../../../meta'; import { DECIMAL_DEGREES_PRECISION, ZOOM_PRECISION } from '../../../../common/constants'; @@ -268,9 +262,6 @@ export class MBMapContainer extends React.Component { ); this.props.layerList.forEach((layer) => layer.syncLayerWithMB(this.state.mbMap)); syncLayerOrder(this.state.mbMap, this.props.spatialFiltersLayer, this.props.layerList); - //syncLayerOrderForSingleLayer(this.state.mbMap, this.props.layerList); - //moveLayerToTop(this.state.mbMap, this.props.spatialFiltersLayer); - //moveLabelsToTop(this.state.mbMap, this.props.layerList, this.props.spatialFiltersLayer); }; _syncMbMapWithInspector = () => { From 6283461cd8338b76382bc26bf8f810926b2c0e25 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Sun, 28 Jun 2020 09:24:09 -0600 Subject: [PATCH 05/15] add unit test for sort layer --- .../map/mb/mb.utils.test.js | 79 +------- .../map/mb/sort_layers.test.ts | 179 ++++++++++++++++++ .../map/mb/sort_layers.ts | 47 +++-- 3 files changed, 203 insertions(+), 102 deletions(-) create mode 100644 x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js b/x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js index 376010f0df9ba..e2050724ef684 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { removeOrphanedSourcesAndLayers, syncLayerOrderForSingleLayer } from './utils'; +import { removeOrphanedSourcesAndLayers } from './utils'; import { SPATIAL_FILTERS_LAYER_ID } from '../../../../common/constants'; import _ from 'lodash'; @@ -186,80 +186,3 @@ describe('removeOrphanedSourcesAndLayers', () => { expect(mockMbMap.getStyle()).toEqual(styleWithSpatialFilters); }); }); - -describe('syncLayerOrderForSingleLayer', () => { - test('should move bar layer in front of foo layer', async () => { - const fooLayer = makeSingleSourceMockLayer('foo'); - const barLayer = makeSingleSourceMockLayer('bar'); - - const currentLayerOrder = [fooLayer, barLayer]; - const nextLayerListOrder = [barLayer, fooLayer]; - - const currentStyle = getMockStyle(currentLayerOrder); - const mockMbMap = new MockMbMap(currentStyle); - syncLayerOrderForSingleLayer(mockMbMap, nextLayerListOrder); - const orderedStyle = mockMbMap.getStyle(); - - const nextStyle = getMockStyle(nextLayerListOrder); - expect(orderedStyle).toEqual(nextStyle); - }); - - test('should fail at moving multiple layers (this tests a limitation of the sync)', async () => { - //This is a known limitation of the layer order syncing. - //It assumes only a single layer will have moved. - //In practice, the Maps app will likely not cause multiple layers to move at once: - // - the UX only allows dragging a single layer - // - redux triggers a updates frequently enough - //But this is conceptually "wrong", as the sync does not actually operate in the same way as all the other mb-syncing methods - - const fooLayer = makeSingleSourceMockLayer('foo'); - const barLayer = makeSingleSourceMockLayer('bar'); - const foozLayer = makeSingleSourceMockLayer('foo'); - const bazLayer = makeSingleSourceMockLayer('baz'); - - const currentLayerOrder = [fooLayer, barLayer, foozLayer, bazLayer]; - const nextLayerListOrder = [bazLayer, barLayer, foozLayer, fooLayer]; - - const currentStyle = getMockStyle(currentLayerOrder); - const mockMbMap = new MockMbMap(currentStyle); - syncLayerOrderForSingleLayer(mockMbMap, nextLayerListOrder); - const orderedStyle = mockMbMap.getStyle(); - - const nextStyle = getMockStyle(nextLayerListOrder); - const isSyncSuccesful = _.isEqual(orderedStyle, nextStyle); - expect(isSyncSuccesful).toEqual(false); - }); - - test('should move bar layer in front of foo layer (multi source)', async () => { - const fooLayer = makeSingleSourceMockLayer('foo'); - const barLayer = makeMultiSourceMockLayer('bar'); - - const currentLayerOrder = [fooLayer, barLayer]; - const nextLayerListOrder = [barLayer, fooLayer]; - - const currentStyle = getMockStyle(currentLayerOrder); - const mockMbMap = new MockMbMap(currentStyle); - syncLayerOrderForSingleLayer(mockMbMap, nextLayerListOrder); - const orderedStyle = mockMbMap.getStyle(); - - const nextStyle = getMockStyle(nextLayerListOrder); - expect(orderedStyle).toEqual(nextStyle); - }); - - test('should move bar layer in front of foo layer, but after baz layer', async () => { - const bazLayer = makeSingleSourceMockLayer('baz'); - const fooLayer = makeSingleSourceMockLayer('foo'); - const barLayer = makeSingleSourceMockLayer('bar'); - - const currentLayerOrder = [bazLayer, fooLayer, barLayer]; - const nextLayerListOrder = [bazLayer, barLayer, fooLayer]; - - const currentStyle = getMockStyle(currentLayerOrder); - const mockMbMap = new MockMbMap(currentStyle); - syncLayerOrderForSingleLayer(mockMbMap, nextLayerListOrder); - const orderedStyle = mockMbMap.getStyle(); - - const nextStyle = getMockStyle(nextLayerListOrder); - expect(orderedStyle).toEqual(nextStyle); - }); -}); diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts new file mode 100644 index 0000000000000..b788b02270d41 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts @@ -0,0 +1,179 @@ +/* + * 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. + */ +/* eslint-disable max-classes-per-file */ + +import _ from 'lodash'; +import { Map as MbMap, Layer as MbLayer, Style as MbStyle } from 'mapbox-gl'; +import { getIsTextLayer, syncLayerOrder } from './sort_layers'; +import { SPATIAL_FILTERS_LAYER_ID } from '../../../../common/constants'; +import { AbstractLayer } from '../../../classes/layers/layer'; + +let moveCounter = 0; + +class MockMbMap { + private _style: MbStyle; + + constructor(style: MbStyle) { + this._style = _.cloneDeep(style); + } + + getStyle() { + return _.cloneDeep(this._style); + } + + moveLayer(id: string, beforeId?: string) { + moveCounter++; + const layerIndex = this._style.layers.findIndex((layer) => { + return layer.id === id; + }); + if (layerIndex === -1) { + throw new Error(`Can not move layer, layer with id: ${id} does not exist`); + } + const moveMbLayer = this._style.layers[layerIndex]; + + if (beforeId) { + const beforeLayerIndex = this._style.layers.findIndex((mbLayer) => { + return mbLayer.id === beforeId; + }); + if (beforeLayerIndex === -1) { + throw new Error(`Can not move layer, before layer with id: ${id} does not exist`); + } + this._style.layers.splice(beforeLayerIndex, 0, moveMbLayer); + } else { + const topIndex = this._style.layers.length; + this._style.layers.splice(topIndex, 0, moveMbLayer); + } + + // Remove layer from previous location + this._style.layers.splice(layerIndex, 1); + + return this; + } +} + +class MockMapLayer { + private readonly _id: string; + private readonly _bubbleLabelsToTop: boolean; + + constructor(id: string, bubbleLabelsToTop: boolean) { + this._id = id; + this._bubbleLabelsToTop = bubbleLabelsToTop; + } + + ownsMbLayerId(mbLayerId: string) { + return mbLayerId.startsWith(this._id); + } + + bubbleLabelsToTop() { + return this._bubbleLabelsToTop; + } + + getId() { + return this._id; + } +} + +test('getIsTextLayer', () => { + const paintLabelMbLayer = { + id: `mylayer_text`, + type: 'symbol', + paint: { 'text-color': 'red' }, + } as MbLayer; + expect(getIsTextLayer(paintLabelMbLayer)).toBe(true); + + const layoutLabelMbLayer = { + id: `mylayer_text`, + type: 'symbol', + layout: { 'text-size': 'red' }, + } as MbLayer; + expect(getIsTextLayer(layoutLabelMbLayer)).toBe(true); + + const iconMbLayer = { + id: `mylayer_text`, + type: 'symbol', + paint: { 'icon-color': 'house' }, + } as MbLayer; + expect(getIsTextLayer(iconMbLayer)).toBe(false); + + const circleMbLayer = { id: `mylayer_text`, type: 'circle' } as MbLayer; + expect(getIsTextLayer(circleMbLayer)).toBe(false); +}); + +describe('sortLayer', () => { + const ALPHA_LAYER_ID = 'alpha'; + const BRAVO_LAYER_ID = 'bravo'; + const CHARLIE_LAYER_ID = 'charlie'; + + const spatialFilterLayer = new MockMapLayer(SPATIAL_FILTERS_LAYER_ID, false); + const mapLayers = [ + new MockMapLayer(CHARLIE_LAYER_ID, true), + new MockMapLayer(BRAVO_LAYER_ID, false), + new MockMapLayer(ALPHA_LAYER_ID, false), + ]; + + beforeEach(() => { + moveCounter = 0; + }); + + test('Should move layers to expected order', () => { + const initialMbStyle = { + version: 0, + layers: [ + { id: `${BRAVO_LAYER_ID}_text`, type: 'symbol' } as MbLayer, + { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as MbLayer, + { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { + id: `${CHARLIE_LAYER_ID}_text`, + type: 'symbol', + paint: { 'text-color': 'red' }, + } as MbLayer, + { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as MbLayer, + { id: `${ALPHA_LAYER_ID}_text`, type: 'symbol' } as MbLayer, + { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + ], + }; + const mbMap = new MockMbMap(initialMbStyle); + syncLayerOrder(mbMap, spatialFilterLayer, mapLayers); + const sortedMbStyle = mbMap.getStyle(); + const sortedMbLayerIds = sortedMbStyle.layers.map((mbLayer) => { + return mbLayer.id; + }); + expect(sortedMbLayerIds).toEqual([ + 'charlie_fill', + 'bravo_text', + 'bravo_circle', + 'alpha_text', + 'alpha_circle', + 'charlie_text', + 'SPATIAL_FILTERS_LAYER_ID_fill', + 'SPATIAL_FILTERS_LAYER_ID_circle', + ]); + }); + + test('Should not call move layers when layers are in expected order', () => { + const initialMbStyle = { + version: 0, + layers: [ + { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as MbLayer, + { id: `${BRAVO_LAYER_ID}_text`, type: 'symbol' } as MbLayer, + { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { id: `${ALPHA_LAYER_ID}_text`, type: 'symbol' } as MbLayer, + { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { + id: `${CHARLIE_LAYER_ID}_text`, + type: 'symbol', + paint: { 'text-color': 'red' }, + } as MbLayer, + { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as MbLayer, + { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + ], + }; + const mbMap = new MockMbMap(initialMbStyle); + syncLayerOrder(mbMap, spatialFilterLayer, mapLayers); + expect(moveCounter).toBe(0); + }); +}); diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts index 87a1009e02fad..9d1cd9de8680d 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Map as MbMap } from 'mapbox-gl'; +import { Map as MbMap, Layer as MbLayer } from 'mapbox-gl'; import { ILayer } from '../../../classes/layers/layer'; // "Layer" is overloaded. -// 1) Map layer: ILayer implemenation. A single map layer consists of one to many mapbox layers. -// 2) Mapbox layer: Individual unit of rendering such as text, circles, polygons, or lines. +// 1) Map layer (ILayer): A single map layer consists of one to many mapbox layers. +// 2) Mapbox layer (MbLayer): Individual unit of rendering such as text, circles, polygons, or lines. -function getIsTextLayer(mbLayer) { +export function getIsTextLayer(mbLayer: MbLayer) { if (mbLayer.type !== 'symbol') { return false; } @@ -28,7 +28,11 @@ function getIsTextLayer(mbLayer) { }); } -function doesMbLayerBelongToMapLayerAndClass(mapLayer: ILayer, mbLayer, layerClass: LAYER_CLASS) { +function doesMbLayerBelongToMapLayerAndClass( + mapLayer: ILayer, + mbLayer: MbLayer, + layerClass: LAYER_CLASS +) { if (!mapLayer.ownsMbLayerId(mbLayer.id)) { return false; } @@ -49,10 +53,10 @@ enum LAYER_CLASS { function moveMapLayer( mbMap: MbMap, - mbLayers: unknown[], + mbLayers: MbLayer[], mapLayer: ILayer, layerClass: LAYER_CLASS, - beneathMbLayerId: string | null + beneathMbLayerId?: string ) { mbLayers .filter((mbLayer) => { @@ -63,11 +67,11 @@ function moveMapLayer( }); } -function getBottomMbLayerId(mbLayers: unknown[], mapLayer: ILayer, layerClass: LAYER_CLASS) { +function getBottomMbLayerId(mbLayers: MbLayer[], mapLayer: ILayer, layerClass: LAYER_CLASS) { const bottomMbLayer = mbLayers.find((mbLayer) => { return doesMbLayerBelongToMapLayerAndClass(mapLayer, mbLayer, layerClass); }); - return bottomMbLayer ? bottomMbLayer.id : null; + return bottomMbLayer ? bottomMbLayer.id : undefined; } function isLayerInOrder( @@ -76,7 +80,7 @@ function isLayerInOrder( layerClass: LAYER_CLASS, beneathMbLayerId: string | null ) { - const mbLayers = mbMap.getStyle().layers; // check ordering against mapbox to account for any upstream moves. + const mbLayers = mbMap.getStyle().layers!; // check ordering against mapbox to account for any upstream moves. if (!beneathMbLayerId) { // Check that map layer is top layer @@ -102,17 +106,17 @@ function isLayerInOrder( return nextMbLayerId === beneathMbLayerId; } -export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerList: ILayer) { - const mbStyle = mbMap.getStyle(); - if (!mbStyle.layers || mbStyle.layers.length === 0) { +export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerList: ILayer[]) { + const mbLayers = mbMap.getStyle().layers; + if (!mbLayers || mbLayers.length === 0) { return; } // Ensure spatial filters layer is the top layer. if (!isLayerInOrder(mbMap, spatialFiltersLayer, LAYER_CLASS.ANY, null)) { - moveMapLayer(mbMap, mbStyle.layers, spatialFiltersLayer, LAYER_CLASS.ANY); + moveMapLayer(mbMap, mbLayers, spatialFiltersLayer, LAYER_CLASS.ANY); } - let beneathMbLayerId = getBottomMbLayerId(mbStyle.layers, spatialFiltersLayer, LAYER_CLASS.ANY); + let beneathMbLayerId = getBottomMbLayerId(mbLayers, spatialFiltersLayer, LAYER_CLASS.ANY); // Sort map layer labels [...layerList] @@ -122,22 +126,17 @@ export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerL }) .forEach((mapLayer: ILayer) => { if (!isLayerInOrder(mbMap, mapLayer, LAYER_CLASS.LABEL, beneathMbLayerId)) { - moveMapLayer(mbMap, mbStyle.layers, mapLayer, LAYER_CLASS.LABEL, beneathMbLayerId); + moveMapLayer(mbMap, mbLayers, mapLayer, LAYER_CLASS.LABEL, beneathMbLayerId); } - beneathMbLayerId = getBottomMbLayerId( - mbStyle.layers, - mapLayer, - LAYER_CLASS.LABEL, - beneathMbLayerId - ); + beneathMbLayerId = getBottomMbLayerId(mbLayers, mapLayer, LAYER_CLASS.LABEL); }); // Sort map layers [...layerList].reverse().forEach((mapLayer: ILayer) => { const layerClass = mapLayer.bubbleLabelsToTop() ? LAYER_CLASS.NON_LABEL : LAYER_CLASS.ANY; if (!isLayerInOrder(mbMap, mapLayer, layerClass, beneathMbLayerId)) { - moveMapLayer(mbMap, mbStyle.layers, mapLayer, layerClass, beneathMbLayerId); + moveMapLayer(mbMap, mbLayers, mapLayer, layerClass, beneathMbLayerId); } - beneathMbLayerId = getBottomMbLayerId(mbStyle.layers, mapLayer, layerClass, beneathMbLayerId); + beneathMbLayerId = getBottomMbLayerId(mbLayers, mapLayer, layerClass); }); } From 777819df941a500c3602237f25515870be8c17fa Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Sun, 28 Jun 2020 09:40:30 -0600 Subject: [PATCH 06/15] tslint --- .../map/mb/sort_layers.test.ts | 24 ++++++++++++------- .../map/mb/sort_layers.ts | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts index b788b02270d41..4562cb533ccdd 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts @@ -9,7 +9,7 @@ import _ from 'lodash'; import { Map as MbMap, Layer as MbLayer, Style as MbStyle } from 'mapbox-gl'; import { getIsTextLayer, syncLayerOrder } from './sort_layers'; import { SPATIAL_FILTERS_LAYER_ID } from '../../../../common/constants'; -import { AbstractLayer } from '../../../classes/layers/layer'; +import { ILayer } from '../../../classes/layers/layer'; let moveCounter = 0; @@ -26,6 +26,11 @@ class MockMbMap { moveLayer(id: string, beforeId?: string) { moveCounter++; + + if (!this._style.layers) { + throw new Error(`Can not move layer, mapbox style does not contain layers`); + } + const layerIndex = this._style.layers.findIndex((layer) => { return layer.id === id; }); @@ -107,11 +112,14 @@ describe('sortLayer', () => { const BRAVO_LAYER_ID = 'bravo'; const CHARLIE_LAYER_ID = 'charlie'; - const spatialFilterLayer = new MockMapLayer(SPATIAL_FILTERS_LAYER_ID, false); + const spatialFilterLayer = (new MockMapLayer( + SPATIAL_FILTERS_LAYER_ID, + false + ) as unknown) as ILayer; const mapLayers = [ - new MockMapLayer(CHARLIE_LAYER_ID, true), - new MockMapLayer(BRAVO_LAYER_ID, false), - new MockMapLayer(ALPHA_LAYER_ID, false), + (new MockMapLayer(CHARLIE_LAYER_ID, true) as unknown) as ILayer, + (new MockMapLayer(BRAVO_LAYER_ID, false) as unknown) as ILayer, + (new MockMapLayer(ALPHA_LAYER_ID, false) as unknown) as ILayer, ]; beforeEach(() => { @@ -137,9 +145,9 @@ describe('sortLayer', () => { ], }; const mbMap = new MockMbMap(initialMbStyle); - syncLayerOrder(mbMap, spatialFilterLayer, mapLayers); + syncLayerOrder((mbMap as unknown) as MbMap, spatialFilterLayer, mapLayers); const sortedMbStyle = mbMap.getStyle(); - const sortedMbLayerIds = sortedMbStyle.layers.map((mbLayer) => { + const sortedMbLayerIds = sortedMbStyle.layers!.map((mbLayer) => { return mbLayer.id; }); expect(sortedMbLayerIds).toEqual([ @@ -173,7 +181,7 @@ describe('sortLayer', () => { ], }; const mbMap = new MockMbMap(initialMbStyle); - syncLayerOrder(mbMap, spatialFilterLayer, mapLayers); + syncLayerOrder((mbMap as unknown) as MbMap, spatialFilterLayer, mapLayers); expect(moveCounter).toBe(0); }); }); diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts index 9d1cd9de8680d..909dee311cd65 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts @@ -78,7 +78,7 @@ function isLayerInOrder( mbMap: MbMap, mapLayer: ILayer, layerClass: LAYER_CLASS, - beneathMbLayerId: string | null + beneathMbLayerId?: string ) { const mbLayers = mbMap.getStyle().layers!; // check ordering against mapbox to account for any upstream moves. @@ -113,7 +113,7 @@ export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerL } // Ensure spatial filters layer is the top layer. - if (!isLayerInOrder(mbMap, spatialFiltersLayer, LAYER_CLASS.ANY, null)) { + if (!isLayerInOrder(mbMap, spatialFiltersLayer, LAYER_CLASS.ANY)) { moveMapLayer(mbMap, mbLayers, spatialFiltersLayer, LAYER_CLASS.ANY); } let beneathMbLayerId = getBottomMbLayerId(mbLayers, spatialFiltersLayer, LAYER_CLASS.ANY); From a84f67c96592895da75f75d666487753064eb174 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Sun, 28 Jun 2020 09:43:29 -0600 Subject: [PATCH 07/15] clean up --- x-pack/plugins/maps/public/classes/layers/layer.tsx | 2 +- .../maps/public/connected_components/map/mb/sort_layers.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index 4a0a9eb14b4bb..c16e952b3c701 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -190,7 +190,7 @@ export class AbstractLayer implements ILayer { async getDisplayName(source?: ISource): Promise { if (this._descriptor.label) { - return `${this._descriptor.label} ${this._descriptor.id}`; + return this._descriptor.label; } const sourceDisplayName = source diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts index 909dee311cd65..6bde1858380bc 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts @@ -7,7 +7,7 @@ import { Map as MbMap, Layer as MbLayer } from 'mapbox-gl'; import { ILayer } from '../../../classes/layers/layer'; -// "Layer" is overloaded. +// "Layer" is overloaded and can mean the following // 1) Map layer (ILayer): A single map layer consists of one to many mapbox layers. // 2) Mapbox layer (MbLayer): Individual unit of rendering such as text, circles, polygons, or lines. From 0ef45b2a225b7775d63cd4d9406055576293fed6 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Sun, 28 Jun 2020 21:31:09 -0600 Subject: [PATCH 08/15] make labelsOnTop configurable --- .../maps/common/descriptor_types/sources.ts | 1 + .../maps/public/actions/layer_actions.ts | 9 ++ .../maps/public/classes/layers/layer.tsx | 9 +- .../vector_tile_layer/vector_tile_layer.js | 6 +- .../layer_settings/{index.js => index.tsx} | 17 +-- .../layer_settings/layer_settings.js | 87 ----------- .../layer_settings/layer_settings.tsx | 135 ++++++++++++++++++ .../map/mb/sort_layers.test.ts | 10 +- .../map/mb/sort_layers.ts | 4 +- 9 files changed, 171 insertions(+), 107 deletions(-) rename x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/{index.js => index.tsx} (71%) delete mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js create mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx diff --git a/x-pack/plugins/maps/common/descriptor_types/sources.ts b/x-pack/plugins/maps/common/descriptor_types/sources.ts index 86ace0e32cc84..304173fbc0723 100644 --- a/x-pack/plugins/maps/common/descriptor_types/sources.ts +++ b/x-pack/plugins/maps/common/descriptor_types/sources.ts @@ -169,6 +169,7 @@ export type LayerDescriptor = { alpha?: number; id: string; label?: string | null; + labelsOnTop?: boolean; minZoom?: number; maxZoom?: number; sourceDescriptor: SourceDescriptor | null; diff --git a/x-pack/plugins/maps/public/actions/layer_actions.ts b/x-pack/plugins/maps/public/actions/layer_actions.ts index 51e251a5d8e20..62fb517deeca7 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.ts @@ -318,6 +318,15 @@ export function updateLayerAlpha(id: string, alpha: number) { }; } +export function updateLabelsOnTop(id: string, labelsOnTop: boolean) { + return { + type: UPDATE_LAYER_PROP, + id, + propName: 'labelsOnTop', + newValue: labelsOnTop, + }; +} + export function setLayerQuery(id: string, query: Query) { return (dispatch: Dispatch) => { dispatch({ diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index c16e952b3c701..e376c4fe58fb0 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -76,7 +76,8 @@ export interface ILayer { getPrevRequestToken(dataId: string): symbol | undefined; destroy: () => void; isPreviewLayer: () => boolean; - bubbleLabelsToTop: () => boolean; + labelsOnTop: () => boolean; + supportsLabelsOnTop: () => boolean; } export type Footnote = { icon: ReactElement; @@ -485,7 +486,11 @@ export class AbstractLayer implements ILayer { return this._descriptor.type; } - bubbleLabelsToTop(): boolean { + labelsOnTop(): boolean { + return false; + } + + supportsLabelsOnTop(): boolean { return false; } } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js index d41b48cb74605..ceea8815c3aaa 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js @@ -278,7 +278,11 @@ export class VectorTileLayer extends TileLayer { }); } - bubbleLabelsToTop() { + labelsOnTop() { + return !!this._descriptor.labelsOnTop; + } + + supportsLabelsOnTop() { return true; } } diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx similarity index 71% rename from x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx index 0d2732184afc4..e27d96912b509 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AnyAction, Dispatch } from 'redux'; import { connect } from 'react-redux'; import { LayerSettings } from './layer_settings'; import { getSelectedLayer } from '../../../selectors/map_selectors'; @@ -12,28 +13,24 @@ import { updateLayerMaxZoom, updateLayerMinZoom, updateLayerAlpha, + updateLabelsOnTop, } from '../../../actions'; -import { MAX_ZOOM } from '../../../../common/constants'; +import { MapStoreState } from '../../../reducers/store'; -function mapStateToProps(state = {}) { +function mapStateToProps(state: MapStoreState) { const selectedLayer = getSelectedLayer(state); return { - minVisibilityZoom: selectedLayer.getMinSourceZoom(), - maxVisibilityZoom: MAX_ZOOM, - alpha: selectedLayer.getAlpha(), - label: selectedLayer.getLabel(), - layerId: selectedLayer.getId(), - maxZoom: selectedLayer.getMaxZoom(), - minZoom: selectedLayer.getMinZoom(), + layer: selectedLayer ? selectedLayer : null, }; } -function mapDispatchToProps(dispatch) { +function mapDispatchToProps(dispatch: Dispatch) { return { updateLabel: (id, label) => dispatch(updateLayerLabel(id, label)), updateMinZoom: (id, minZoom) => dispatch(updateLayerMinZoom(id, minZoom)), updateMaxZoom: (id, maxZoom) => dispatch(updateLayerMaxZoom(id, maxZoom)), updateAlpha: (id, alpha) => dispatch(updateLayerAlpha(id, alpha)), + updateLabelsOnTop: (id, alpha) => dispatch(updateLabelsOnTop(id, alpha)), }; } diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js deleted file mode 100644 index bc99285cfc7aa..0000000000000 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 React, { Fragment } from 'react'; - -import { EuiTitle, EuiPanel, EuiFormRow, EuiFieldText, EuiSpacer } from '@elastic/eui'; - -import { AlphaSlider } from '../../../components/alpha_slider'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { ValidatedDualRange } from '../../../../../../../src/plugins/kibana_react/public'; -export function LayerSettings(props) { - const onLabelChange = (event) => { - const label = event.target.value; - props.updateLabel(props.layerId, label); - }; - - const onZoomChange = ([min, max]) => { - props.updateMinZoom(props.layerId, Math.max(props.minVisibilityZoom, parseInt(min, 10))); - props.updateMaxZoom(props.layerId, Math.min(props.maxVisibilityZoom, parseInt(max, 10))); - }; - - const onAlphaChange = (alpha) => { - props.updateAlpha(props.layerId, alpha); - }; - - const renderZoomSliders = () => { - return ( - - ); - }; - - const renderLabel = () => { - return ( - - - - ); - }; - - return ( - - - -
- -
-
- - - {renderLabel()} - {renderZoomSliders()} - -
- - -
- ); -} diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx new file mode 100644 index 0000000000000..ff39407d24ef1 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx @@ -0,0 +1,135 @@ +/* + * 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 React, { ChangeEvent, Fragment } from 'react'; +import { + EuiTitle, + EuiPanel, + EuiFormRow, + EuiFieldText, + EuiSpacer, + EuiSwitch, + EuiSwitchEvent, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { MAX_ZOOM } from '../../../../common/constants'; +import { AlphaSlider } from '../../../components/alpha_slider'; +import { ValidatedDualRange } from '../../../../../../../src/plugins/kibana_react/public'; + +interface Props { + layer: ILayer | null; + updateLabel: (layerId: string, label: string) => void; + updateMinZoom: (layerId: string, minZoom: number) => void; + updateMaxZoom: (layerId, maxZoom: number) => void; + updateAlpha: (layerId, alpha: number) => void; + updateLabelsOnTop: (layerId, labelsOnTop: boolean) => void; +} + +export function LayerSettings(props: Props) { + if (!props.layer) { + return null; + } + + const minVisibilityZoom = props.layer.getMinSourceZoom(); + const maxVisibilityZoom = MAX_ZOOM; + const onLabelChange = (event: ChangeEvent) => { + const label = event.target.value; + props.updateLabel(props.layer.getId(), label); + }; + + const onZoomChange = (value: [string, string]) => { + props.updateMinZoom(props.layer.getId(), Math.max(minVisibilityZoom, parseInt(value[0], 10))); + props.updateMaxZoom(props.layer.getId(), Math.min(maxVisibilityZoom, parseInt(value[1], 10))); + }; + + const onAlphaChange = (alpha: number) => { + props.updateAlpha(props.layer.getId(), alpha); + }; + + const onLabelsOnTopChange = (event: EuiSwitchEvent) => { + props.updateLabelsOnTop(props.layer.getId(), event.target.checked); + }; + + const renderZoomSliders = () => { + return ( + + ); + }; + + const renderLabel = () => { + return ( + + + + ); + }; + + const renderShowLabelsOnTop = () => { + if (!props.layer.supportsLabelsOnTop()) { + return null; + } + + return ( + + + + ); + }; + + return ( + + + +
+ +
+
+ + + {renderLabel()} + {renderZoomSliders()} + + {renderShowLabelsOnTop()} +
+ + +
+ ); +} diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts index 4562cb533ccdd..cb73898a20cba 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts @@ -61,19 +61,19 @@ class MockMbMap { class MockMapLayer { private readonly _id: string; - private readonly _bubbleLabelsToTop: boolean; + private readonly _labelsOnTop: boolean; - constructor(id: string, bubbleLabelsToTop: boolean) { + constructor(id: string, labelsOnTop: boolean) { this._id = id; - this._bubbleLabelsToTop = bubbleLabelsToTop; + this._labelsOnTop = labelsOnTop; } ownsMbLayerId(mbLayerId: string) { return mbLayerId.startsWith(this._id); } - bubbleLabelsToTop() { - return this._bubbleLabelsToTop; + labelsOnTop() { + return this._labelsOnTop; } getId() { diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts index 6bde1858380bc..03a9edad8cf22 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts @@ -122,7 +122,7 @@ export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerL [...layerList] .reverse() .filter((mapLayer) => { - return mapLayer.bubbleLabelsToTop(); + return mapLayer.labelsOnTop(); }) .forEach((mapLayer: ILayer) => { if (!isLayerInOrder(mbMap, mapLayer, LAYER_CLASS.LABEL, beneathMbLayerId)) { @@ -133,7 +133,7 @@ export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerL // Sort map layers [...layerList].reverse().forEach((mapLayer: ILayer) => { - const layerClass = mapLayer.bubbleLabelsToTop() ? LAYER_CLASS.NON_LABEL : LAYER_CLASS.ANY; + const layerClass = mapLayer.labelsOnTop() ? LAYER_CLASS.NON_LABEL : LAYER_CLASS.ANY; if (!isLayerInOrder(mbMap, mapLayer, layerClass, beneathMbLayerId)) { moveMapLayer(mbMap, mbLayers, mapLayer, layerClass, beneathMbLayerId); } From 18ec7a20dc0bbbe7352d12e3dd61ef420dd757e2 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Sun, 28 Jun 2020 21:40:01 -0600 Subject: [PATCH 09/15] tslint --- .../layer_panel/layer_settings/index.tsx | 11 ++++++----- .../layer_settings/layer_settings.tsx | 19 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx index e27d96912b509..2c28d5d3bdac4 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx @@ -26,11 +26,12 @@ function mapStateToProps(state: MapStoreState) { function mapDispatchToProps(dispatch: Dispatch) { return { - updateLabel: (id, label) => dispatch(updateLayerLabel(id, label)), - updateMinZoom: (id, minZoom) => dispatch(updateLayerMinZoom(id, minZoom)), - updateMaxZoom: (id, maxZoom) => dispatch(updateLayerMaxZoom(id, maxZoom)), - updateAlpha: (id, alpha) => dispatch(updateLayerAlpha(id, alpha)), - updateLabelsOnTop: (id, alpha) => dispatch(updateLabelsOnTop(id, alpha)), + updateLabel: (id: string, label: string) => dispatch(updateLayerLabel(id, label)), + updateMinZoom: (id: string, minZoom: number) => dispatch(updateLayerMinZoom(id, minZoom)), + updateMaxZoom: (id: string, maxZoom: number) => dispatch(updateLayerMaxZoom(id, maxZoom)), + updateAlpha: (id: string, alpha: number) => dispatch(updateLayerAlpha(id, alpha)), + updateLabelsOnTop: (id: string, labelsOnTop: boolean) => + dispatch(updateLabelsOnTop(id, labelsOnTop)), }; } diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx index ff39407d24ef1..0bfa1b9ee9eca 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx @@ -19,14 +19,15 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { MAX_ZOOM } from '../../../../common/constants'; import { AlphaSlider } from '../../../components/alpha_slider'; import { ValidatedDualRange } from '../../../../../../../src/plugins/kibana_react/public'; +import { ILayer } from '../../../classes/layers/layer'; interface Props { layer: ILayer | null; updateLabel: (layerId: string, label: string) => void; updateMinZoom: (layerId: string, minZoom: number) => void; - updateMaxZoom: (layerId, maxZoom: number) => void; - updateAlpha: (layerId, alpha: number) => void; - updateLabelsOnTop: (layerId, labelsOnTop: boolean) => void; + updateMaxZoom: (layerId: string, maxZoom: number) => void; + updateAlpha: (layerId: string, alpha: number) => void; + updateLabelsOnTop: (layerId: string, labelsOnTop: boolean) => void; } export function LayerSettings(props: Props) { @@ -36,22 +37,24 @@ export function LayerSettings(props: Props) { const minVisibilityZoom = props.layer.getMinSourceZoom(); const maxVisibilityZoom = MAX_ZOOM; + const layerId = props.layer.getId(); + const onLabelChange = (event: ChangeEvent) => { const label = event.target.value; - props.updateLabel(props.layer.getId(), label); + props.updateLabel(layerId, label); }; const onZoomChange = (value: [string, string]) => { - props.updateMinZoom(props.layer.getId(), Math.max(minVisibilityZoom, parseInt(value[0], 10))); - props.updateMaxZoom(props.layer.getId(), Math.min(maxVisibilityZoom, parseInt(value[1], 10))); + props.updateMinZoom(layerId, Math.max(minVisibilityZoom, parseInt(value[0], 10))); + props.updateMaxZoom(layerId, Math.min(maxVisibilityZoom, parseInt(value[1], 10))); }; const onAlphaChange = (alpha: number) => { - props.updateAlpha(props.layer.getId(), alpha); + props.updateAlpha(layerId, alpha); }; const onLabelsOnTopChange = (event: EuiSwitchEvent) => { - props.updateLabelsOnTop(props.layer.getId(), event.target.checked); + props.updateLabelsOnTop(layerId, event.target.checked); }; const renderZoomSliders = () => { From 26d86812c8eba1bc53157515e2988ddc3f1f3c4d Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Sun, 28 Jun 2020 21:42:34 -0600 Subject: [PATCH 10/15] more tslint --- .../layer_panel/layer_settings/layer_settings.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx index 0bfa1b9ee9eca..22912b3829154 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx @@ -66,7 +66,7 @@ export function LayerSettings(props: Props) { formRowDisplay="columnCompressed" min={minVisibilityZoom} max={maxVisibilityZoom} - value={[props.layer.getMinZoom(), props.layer.getMaxZoom()]} + value={[props.layer!.getMinZoom(), props.layer!.getMaxZoom()]} showInput="inputWithPopover" showRange showLabels @@ -88,13 +88,13 @@ export function LayerSettings(props: Props) { })} display="columnCompressed" > - + ); }; const renderShowLabelsOnTop = () => { - if (!props.layer.supportsLabelsOnTop()) { + if (!props.layer!.supportsLabelsOnTop()) { return null; } @@ -104,7 +104,7 @@ export function LayerSettings(props: Props) { label={i18n.translate('xpack.maps.layerPanel.settingsPanel.labelsOnTop', { defaultMessage: `Show labels on top`, })} - checked={props.layer.labelsOnTop()} + checked={props.layer!.labelsOnTop()} onChange={onLabelsOnTopChange} data-test-subj="mapLayerPanelApplyGlobalQueryCheckbox" compressed From 1433b12ae83805e250f84e99f6827f6d6123825d Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 Jul 2020 13:04:55 -0600 Subject: [PATCH 11/15] add another test case for single layer move --- .../map/mb/sort_layers.test.ts | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts index cb73898a20cba..3b55122cf09d4 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts @@ -126,7 +126,7 @@ describe('sortLayer', () => { moveCounter = 0; }); - test('Should move layers to expected order', () => { + test('Should initial layer load order to expected order', () => { const initialMbStyle = { version: 0, layers: [ @@ -162,6 +162,42 @@ describe('sortLayer', () => { ]); }); + test('Should move layer single order to expected order', () => { + const initialMbStyle = { + version: 0, + layers: [ + { id: `${CHARLIE_LAYER_ID}_fill`, type: 'fill' } as MbLayer, + { id: `${ALPHA_LAYER_ID}_text`, type: 'symbol' } as MbLayer, + { id: `${ALPHA_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { id: `${BRAVO_LAYER_ID}_text`, type: 'symbol' } as MbLayer, + { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { + id: `${CHARLIE_LAYER_ID}_text`, + type: 'symbol', + paint: { 'text-color': 'red' }, + } as MbLayer, + { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as MbLayer, + { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + ], + }; + const mbMap = new MockMbMap(initialMbStyle); + syncLayerOrder((mbMap as unknown) as MbMap, spatialFilterLayer, mapLayers); + const sortedMbStyle = mbMap.getStyle(); + const sortedMbLayerIds = sortedMbStyle.layers!.map((mbLayer) => { + return mbLayer.id; + }); + expect(sortedMbLayerIds).toEqual([ + 'charlie_fill', + 'bravo_text', + 'bravo_circle', + 'alpha_text', + 'alpha_circle', + 'charlie_text', + 'SPATIAL_FILTERS_LAYER_ID_fill', + 'SPATIAL_FILTERS_LAYER_ID_circle', + ]); + }); + test('Should not call move layers when layers are in expected order', () => { const initialMbStyle = { version: 0, From 9f5c9027f5d9670c9be6e9af95e280de139844bb Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 Jul 2020 13:06:31 -0600 Subject: [PATCH 12/15] clarify should messages --- .../public/connected_components/map/mb/sort_layers.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts index 3b55122cf09d4..56a5f45c87246 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts @@ -126,7 +126,7 @@ describe('sortLayer', () => { moveCounter = 0; }); - test('Should initial layer load order to expected order', () => { + test('Should sort initial layer load order to expected order', () => { const initialMbStyle = { version: 0, layers: [ @@ -162,7 +162,7 @@ describe('sortLayer', () => { ]); }); - test('Should move layer single order to expected order', () => { + test('Should sort single layer single move to expected order', () => { const initialMbStyle = { version: 0, layers: [ From f76b329a029fdd7c2f9308760099a4a838907661 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 Jul 2020 13:49:41 -0600 Subject: [PATCH 13/15] fix assert not null operators --- .../layer_panel/layer_settings/index.tsx | 10 +--------- .../layer_panel/layer_settings/layer_settings.tsx | 14 +++++--------- .../connected_components/layer_panel/view.js | 2 +- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx index 2c28d5d3bdac4..4b3311b4afec6 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx @@ -7,7 +7,6 @@ import { AnyAction, Dispatch } from 'redux'; import { connect } from 'react-redux'; import { LayerSettings } from './layer_settings'; -import { getSelectedLayer } from '../../../selectors/map_selectors'; import { updateLayerLabel, updateLayerMaxZoom, @@ -17,13 +16,6 @@ import { } from '../../../actions'; import { MapStoreState } from '../../../reducers/store'; -function mapStateToProps(state: MapStoreState) { - const selectedLayer = getSelectedLayer(state); - return { - layer: selectedLayer ? selectedLayer : null, - }; -} - function mapDispatchToProps(dispatch: Dispatch) { return { updateLabel: (id: string, label: string) => dispatch(updateLayerLabel(id, label)), @@ -35,5 +27,5 @@ function mapDispatchToProps(dispatch: Dispatch) { }; } -const connectedLayerSettings = connect(mapStateToProps, mapDispatchToProps)(LayerSettings); +const connectedLayerSettings = connect(null, mapDispatchToProps)(LayerSettings); export { connectedLayerSettings as LayerSettings }; diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx index 22912b3829154..35af8f68ff89d 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx @@ -22,7 +22,7 @@ import { ValidatedDualRange } from '../../../../../../../src/plugins/kibana_reac import { ILayer } from '../../../classes/layers/layer'; interface Props { - layer: ILayer | null; + layer: ILayer; updateLabel: (layerId: string, label: string) => void; updateMinZoom: (layerId: string, minZoom: number) => void; updateMaxZoom: (layerId: string, maxZoom: number) => void; @@ -31,10 +31,6 @@ interface Props { } export function LayerSettings(props: Props) { - if (!props.layer) { - return null; - } - const minVisibilityZoom = props.layer.getMinSourceZoom(); const maxVisibilityZoom = MAX_ZOOM; const layerId = props.layer.getId(); @@ -66,7 +62,7 @@ export function LayerSettings(props: Props) { formRowDisplay="columnCompressed" min={minVisibilityZoom} max={maxVisibilityZoom} - value={[props.layer!.getMinZoom(), props.layer!.getMaxZoom()]} + value={[props.layer.getMinZoom(), props.layer.getMaxZoom()]} showInput="inputWithPopover" showRange showLabels @@ -88,13 +84,13 @@ export function LayerSettings(props: Props) { })} display="columnCompressed" > - + ); }; const renderShowLabelsOnTop = () => { - if (!props.layer!.supportsLabelsOnTop()) { + if (!props.layer.supportsLabelsOnTop()) { return null; } @@ -104,7 +100,7 @@ export function LayerSettings(props: Props) { label={i18n.translate('xpack.maps.layerPanel.settingsPanel.labelsOnTop', { defaultMessage: `Show labels on top`, })} - checked={props.layer!.labelsOnTop()} + checked={props.layer.labelsOnTop()} onChange={onLabelsOnTopChange} data-test-subj="mapLayerPanelApplyGlobalQueryCheckbox" compressed diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/view.js b/x-pack/plugins/maps/public/connected_components/layer_panel/view.js index 557fe5fd5f705..71d76ff53d8a9 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/view.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/view.js @@ -205,7 +205,7 @@ export class LayerPanel extends React.Component {
- + {this.props.selectedLayer.renderSourceSettingsEditor({ onChange: this._onSourceChange, From 395d5547a79da403d106c8dc94e6fedfaedafbcc Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 Jul 2020 13:56:49 -0600 Subject: [PATCH 14/15] review feedback --- .../plugins/maps/common/descriptor_types/sources.ts | 2 +- x-pack/plugins/maps/public/actions/layer_actions.ts | 6 +++--- x-pack/plugins/maps/public/classes/layers/layer.tsx | 4 ++-- .../layers/vector_tile_layer/vector_tile_layer.js | 4 ++-- .../layer_panel/layer_settings/index.tsx | 5 ++--- .../layer_panel/layer_settings/layer_settings.tsx | 4 ++-- .../connected_components/map/mb/sort_layers.test.ts | 12 +++++++----- .../connected_components/map/mb/sort_layers.ts | 4 ++-- 8 files changed, 21 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/maps/common/descriptor_types/sources.ts b/x-pack/plugins/maps/common/descriptor_types/sources.ts index 304173fbc0723..e32b5f44c8272 100644 --- a/x-pack/plugins/maps/common/descriptor_types/sources.ts +++ b/x-pack/plugins/maps/common/descriptor_types/sources.ts @@ -169,7 +169,7 @@ export type LayerDescriptor = { alpha?: number; id: string; label?: string | null; - labelsOnTop?: boolean; + areLabelsOnTop?: boolean; minZoom?: number; maxZoom?: number; sourceDescriptor: SourceDescriptor | null; diff --git a/x-pack/plugins/maps/public/actions/layer_actions.ts b/x-pack/plugins/maps/public/actions/layer_actions.ts index 62fb517deeca7..7cfd4681ff095 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.ts @@ -318,12 +318,12 @@ export function updateLayerAlpha(id: string, alpha: number) { }; } -export function updateLabelsOnTop(id: string, labelsOnTop: boolean) { +export function updateLabelsOnTop(id: string, areLabelsOnTop: boolean) { return { type: UPDATE_LAYER_PROP, id, - propName: 'labelsOnTop', - newValue: labelsOnTop, + propName: 'areLabelsOnTop', + newValue: areLabelsOnTop, }; } diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index e376c4fe58fb0..d6f6ee8fa609b 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -76,7 +76,7 @@ export interface ILayer { getPrevRequestToken(dataId: string): symbol | undefined; destroy: () => void; isPreviewLayer: () => boolean; - labelsOnTop: () => boolean; + areLabelsOnTop: () => boolean; supportsLabelsOnTop: () => boolean; } export type Footnote = { @@ -486,7 +486,7 @@ export class AbstractLayer implements ILayer { return this._descriptor.type; } - labelsOnTop(): boolean { + areLabelsOnTop(): boolean { return false; } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js index ceea8815c3aaa..96dad0c01139e 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js @@ -278,8 +278,8 @@ export class VectorTileLayer extends TileLayer { }); } - labelsOnTop() { - return !!this._descriptor.labelsOnTop; + areLabelsOnTop() { + return !!this._descriptor.areLabelsOnTop; } supportsLabelsOnTop() { diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx index 4b3311b4afec6..d2468496fbe0d 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx @@ -14,7 +14,6 @@ import { updateLayerAlpha, updateLabelsOnTop, } from '../../../actions'; -import { MapStoreState } from '../../../reducers/store'; function mapDispatchToProps(dispatch: Dispatch) { return { @@ -22,8 +21,8 @@ function mapDispatchToProps(dispatch: Dispatch) { updateMinZoom: (id: string, minZoom: number) => dispatch(updateLayerMinZoom(id, minZoom)), updateMaxZoom: (id: string, maxZoom: number) => dispatch(updateLayerMaxZoom(id, maxZoom)), updateAlpha: (id: string, alpha: number) => dispatch(updateLayerAlpha(id, alpha)), - updateLabelsOnTop: (id: string, labelsOnTop: boolean) => - dispatch(updateLabelsOnTop(id, labelsOnTop)), + updateLabelsOnTop: (id: string, areLabelsOnTop: boolean) => + dispatch(updateLabelsOnTop(id, areLabelsOnTop)), }; } diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx index 35af8f68ff89d..33d684b320208 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx @@ -27,7 +27,7 @@ interface Props { updateMinZoom: (layerId: string, minZoom: number) => void; updateMaxZoom: (layerId: string, maxZoom: number) => void; updateAlpha: (layerId: string, alpha: number) => void; - updateLabelsOnTop: (layerId: string, labelsOnTop: boolean) => void; + updateLabelsOnTop: (layerId: string, areLabelsOnTop: boolean) => void; } export function LayerSettings(props: Props) { @@ -100,7 +100,7 @@ export function LayerSettings(props: Props) { label={i18n.translate('xpack.maps.layerPanel.settingsPanel.labelsOnTop', { defaultMessage: `Show labels on top`, })} - checked={props.layer.labelsOnTop()} + checked={props.layer.areLabelsOnTop()} onChange={onLabelsOnTopChange} data-test-subj="mapLayerPanelApplyGlobalQueryCheckbox" compressed diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts index 56a5f45c87246..273611e94ee40 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.test.ts @@ -61,19 +61,19 @@ class MockMbMap { class MockMapLayer { private readonly _id: string; - private readonly _labelsOnTop: boolean; + private readonly _areLabelsOnTop: boolean; - constructor(id: string, labelsOnTop: boolean) { + constructor(id: string, areLabelsOnTop: boolean) { this._id = id; - this._labelsOnTop = labelsOnTop; + this._areLabelsOnTop = areLabelsOnTop; } ownsMbLayerId(mbLayerId: string) { return mbLayerId.startsWith(this._id); } - labelsOnTop() { - return this._labelsOnTop; + areLabelsOnTop() { + return this._areLabelsOnTop; } getId() { @@ -126,6 +126,7 @@ describe('sortLayer', () => { moveCounter = 0; }); + // Initial order that styles are added to mapbox is non-deterministic and depends on the order of data fetches. test('Should sort initial layer load order to expected order', () => { const initialMbStyle = { version: 0, @@ -162,6 +163,7 @@ describe('sortLayer', () => { ]); }); + // Test case testing when layer is moved in Table of Contents test('Should sort single layer single move to expected order', () => { const initialMbStyle = { version: 0, diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts index 03a9edad8cf22..4752eeba2376a 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/map/mb/sort_layers.ts @@ -122,7 +122,7 @@ export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerL [...layerList] .reverse() .filter((mapLayer) => { - return mapLayer.labelsOnTop(); + return mapLayer.areLabelsOnTop(); }) .forEach((mapLayer: ILayer) => { if (!isLayerInOrder(mbMap, mapLayer, LAYER_CLASS.LABEL, beneathMbLayerId)) { @@ -133,7 +133,7 @@ export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerL // Sort map layers [...layerList].reverse().forEach((mapLayer: ILayer) => { - const layerClass = mapLayer.labelsOnTop() ? LAYER_CLASS.NON_LABEL : LAYER_CLASS.ANY; + const layerClass = mapLayer.areLabelsOnTop() ? LAYER_CLASS.NON_LABEL : LAYER_CLASS.ANY; if (!isLayerInOrder(mbMap, mapLayer, layerClass, beneathMbLayerId)) { moveMapLayer(mbMap, mbLayers, mapLayer, layerClass, beneathMbLayerId); } From 6692b1919a43f6ebfe02b3c2eb699c60e5d16ed0 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 Jul 2020 15:11:59 -0600 Subject: [PATCH 15/15] update snapshot --- .../layer_panel/__snapshots__/view.test.js.snap | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap b/x-pack/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap index 1620e3058be67..1c48ed2290dce 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap @@ -89,7 +89,19 @@ exports[`LayerPanel is rendered 1`] = ` className="mapLayerPanel__bodyOverflow" > - +
mockSourceSettings