From cebc6e58f9a5139ad7da2078ab539d1d9584579b Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Tue, 28 Apr 2020 17:14:44 -0400 Subject: [PATCH 1/6] init commit --- .../maps/public/actions/map_actions.d.ts | 4 ++ .../maps/public/actions/map_actions.js | 50 +++++++++++++++++++ .../fit_to_data/fit_to_data.tsx | 44 ++++++++++++++++ .../toolbar_overlay/fit_to_data/index.ts | 29 +++++++++++ .../toolbar_overlay/toolbar_overlay.js | 5 ++ .../maps/public/selectors/map_selectors.d.ts | 5 +- .../maps/public/selectors/map_selectors.js | 13 +++++ 7 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/index.ts diff --git a/x-pack/plugins/maps/public/actions/map_actions.d.ts b/x-pack/plugins/maps/public/actions/map_actions.d.ts index c8db284a5c4f1..77be13b3ef2fd 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.d.ts +++ b/x-pack/plugins/maps/public/actions/map_actions.d.ts @@ -13,6 +13,7 @@ import { MapFilters, MapCenterAndZoom, MapRefreshConfig, + MapExtent, } from '../../common/descriptor_types'; import { MapSettings } from '../reducers/map'; @@ -34,6 +35,9 @@ export function updateSourceProp( ): void; export function setGotoWithCenter(config: MapCenterAndZoom): AnyAction; +export function setGotoWithBounds(config: MapExtent): AnyAction; + +export function fitToDataBounds(): AnyAction; export function replaceLayerList(layerList: unknown[]): AnyAction; diff --git a/x-pack/plugins/maps/public/actions/map_actions.js b/x-pack/plugins/maps/public/actions/map_actions.js index da6ba6b481054..ea2602397702b 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.js +++ b/x-pack/plugins/maps/public/actions/map_actions.js @@ -19,6 +19,7 @@ import { getOpenTooltips, getQuery, getDataRequestDescriptor, + getFittableLayers, } from '../selectors/map_selectors'; import { FLYOUT_STATE } from '../reducers/ui'; @@ -567,6 +568,55 @@ export function fitToLayerExtent(layerId) { }; } +export function fitToDataBounds() { + return async function(dispatch, getState) { + const layerList = getFittableLayers(getState()); + + if (!layerList.length) { + return; + } + + const dataFilters = getDataFilters(getState()); + const boundsPromises = layerList.map(async layer => { + return layer.getBounds(dataFilters); + }); + + const bounds = await Promise.all(boundsPromises); + const corners = []; + for (let i = 0; i < bounds.length; i++) { + const b = bounds[i]; + + //filter out undefined bounds (uses Infinity due to turf responses) + + if ( + b.minLon === Infinity || + b.maxLon === Infinity || + b.minLat === -Infinity || + b.maxLat === -Infinity + ) { + continue; + } + + corners.push([b.minLon, b.minLat]); + corners.push([b.maxLon, b.maxLat]); + } + + if (!corners.length) { + return; + } + + const turfUnionBbox = turf.bbox(turf.multiPoint(corners)); + const dataBounds = { + minLon: turfUnionBbox[0], + minLat: turfUnionBbox[1], + maxLon: turfUnionBbox[2], + maxLat: turfUnionBbox[3], + }; + + dispatch(setGotoWithBounds(dataBounds)); + }; +} + export function setGotoWithBounds(bounds) { return { type: SET_GOTO, diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx new file mode 100644 index 0000000000000..cfca19c24256e --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx @@ -0,0 +1,44 @@ +/* + * 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, { Component } from 'react'; + +import { EuiButtonIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ILayer } from '../../../../../../layers/layer'; + +interface Props { + layerList: ILayer[]; + fitToBounds: () => void; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface State {} + +// eslint-disable-next-line react/prefer-stateless-function +export class FitToData extends Component { + render() { + if (this.props.layerList.length === 0) { + return null; + } + + return ( + + ); + } +} diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/index.ts b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/index.ts new file mode 100644 index 0000000000000..d6ded62f2f480 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/index.ts @@ -0,0 +1,29 @@ +/* + * 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 { AnyAction, Dispatch } from 'redux'; +import { connect } from 'react-redux'; +import { MapStoreState } from '../../../reducers/store'; +import { fitToDataBounds } from '../../../actions/map_actions'; +import { getFittableLayers } from '../../../selectors/map_selectors'; +import { FitToData } from './fit_to_data'; + +function mapStateToProps(state: MapStoreState) { + return { + layerList: getFittableLayers(state), + }; +} + +function mapDispatchToProps(dispatch: Dispatch) { + return { + fitToBounds: () => { + dispatch(fitToDataBounds()); + }, + }; +} + +const connectedFitToData = connect(mapStateToProps, mapDispatchToProps)(FitToData); +export { connectedFitToData as FitToData }; diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js index 32668be8f8f67..1468c4b5f771f 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js @@ -8,6 +8,7 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { SetViewControl } from './set_view_control'; import { ToolsControl } from './tools_control'; +import { FitToData } from './fit_to_data'; export class ToolbarOverlay extends React.Component { _renderToolsControl() { @@ -37,6 +38,10 @@ export class ToolbarOverlay extends React.Component { {this._renderToolsControl()} + + + + ); } diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.d.ts b/x-pack/plugins/maps/public/selectors/map_selectors.d.ts index 4d0f652af982a..3132a27942f54 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.d.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.d.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AnyAction } from 'redux'; import { MapCenter } from '../../common/descriptor_types'; import { MapStoreState } from '../reducers/store'; import { MapSettings } from '../reducers/map'; import { IVectorLayer } from '../layers/vector_layer'; +import { ILayer } from '../layers/layer'; export function getHiddenLayerIds(state: MapStoreState): string[]; @@ -23,3 +23,6 @@ export function getMapSettings(state: MapStoreState): MapSettings; export function hasMapSettingsChanges(state: MapStoreState): boolean; export function getSpatialFiltersLayer(state: MapStoreState): IVectorLayer; + +export function getLayerList(state: MapStoreState): ILayer[]; +export function getFittableLayers(state: MapStoreState): ILayer[]; diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.js b/x-pack/plugins/maps/public/selectors/map_selectors.js index f43c92d4c9945..b4ea91e22087b 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/plugins/maps/public/selectors/map_selectors.js @@ -251,6 +251,19 @@ export const getLayerList = createSelector( } ); +export const getFittableLayers = createSelector(getLayerList, layerList => { + return layerList.filter(layer => { + //These are the only layer-types that implement bounding-box retrieval reliably + //This will _not_ work if Maps will allow register custom layer types + const isFittable = + layer.constructor.type === LAYER_TYPE.VECTOR || + layer.constructor.type === LAYER_TYPE.BLENDED_VECTOR || + layer.constructor.type === LAYER_TYPE.HEATMAP; + + return isFittable && layer.isVisible(); + }); +}); + export const getHiddenLayerIds = createSelector(getLayerListRaw, layers => layers.filter(layer => !layer.visible).map(layer => layer.id) ); From a87972b8cbd8ba78d1ce3f0b4955a24c27d2bdcf Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Sun, 3 May 2020 23:53:51 -0400 Subject: [PATCH 2/6] feedback --- .../toolbar_overlay.test.tsx.snap | 44 ++++++++++++++++++ .../toolbar_overlay/_index.scss | 5 ++ .../fit_to_data/fit_to_data.tsx | 46 ++++++++----------- .../toolbar_overlay/toolbar_overlay.js | 4 +- .../toolbar_overlay/toolbar_overlay.test.tsx | 20 ++++++++ .../maps/public/selectors/map_selectors.js | 6 +-- 6 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/__snapshots__/toolbar_overlay.test.tsx.snap create mode 100644 x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/__snapshots__/toolbar_overlay.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/__snapshots__/toolbar_overlay.test.tsx.snap new file mode 100644 index 0000000000000..3407bcfd4f845 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/__snapshots__/toolbar_overlay.test.tsx.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Must render zoom tools 1`] = ` + + + + + + + + +`; + +exports[`Must zoom tools and draw filter tools 1`] = ` + + + + + + + + + + + +`; diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss index 2754a3e204263..712bd76c9e8e2 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss @@ -13,6 +13,11 @@ background-color: $euiColorEmptyShade !important; pointer-events: all; + display: inline-block; + position: relative; + vertical-align: middle; + max-width: 100%; + &:enabled, &:enabled:hover, &:enabled:focus { diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx index cfca19c24256e..1ad385879e0ce 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx @@ -15,30 +15,24 @@ interface Props { fitToBounds: () => void; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface State {} - -// eslint-disable-next-line react/prefer-stateless-function -export class FitToData extends Component { - render() { - if (this.props.layerList.length === 0) { - return null; - } - - return ( - - ); +export const FitToData: React.FunctionComponent = ({ layerList, fitToBounds }: Props) => { + if (layerList.length === 0) { + return null; } -} + + return ( + + ); +}; diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js index 1468c4b5f771f..a4f85163512f7 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js @@ -37,11 +37,11 @@ export class ToolbarOverlay extends React.Component { - {this._renderToolsControl()} - + + {this._renderToolsControl()} ); } diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx new file mode 100644 index 0000000000000..f527193167652 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx @@ -0,0 +1,20 @@ +/* + * 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 { ToolbarOverlay } from './toolbar_overlay'; + +test('Must render zoom tools', async () => { + const component = shallow(); + expect(component).toMatchSnapshot(); +}); + +test('Must zoom tools and draw filter tools', async () => { + const component = shallow( {}} geoFields={['coordinates']} />); + expect(component).toMatchSnapshot(); +}); diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.js b/x-pack/plugins/maps/public/selectors/map_selectors.js index b4ea91e22087b..38a862973623a 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/plugins/maps/public/selectors/map_selectors.js @@ -256,9 +256,9 @@ export const getFittableLayers = createSelector(getLayerList, layerList => { //These are the only layer-types that implement bounding-box retrieval reliably //This will _not_ work if Maps will allow register custom layer types const isFittable = - layer.constructor.type === LAYER_TYPE.VECTOR || - layer.constructor.type === LAYER_TYPE.BLENDED_VECTOR || - layer.constructor.type === LAYER_TYPE.HEATMAP; + layer.getType() === LAYER_TYPE.VECTOR || + layer.getType() === LAYER_TYPE.BLENDED_VECTOR || + layer.getType() === LAYER_TYPE.HEATMAP; return isFittable && layer.isVisible(); }); From 409a9840b3a2519067978171debdeeb5ab2d5db7 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Sun, 3 May 2020 23:55:37 -0400 Subject: [PATCH 3/6] type check --- .../toolbar_overlay/fit_to_data/fit_to_data.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx index 1ad385879e0ce..d368a30470dea 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component } from 'react'; +import React from 'react'; import { EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ILayer } from '../../../../../../layers/layer'; +import { ILayer } from '../../../layers/layer'; interface Props { layerList: ILayer[]; From 5e1772ac656a4d438cfa0cf26c70f6999998f02c Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Mon, 4 May 2020 09:45:45 -0400 Subject: [PATCH 4/6] ignore js file --- .../toolbar_overlay/toolbar_overlay.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx index f527193167652..c03aa1e098540 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { shallow } from 'enzyme'; +// @ts-ignore import { ToolbarOverlay } from './toolbar_overlay'; test('Must render zoom tools', async () => { From df3ab1ebe3e557dcbd42a652f7722a71b003dbc2 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Mon, 4 May 2020 12:20:19 -0400 Subject: [PATCH 5/6] simplify styling --- .../public/connected_components/toolbar_overlay/_index.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss index 712bd76c9e8e2..e92e89b170370 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss @@ -12,11 +12,7 @@ // sass-lint:disable-block no-important background-color: $euiColorEmptyShade !important; pointer-events: all; - - display: inline-block; position: relative; - vertical-align: middle; - max-width: 100%; &:enabled, &:enabled:hover, From a2bcf945cc51a9a72ebe1f07e49ba63efdc2c24a Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Mon, 4 May 2020 16:35:21 -0400 Subject: [PATCH 6/6] modify icon --- .../toolbar_overlay/fit_to_data/fit_to_data.tsx | 2 +- .../__snapshots__/toc_entry_actions_popover.test.tsx.snap | 6 +++--- .../toc_entry_actions_popover/toc_entry_actions_popover.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx index d368a30470dea..0b168badb2f3f 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/fit_to_data/fit_to_data.tsx @@ -25,7 +25,7 @@ export const FitToData: React.FunctionComponent = ({ layerList, fitToBoun className="mapToolbarOverlay__button" onClick={fitToBounds} data-test-subj="fitToData" - iconType="search" + iconType="expand" color="text" aria-label={i18n.translate('xpack.maps.fitToData.fitButtonLabel', { defaultMessage: 'Fit to data bounds', diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap index b8c652909408a..388712e1ebcca 100644 --- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap @@ -72,7 +72,7 @@ exports[`TOCEntryActionsPopover is rendered 1`] = ` "disabled": false, "icon": , "name": "Fit to data", "onClick": [Function], @@ -200,7 +200,7 @@ exports[`TOCEntryActionsPopover should disable fit to data when supportsFitToBou "disabled": true, "icon": , "name": "Fit to data", "onClick": [Function], @@ -328,7 +328,7 @@ exports[`TOCEntryActionsPopover should not show edit actions in read only mode 1 "disabled": false, "icon": , "name": "Fit to data", "onClick": [Function], diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx index d628cca61de11..2f639f595a9be 100644 --- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx @@ -137,7 +137,7 @@ export class TOCEntryActionsPopover extends Component { name: i18n.translate('xpack.maps.layerTocActions.fitToDataTitle', { defaultMessage: 'Fit to data', }), - icon: , + icon: , 'data-test-subj': 'fitToBoundsButton', toolTipContent: this.state.supportsFitToBounds ? null