fix merge conflicts (#31702)

This commit is contained in:
Nathan Reese 2019-02-21 10:24:00 -07:00 committed by GitHub
parent 47d838e436
commit 7937f1ae31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 624 additions and 20 deletions

View file

@ -0,0 +1,390 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LayerTocActions is rendered 1`] = `
<EuiPopover
anchorPosition="leftUp"
button={
<EuiButtonEmpty
color="text"
data-test-subj="layerTocActionsPanelToggleButtonlayer1"
flush="left"
iconSide="left"
onClick={[Function]}
size="xs"
type="button"
>
<div>
icon mock
</div>
</EuiButtonEmpty>
}
closePopover={[Function]}
hasArrow={true}
id="contextMenu"
isOpen={false}
ownFocus={false}
panelPaddingSize="none"
withTitle={true}
>
<EuiContextMenu
data-test-subj="layerTocActionsPanellayer1"
initialPanelId={0}
panels={
Array [
Object {
"id": 0,
"items": Array [
Object {
"data-test-subj": "fitToBoundsButton",
"disabled": false,
"icon": <EuiIcon
size="m"
type="search"
/>,
"name": "Fit to data",
"onClick": [Function],
"toolTipContent": null,
},
Object {
"data-test-subj": "layerVisibilityToggleButton",
"icon": <EuiIcon
size="m"
type="eye"
/>,
"name": "Hide layer",
"onClick": [Function],
},
],
"title": "Layer actions",
},
]
}
/>
</EuiPopover>
`;
exports[`LayerTocActions should disable fit to data when supportsFitToBounds is false 1`] = `
<EuiPopover
anchorPosition="leftUp"
button={
<EuiButtonEmpty
color="text"
data-test-subj="layerTocActionsPanelToggleButtonlayer1"
flush="left"
iconSide="left"
onClick={[Function]}
size="xs"
type="button"
>
<div>
icon mock
</div>
</EuiButtonEmpty>
}
closePopover={[Function]}
hasArrow={true}
id="contextMenu"
isOpen={false}
ownFocus={false}
panelPaddingSize="none"
withTitle={true}
>
<EuiContextMenu
data-test-subj="layerTocActionsPanellayer1"
initialPanelId={0}
panels={
Array [
Object {
"id": 0,
"items": Array [
Object {
"data-test-subj": "fitToBoundsButton",
"disabled": true,
"icon": <EuiIcon
size="m"
type="search"
/>,
"name": "Fit to data",
"onClick": [Function],
"toolTipContent": "Layer does not support fit to data",
},
Object {
"data-test-subj": "layerVisibilityToggleButton",
"icon": <EuiIcon
size="m"
type="eye"
/>,
"name": "Hide layer",
"onClick": [Function],
},
],
"title": "Layer actions",
},
]
}
/>
</EuiPopover>
`;
exports[`LayerTocActions should display spinner when layer is loading 1`] = `
<EuiPopover
anchorPosition="leftUp"
button={
<EuiButtonEmpty
color="text"
data-test-subj="layerTocActionsPanelToggleButtonlayer1"
flush="left"
iconSide="left"
onClick={[Function]}
size="xs"
type="button"
>
<EuiLoadingSpinner
size="m"
/>
</EuiButtonEmpty>
}
closePopover={[Function]}
hasArrow={true}
id="contextMenu"
isOpen={false}
ownFocus={false}
panelPaddingSize="none"
withTitle={true}
>
<EuiContextMenu
data-test-subj="layerTocActionsPanellayer1"
initialPanelId={0}
panels={
Array [
Object {
"id": 0,
"items": Array [
Object {
"data-test-subj": "fitToBoundsButton",
"disabled": false,
"icon": <EuiIcon
size="m"
type="search"
/>,
"name": "Fit to data",
"onClick": [Function],
"toolTipContent": null,
},
Object {
"data-test-subj": "layerVisibilityToggleButton",
"icon": <EuiIcon
size="m"
type="eye"
/>,
"name": "Hide layer",
"onClick": [Function],
},
],
"title": "Layer actions",
},
]
}
/>
</EuiPopover>
`;
exports[`LayerTocActions should provide feedback when layer is not visible because of current zoom level 1`] = `
<EuiPopover
anchorPosition="leftUp"
button={
<EuiButtonEmpty
color="text"
data-test-subj="layerTocActionsPanelToggleButtonlayer1"
flush="left"
iconSide="left"
onClick={[Function]}
size="xs"
type="button"
>
<EuiToolTip
content="Map is at zoom level 0.
This layer is only visible between zoom levels 2 to 3."
delay="regular"
position="top"
>
<div>
icon mock
</div>
</EuiToolTip>
</EuiButtonEmpty>
}
closePopover={[Function]}
hasArrow={true}
id="contextMenu"
isOpen={false}
ownFocus={false}
panelPaddingSize="none"
withTitle={true}
>
<EuiContextMenu
data-test-subj="layerTocActionsPanellayer1"
initialPanelId={0}
panels={
Array [
Object {
"id": 0,
"items": Array [
Object {
"data-test-subj": "fitToBoundsButton",
"disabled": false,
"icon": <EuiIcon
size="m"
type="search"
/>,
"name": "Fit to data",
"onClick": [Function],
"toolTipContent": null,
},
Object {
"data-test-subj": "layerVisibilityToggleButton",
"icon": <EuiIcon
size="m"
type="eye"
/>,
"name": "Hide layer",
"onClick": [Function],
},
],
"title": "Layer actions",
},
]
}
/>
</EuiPopover>
`;
exports[`LayerTocActions should show visible toggle when layer is not visible 1`] = `
<EuiPopover
anchorPosition="leftUp"
button={
<EuiButtonEmpty
color="text"
data-test-subj="layerTocActionsPanelToggleButtonlayer1"
flush="left"
iconSide="left"
onClick={[Function]}
size="xs"
type="button"
>
<div>
icon mock
</div>
</EuiButtonEmpty>
}
closePopover={[Function]}
hasArrow={true}
id="contextMenu"
isOpen={false}
ownFocus={false}
panelPaddingSize="none"
withTitle={true}
>
<EuiContextMenu
data-test-subj="layerTocActionsPanellayer1"
initialPanelId={0}
panels={
Array [
Object {
"id": 0,
"items": Array [
Object {
"data-test-subj": "fitToBoundsButton",
"disabled": false,
"icon": <EuiIcon
size="m"
type="search"
/>,
"name": "Fit to data",
"onClick": [Function],
"toolTipContent": null,
},
Object {
"data-test-subj": "layerVisibilityToggleButton",
"icon": <EuiIcon
size="m"
type="eyeClosed"
/>,
"name": "Show layer",
"onClick": [Function],
},
],
"title": "Layer actions",
},
]
}
/>
</EuiPopover>
`;
exports[`LayerTocActions should show warning when layer has errors 1`] = `
<EuiPopover
anchorPosition="leftUp"
button={
<EuiButtonEmpty
color="text"
data-test-subj="layerTocActionsPanelToggleButtonlayer1"
flush="left"
iconSide="left"
onClick={[Function]}
size="xs"
type="button"
>
<EuiIconTip
aria-label="Load warning"
color="warning"
content="simulated layer error"
size="m"
type="alert"
/>
</EuiButtonEmpty>
}
closePopover={[Function]}
hasArrow={true}
id="contextMenu"
isOpen={false}
ownFocus={false}
panelPaddingSize="none"
withTitle={true}
>
<EuiContextMenu
data-test-subj="layerTocActionsPanellayer1"
initialPanelId={0}
panels={
Array [
Object {
"id": 0,
"items": Array [
Object {
"data-test-subj": "fitToBoundsButton",
"disabled": false,
"icon": <EuiIcon
size="m"
type="search"
/>,
"name": "Fit to data",
"onClick": [Function],
"toolTipContent": null,
},
Object {
"data-test-subj": "layerVisibilityToggleButton",
"icon": <EuiIcon
size="m"
type="eye"
/>,
"name": "Hide layer",
"onClick": [Function],
},
],
"title": "Layer actions",
},
]
}
/>
</EuiPopover>
`;

View file

@ -41,9 +41,26 @@ function cleanDisplayName(displayName) {
export class LayerTocActions extends Component {
state = {
isPopoverOpen: false
isPopoverOpen: false,
supportsFitToBounds: false,
};
componentDidMount() {
this._isMounted = true;
this._loadSupportsFitToBounds();
}
componentWillUnmount() {
this._isMounted = false;
}
async _loadSupportsFitToBounds() {
const supportsFitToBounds = await this.props.layer.supportsFitToBounds();
if (this._isMounted) {
this.setState({ supportsFitToBounds });
}
}
_onClick = () => {
this.setState(prevState => ({
isPopoverOpen: !prevState.isPopoverOpen,
@ -128,6 +145,9 @@ export class LayerTocActions extends Component {
size="m"
/>
),
'data-test-subj': 'fitToBoundsButton',
toolTipContent: this.state.supportsFitToBounds ? null : 'Layer does not support fit to data',
disabled: !this.state.supportsFitToBounds,
onClick: () => {
this._closePopover();
this.props.fitToBounds();

View file

@ -0,0 +1,143 @@
/*
* 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 from 'react';
import { shallow } from 'enzyme';
import { LayerTocActions } from './layer_toc_actions';
let supportsFitToBounds;
let isLayerLoading;
let isVisible;
let hasErrors;
let showAtZoomLevel;
const layerMock = {
supportsFitToBounds: () => { return supportsFitToBounds; },
isVisible: () => { return isVisible; },
hasErrors: () => { return hasErrors; },
getErrors: () => { return 'simulated layer error'; },
isLayerLoading: () => { return isLayerLoading; },
showAtZoomLevel: () => { return showAtZoomLevel; },
getZoomConfig: () => { return { minZoom: 2, maxZoom: 3 }; },
getIcon: () => { return (<div>icon mock</div>); },
};
const defaultProps = {
displayName: 'layer1',
zoom: 0,
layer: layerMock,
};
describe('LayerTocActions', () => {
beforeEach(() => {
supportsFitToBounds = true;
isLayerLoading = false;
isVisible = true;
hasErrors = false;
showAtZoomLevel = true;
});
test('is rendered', async () => {
const component = shallow(
<LayerTocActions
{...defaultProps}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
// Ensure the state changes are reflected
component.update();
expect(component)
.toMatchSnapshot();
});
test('should disable fit to data when supportsFitToBounds is false', async () => {
supportsFitToBounds = false;
const component = shallow(
<LayerTocActions
{...defaultProps}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
// Ensure the state changes are reflected
component.update();
expect(component)
.toMatchSnapshot();
});
test('should display spinner when layer is loading', async () => {
isLayerLoading = true;
const component = shallow(
<LayerTocActions
{...defaultProps}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
// Ensure the state changes are reflected
component.update();
expect(component)
.toMatchSnapshot();
});
test('should show warning when layer has errors', async () => {
hasErrors = true;
const component = shallow(
<LayerTocActions
{...defaultProps}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
// Ensure the state changes are reflected
component.update();
expect(component)
.toMatchSnapshot();
});
test('should show visible toggle when layer is not visible', async () => {
isVisible = false;
const component = shallow(
<LayerTocActions
{...defaultProps}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
// Ensure the state changes are reflected
component.update();
expect(component)
.toMatchSnapshot();
});
test('should provide feedback when layer is not visible because of current zoom level', async () => {
showAtZoomLevel = false;
const component = shallow(
<LayerTocActions
{...defaultProps}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
// Ensure the state changes are reflected
component.update();
expect(component)
.toMatchSnapshot();
});
});

View file

@ -53,6 +53,10 @@ export class AbstractLayer {
return this._source.isJoinable();
}
async supportsFitToBounds() {
return await this._source.supportsFitToBounds();
}
async getDisplayName() {
if (this._descriptor.label) {
return this._descriptor.label;

View file

@ -78,7 +78,7 @@ export class AbstractESSource extends AbstractVectorSource {
return searchSource;
}
async getBoundsForFilters({ query, timeFilters }, layerName) {
async getBoundsForFilters({ query, timeFilters }) {
const searchSource = await this._makeSearchSource({ query, timeFilters }, 0);
const geoField = await this._getGeoField();
@ -96,15 +96,29 @@ export class AbstractESSource extends AbstractVectorSource {
const aggConfigs = new AggConfigs(indexPattern, geoBoundsAgg);
searchSource.setField('aggs', aggConfigs.toDsl());
const esResp = await this._runEsQuery(layerName, searchSource, 'bounds request');
const esBounds = _.get(esResp, 'aggregations.1.bounds');
return (esBounds) ?
{
min_lon: esBounds.top_left.lon,
max_lon: esBounds.bottom_right.lon,
min_lat: esBounds.bottom_right.lat,
max_lat: esBounds.top_left.lat
} : null;
let esBounds;
try {
const esResp = await searchSource.fetch();
esBounds = _.get(esResp, 'aggregations.1.bounds');
} catch(error) {
esBounds = {
top_left: {
lat: 90,
lon: -180
},
bottom_right: {
lat: -90,
lon: 180
}
};
}
return {
min_lon: esBounds.top_left.lon,
max_lon: esBounds.bottom_right.lon,
min_lat: esBounds.bottom_right.lat,
max_lat: esBounds.top_left.lat
};
}
async isTimeAware() {
@ -130,6 +144,18 @@ export class AbstractESSource extends AbstractVectorSource {
}
}
async supportsFitToBounds() {
try {
const geoField = await this._getGeoField();
// geo_bounds aggregation only supports geo_point
// there is currently no backend support for getting bounding box of geo_shape field
return geoField.type !== 'geo_shape';
} catch (error) {
return false;
}
}
async _getGeoField() {
const indexPattern = await this._getIndexPattern();
const geoField = indexPattern.fields.byName[this._descriptor.geoField];

View file

@ -24,6 +24,10 @@ export class AbstractSource {
destroy() {}
async supportsFitToBounds() {
return true;
}
/**
* return list of immutable source properties.
* Immutable source properties are properties that can not be edited by the user.

View file

@ -39,9 +39,16 @@ export default function ({ getPageObjects, getService }) {
expect(beforeRefreshTimerTimestamp).not.to.equal(afterRefreshTimerTimestamp);
});
describe('inspector', () => {
it('should register elasticsearch request in inspector', async () => {
const hits = await getHits();
expect(hits).to.equal('6');
});
});
describe('query bar', () => {
before(async () => {
await PageObjects.maps.setAndSubmitQuery('machine.os.raw : "win 8"');
await PageObjects.maps.setAndSubmitQuery('machine.os.raw : "win 8" OR machine.os.raw : "ios"');
});
after(async () => {
@ -53,7 +60,7 @@ export default function ({ getPageObjects, getService }) {
const requestStats = await inspector.getTableData();
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
await inspector.close();
expect(hits).to.equal('1');
expect(hits).to.equal('3');
});
it('should re-fetch query when "refresh" is clicked', async () => {
@ -62,12 +69,15 @@ export default function ({ getPageObjects, getService }) {
const afterQueryRefreshTimestamp = await getRequestTimestamp();
expect(beforeQueryRefreshTimestamp).not.to.equal(afterQueryRefreshTimestamp);
});
});
describe('inspector', () => {
it('should register elasticsearch request in inspector', async () => {
const hits = await getHits();
expect(hits).to.equal('6');
it('should apply query to fit to bounds', async () => {
// Set view to other side of world so no matching results
await PageObjects.maps.setView(-15, -100, 6);
await PageObjects.maps.clickFitToBounds('logstash');
const { lat, lon, zoom } = await PageObjects.maps.getView();
expect(Math.round(lat)).to.equal(41);
expect(Math.round(lon)).to.equal(-102);
expect(Math.round(zoom)).to.equal(5);
});
});

View file

@ -107,7 +107,7 @@
"title" : "document example",
"description" : "",
"mapStateJSON" : "{\"zoom\":4.1,\"center\":{\"lon\":-100.61091,\"lat\":33.23887},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}",
"layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{\"alphaValue\":1}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"z52lq\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"e1a5e1a6-676c-4a89-8ea9-0d91d64b73c6\",\"type\":\"ES_SEARCH\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"alphaValue\":1},\"previousStyle\":null},\"type\":\"VECTOR\"}]",
"layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{\"alphaValue\":1}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"z52lq\",\"label\":\"logstash\",\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"e1a5e1a6-676c-4a89-8ea9-0d91d64b73c6\",\"type\":\"ES_SEARCH\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"alphaValue\":1},\"previousStyle\":null},\"type\":\"VECTOR\"}]",
"uiStateJSON" : "{\"isDarkMode\":false}",
"bounds" : {
"type" : "envelope",
@ -390,4 +390,4 @@
}
}
}
}
}

View file

@ -175,6 +175,13 @@ export function GisPageProvider({ getService, getPageObjects }) {
await testSubjects.click('layerVisibilityToggleButton');
}
async clickFitToBounds(layerName) {
log.debug(`Fit to bounds, layer: ${layerName}`);
await this.openLayerTocActionsPanel(layerName);
await testSubjects.click('fitToBoundsButton');
await this.waitForLayersToLoad();
}
async openLayerTocActionsPanel(layerName) {
const cleanLayerName = layerName.split(' ').join('');
const isOpen = await testSubjects.exists(`layerTocActionsPanel${cleanLayerName}`);