From d589bc81c05c5cd9168cc28e797745102c23afec Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 20 Aug 2020 10:19:05 -0600 Subject: [PATCH 01/13] [Maps] add drilldown support --- .../common/descriptor_types/map_descriptor.ts | 1 + .../public/components/_action_select.scss | 3 + .../maps/public/components/_index.scss | 1 + .../maps/public/components/action_select.tsx | 97 +++++++++++++++++++ .../components/distance_filter_form.tsx | 18 +++- .../public/components/geometry_filter_form.js | 22 +++++ .../feature_geometry_filter_form.js | 1 + .../features_tooltip/feature_properties.js | 86 +++++++++++----- .../map/features_tooltip/features_tooltip.js | 2 + .../map/mb/draw_control/draw_control.js | 25 +++-- .../map/mb/tooltip_control/tooltip_control.js | 1 + .../map/mb/tooltip_control/tooltip_popover.js | 1 + .../connected_components/map/mb/view.js | 9 +- .../map_container/map_container.tsx | 8 +- .../toolbar_overlay/toolbar_overlay.js | 4 +- .../tools_control/tools_control.js | 3 + .../maps/public/embeddable/map_embeddable.tsx | 47 ++++++++- 17 files changed, 283 insertions(+), 46 deletions(-) create mode 100644 x-pack/plugins/maps/public/components/_action_select.scss create mode 100644 x-pack/plugins/maps/public/components/action_select.tsx diff --git a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts index ed0656a2fc265..d064dfb1c4a37 100644 --- a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts +++ b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts @@ -53,6 +53,7 @@ export type TooltipState = { }; export type DrawState = { + actionId: string; drawType: DRAW_TYPE; filterLabel?: string; // point radius filter alias geoFieldName?: string; diff --git a/x-pack/plugins/maps/public/components/_action_select.scss b/x-pack/plugins/maps/public/components/_action_select.scss new file mode 100644 index 0000000000000..be903ad7d6962 --- /dev/null +++ b/x-pack/plugins/maps/public/components/_action_select.scss @@ -0,0 +1,3 @@ +.mapActionSelectIcon { + margin-right: $euiSizeS; +} diff --git a/x-pack/plugins/maps/public/components/_index.scss b/x-pack/plugins/maps/public/components/_index.scss index 76e27338bdcd4..76ce9f1bc79e3 100644 --- a/x-pack/plugins/maps/public/components/_index.scss +++ b/x-pack/plugins/maps/public/components/_index.scss @@ -1,3 +1,4 @@ +@import 'action_select'; @import 'metric_editors'; @import './geometry_filter'; @import 'tooltip_selector/tooltip_selector'; diff --git a/x-pack/plugins/maps/public/components/action_select.tsx b/x-pack/plugins/maps/public/components/action_select.tsx new file mode 100644 index 0000000000000..ff863e1a6f120 --- /dev/null +++ b/x-pack/plugins/maps/public/components/action_select.tsx @@ -0,0 +1,97 @@ +/* + * 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 { EuiFormRow, EuiSuperSelect, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; + +export const ADD_FILTER_MAPS_ACTION = 'ADD_FILTER_MAPS_ACTION'; + +interface Props { + value?: string; + onChange: (value: string) => void; + getFilterActions?: () => Promise; +} + +interface State { + actions: UiActionsActionDefinition[]; +} + +export class ActionSelect extends Component { + private _isMounted = false; + state: State = { + actions: [], + }; + + componentDidMount() { + this._isMounted = true; + this._loadActions(); + } + + componentWillUnmount() { + this._isMounted = false; + } + + async _loadActions() { + if (!this.props.getFilterActions) { + return; + } + const actions = await this.props.getFilterActions(); + if (this._isMounted) { + this.setState({ actions }); + } + } + + render() { + if (this.state.actions.length === 0) { + return null; + } + + const options = [ + { + value: ADD_FILTER_MAPS_ACTION, + inputDisplay: ( +
+ + +
+ ), + }, + ...this.state.actions.map((action) => { + return { + value: action.id, + inputDisplay: ( +
+ + {action.getDisplayName()} +
+ ), + }; + }), + ]; + + return ( + + + + ); + } +} diff --git a/x-pack/plugins/maps/public/components/distance_filter_form.tsx b/x-pack/plugins/maps/public/components/distance_filter_form.tsx index 768be1558bd69..d131cebeadb7b 100644 --- a/x-pack/plugins/maps/public/components/distance_filter_form.tsx +++ b/x-pack/plugins/maps/public/components/distance_filter_form.tsx @@ -14,13 +14,16 @@ import { EuiTextAlign, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; import { MultiIndexGeoFieldSelect } from './multi_index_geo_field_select'; import { GeoFieldWithIndex } from './geo_field_with_index'; +import { ActionSelect, ADD_FILTER_MAPS_ACTION } from './action_select'; interface Props { className?: string; buttonLabel: string; geoFields: GeoFieldWithIndex[]; + getFilterActions?: () => Promise; onSubmit: ({ filterLabel, indexPatternId, @@ -33,12 +36,14 @@ interface Props { } interface State { + actionId: string; selectedField: GeoFieldWithIndex | undefined; filterLabel: string; } export class DistanceFilterForm extends Component { - state = { + state: State = { + actionId: ADD_FILTER_MAPS_ACTION, selectedField: this.props.geoFields.length ? this.props.geoFields[0] : undefined, filterLabel: '', }; @@ -53,11 +58,16 @@ export class DistanceFilterForm extends Component { }); }; + _onActionIdChange = (value) => { + this.setState({ actionId: value }); + }; + _onSubmit = () => { if (!this.state.selectedField) { return; } this.props.onSubmit({ + actionId: this.state.actionId, filterLabel: this.state.filterLabel, indexPatternId: this.state.selectedField.indexPatternId, geoFieldName: this.state.selectedField.geoFieldName, @@ -86,6 +96,12 @@ export class DistanceFilterForm extends Component { onChange={this._onGeoFieldChange} /> + + diff --git a/x-pack/plugins/maps/public/components/geometry_filter_form.js b/x-pack/plugins/maps/public/components/geometry_filter_form.js index d5cdda3c1c324..9d3d53c8baca0 100644 --- a/x-pack/plugins/maps/public/components/geometry_filter_form.js +++ b/x-pack/plugins/maps/public/components/geometry_filter_form.js @@ -20,11 +20,13 @@ import { i18n } from '@kbn/i18n'; import { ES_GEO_FIELD_TYPE, ES_SPATIAL_RELATIONS } from '../../common/constants'; import { getEsSpatialRelationLabel } from '../../common/i18n_getters'; import { MultiIndexGeoFieldSelect } from './multi_index_geo_field_select'; +import { ActionSelect, ADD_FILTER_MAPS_ACTION } from './action_select'; export class GeometryFilterForm extends Component { static propTypes = { buttonLabel: PropTypes.string.isRequired, geoFields: PropTypes.array.isRequired, + getFilterActions: PropTypes.func, intitialGeometryLabel: PropTypes.string.isRequired, onSubmit: PropTypes.func.isRequired, isFilterGeometryClosed: PropTypes.bool, @@ -36,11 +38,20 @@ export class GeometryFilterForm extends Component { }; state = { + actionId: ADD_FILTER_MAPS_ACTION, selectedField: this.props.geoFields.length ? this.props.geoFields[0] : undefined, geometryLabel: this.props.intitialGeometryLabel, relation: ES_SPATIAL_RELATIONS.INTERSECTS, }; + componentDidMount() { + this._isMounted = true; + } + + componentWillUnmount() { + this._isMounted = false; + } + _onGeoFieldChange = (selectedField) => { this.setState({ selectedField }); }; @@ -57,8 +68,13 @@ export class GeometryFilterForm extends Component { }); }; + _onActionIdChange = (value) => { + this.setState({ actionId: value }); + }; + _onSubmit = () => { this.props.onSubmit({ + actionId: this.state.actionId, geometryLabel: this.state.geometryLabel, indexPatternId: this.state.selectedField.indexPatternId, geoFieldName: this.state.selectedField.geoFieldName, @@ -134,6 +150,12 @@ export class GeometryFilterForm extends Component { {this._renderRelationInput()} + + {error} diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js index e1779c1afbf47..4ddabc3f841bc 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js @@ -126,6 +126,7 @@ export class FeatureGeometryFilterForm extends Component { } )} geoFields={this.props.geoFields} + getFilterActions={this.props.getFilterActions} intitialGeometryLabel={this.props.geometry.type.toLowerCase()} onSubmit={this._createFilter} isFilterGeometryClosed={ diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js index 5e2a153b2ccbf..b4fe02d88e961 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js @@ -5,12 +5,19 @@ */ import React from 'react'; -import { EuiCallOut, EuiLoadingSpinner, EuiTextAlign, EuiButtonIcon } from '@elastic/eui'; +import { + EuiCallOut, + EuiLoadingSpinner, + EuiTextAlign, + EuiButtonIcon, + EuiButtonEmpty, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; export class FeatureProperties extends React.Component { state = { properties: null, + actions: [], loadPropertiesErrorMsg: null, prevWidth: null, prevHeight: null, @@ -21,6 +28,7 @@ export class FeatureProperties extends React.Component { this.prevLayerId = undefined; this.prevFeatureId = undefined; this._loadProperties(); + this._loadActions(); } componentDidUpdate() { @@ -31,6 +39,16 @@ export class FeatureProperties extends React.Component { this._isMounted = false; } + async _loadActions() { + if (!this.props.getFilterActions) { + return; + } + const actions = await this.props.getFilterActions(); + if (this._isMounted) { + this.setState({ actions }); + } + } + _loadProperties = async () => { this._fetchProperties({ nextFeatureId: this.props.featureId, @@ -83,9 +101,7 @@ export class FeatureProperties extends React.Component { } if (this._isMounted) { - this.setState({ - properties, - }); + this.setState({ properties }); } }; @@ -94,26 +110,48 @@ export class FeatureProperties extends React.Component { return null; } - return ( - - { - this.props.onCloseTooltip(); - const filters = await tooltipProperty.getESFilters(); - this.props.addFilters(filters); - }} - aria-label={i18n.translate('xpack.maps.tooltip.filterOnPropertyAriaLabel', { - defaultMessage: 'Filter on property', - })} - data-test-subj="mapTooltipCreateFilterButton" - /> - - ); + const actionButtons = [ + { + this.props.onCloseTooltip(); + const filters = await tooltipProperty.getESFilters(); + this.props.addFilters(filters); + }} + aria-label={i18n.translate('xpack.maps.tooltip.filterOnPropertyAriaLabel', { + defaultMessage: 'Filter on property', + })} + data-test-subj="mapTooltipCreateFilterButton" + />, + ]; + + if (this.state.actions.length) { + this.state.actions.forEach((action) => { + actionButtons.push( +
+ { + this.props.onCloseTooltip(); + const filters = await tooltipProperty.getESFilters(); + this.props.addFilters(filters, action.id); + }} + data-test-subj={`mapTooltipAddFilterButton_${action.id}`} + > + {action.getDisplayName()} + +
+ ); + }); + } + + return {actionButtons}; } render() { diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js index d91bc8e803ab9..e46de0b40a179 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js @@ -115,6 +115,7 @@ export class FeaturesTooltip extends React.Component { geometry={currentFeatureGeometry} geoFields={geoFields} addFilters={this.props.addFilters} + getFilterActions={this.props.getFilterActions} loadPreIndexedShape={this._loadCurrentFeaturePreIndexedShape} /> ); @@ -137,6 +138,7 @@ export class FeaturesTooltip extends React.Component { showFilterButtons={!!this.props.addFilters && this.props.isLocked} onCloseTooltip={this.props.closeTooltip} addFilters={this.props.addFilters} + getFilterActions={this.props.getFilterActions} /> {this._renderActions(geoFields)} diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js index 2daa4b2c900f5..cf0f94f8c870d 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js @@ -62,11 +62,12 @@ export class DrawControl extends React.Component { } }, 0); - _onDraw = (e) => { + _onDraw = async (e) => { if (!e.features.length) { return; } + let filter; if (this.props.drawState.drawType === DRAW_TYPE.DISTANCE) { const circle = e.features[0]; const distanceKm = _.round( @@ -82,7 +83,7 @@ export class DrawControl extends React.Component { } else if (distanceKm <= 100) { precision = 3; } - const filter = createDistanceFilterWithMeta({ + filter = createDistanceFilterWithMeta({ alias: this.props.drawState.filterLabel, distanceKm, geoFieldName: this.props.drawState.geoFieldName, @@ -92,17 +93,12 @@ export class DrawControl extends React.Component { _.round(circle.properties.center[1], precision), ], }); - this.props.addFilters([filter]); - this.props.disableDrawState(); - return; - } - - const geometry = e.features[0].geometry; - // MapboxDraw returns coordinates with 12 decimals. Round to a more reasonable number - roundCoordinates(geometry.coordinates); + } else { + const geometry = e.features[0].geometry; + // MapboxDraw returns coordinates with 12 decimals. Round to a more reasonable number + roundCoordinates(geometry.coordinates); - try { - const filter = createSpatialFilterWithGeometry({ + filter = createSpatialFilterWithGeometry({ geometry: this.props.drawState.drawType === DRAW_TYPE.BOUNDS ? getBoundingBoxGeometry(geometry) @@ -113,7 +109,10 @@ export class DrawControl extends React.Component { geometryLabel: this.props.drawState.geometryLabel, relation: this.props.drawState.relation, }); - this.props.addFilters([filter]); + } + + try { + await this.props.addFilters([filter], this.props.drawState.actionId); } catch (error) { // TODO notify user why filter was not created console.error(error); diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js index 84a29db852539..91e1c12bce572 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js @@ -195,6 +195,7 @@ export class TooltipControl extends React.Component { mbMap={this.props.mbMap} layerList={this.props.layerList} addFilters={this.props.addFilters} + getFilterActions={this.props.getFilterActions} renderTooltipContent={this.props.renderTooltipContent} geoFields={this.props.geoFields} features={features} diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js index 6c42057680408..537e135625449 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js @@ -117,6 +117,7 @@ export class TooltipPopover extends Component { _renderTooltipContent = () => { const publicProps = { addFilters: this.props.addFilters, + getFilterActions: this.props.getFilterActions, closeTooltip: this.props.closeTooltip, features: this.props.features, isLocked: this.props.isLocked, 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 d85959c3a08a4..8c8ad721544a2 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 @@ -304,11 +304,18 @@ export class MBMap extends React.Component { let drawControl; let tooltipControl; if (this.state.mbMap) { - drawControl = ; + drawControl = ( + + ); tooltipControl = !this.props.disableTooltipControl ? ( diff --git a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx index beb1eb0947c50..529845d1f7446 100644 --- a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx @@ -183,6 +183,7 @@ export class MapContainer extends Component { render() { const { addFilters, + getFilterActions, flyoutDisplay, isFullScreen, exitFullScreen, @@ -230,11 +231,16 @@ export class MapContainer extends Component { {!this.props.hideToolbarOverlay && ( - + )} 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 a4f85163512f7..338cbc2e71eb3 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 @@ -12,14 +12,14 @@ import { FitToData } from './fit_to_data'; export class ToolbarOverlay extends React.Component { _renderToolsControl() { - const { addFilters, geoFields } = this.props; + const { addFilters, geoFields, getFilterActions } = this.props; if (!addFilters || !geoFields.length) { return null; } return ( - + ); } diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js index a06def086b861..a331aa70f7816 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js @@ -123,6 +123,7 @@ export class ToolsControl extends Component { className="mapDrawControl__geometryFilterForm" buttonLabel={DRAW_SHAPE_LABEL_SHORT} geoFields={this.props.geoFields} + getFilterActions={this.props.getFilterActions} intitialGeometryLabel={i18n.translate( 'xpack.maps.toolbarOverlay.drawShape.initialGeometryLabel', { @@ -141,6 +142,7 @@ export class ToolsControl extends Component { className="mapDrawControl__geometryFilterForm" buttonLabel={DRAW_BOUNDS_LABEL_SHORT} geoFields={this.props.geoFields} + getFilterActions={this.props.getFilterActions} intitialGeometryLabel={i18n.translate( 'xpack.maps.toolbarOverlay.drawBounds.initialGeometryLabel', { @@ -161,6 +163,7 @@ export class ToolsControl extends Component { geoFields={this.props.geoFields.filter(({ geoFieldType }) => { return geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT; })} + getFilterActions={this.props.getFilterActions} onSubmit={this._initiateDistanceDraw} /> ), diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 43ff274b1353f..cb544764f0112 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -11,7 +11,10 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { Subscription } from 'rxjs'; import { Unsubscribe } from 'redux'; import { Embeddable, IContainer } from '../../../../../src/plugins/embeddable/public'; -import { APPLY_FILTER_TRIGGER } from '../../../../../src/plugins/ui_actions/public'; +import { + APPLY_FILTER_TRIGGER, + TriggerContextMapping, +} from '../../../../../src/plugins/ui_actions/public'; import { esFilters, TimeRange, @@ -50,6 +53,7 @@ import { RenderToolTipContent } from '../classes/tooltips/tooltip_property'; import { getUiActions, getCoreI18n } from '../kibana_services'; import { LayerDescriptor } from '../../common/descriptor_types'; import { MapContainer } from '../connected_components/map_container'; +import { ADD_FILTER_MAPS_ACTION } from '../components/action_select'; import { MapEmbeddableConfig, MapEmbeddableInput, MapEmbeddableOutput } from './types'; export { MapEmbeddableInput, MapEmbeddableConfig }; @@ -69,6 +73,7 @@ export class MapEmbeddable extends Embeddable this.onContainerStateChanged(input)); + this._actionContext = { + embeddable: this, + }; + } + + supportedTriggers(): Array { + return [APPLY_FILTER_TRIGGER]; } setRenderTooltipContent = (renderTooltipContent: RenderToolTipContent) => { @@ -226,6 +238,7 @@ export class MapEmbeddable extends Embeddable @@ -243,13 +256,39 @@ export class MapEmbeddable extends Embeddable(replaceLayerList(this._layerList)); } - addFilters = (filters: Filter[]) => { - getUiActions().executeTriggerActions(APPLY_FILTER_TRIGGER, { + addFilters = async (filters: Filter[], actionId: string = ADD_FILTER_MAPS_ACTION) => { + if (actionId === ADD_FILTER_MAPS_ACTION) { + const applyFilterAction = getUiActions().getAction('ACTION_APPLY_FILTER'); + if (!applyFilterAction) { + throw new Error('Unable to apply filter, could not locate action'); + } + applyFilterAction.execute(this.getFilterActionContext(filters)); + return; + } + + const action = (await this.getFilterActions()).find((filterAction) => { + return filterAction.id === actionId; + }); + if (!action) { + throw new Error('Unable to apply filter, could not locate action'); + } + action.execute(this.getFilterActionContext(filters)); + }; + + getFilterActions = async () => { + return await getUiActions().getTriggerCompatibleActions('FILTER_TRIGGER', { embeddable: this, - filters, }); }; + getFilterActionContext = (filters) => { + return { + embeddable: this, + filters, + trigger: { id: 'FILTER_TRIGGER' }, + }; + }; + destroy() { super.destroy(); if (this._unsubscribeFromStore) { From d9e2bd3276b08149a88eaac29367696e5a41e772 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 20 Aug 2020 12:28:17 -0600 Subject: [PATCH 02/13] filter actions view --- x-pack/plugins/maps/common/i18n_getters.ts | 6 + .../feature_geometry_filter_form.js | 36 +--- .../features_tooltip/feature_properties.js | 171 ++++++++++++++---- .../map/features_tooltip/features_tooltip.js | 71 ++++++-- 4 files changed, 197 insertions(+), 87 deletions(-) diff --git a/x-pack/plugins/maps/common/i18n_getters.ts b/x-pack/plugins/maps/common/i18n_getters.ts index 0008a119f1c7c..e3b5df6628ce4 100644 --- a/x-pack/plugins/maps/common/i18n_getters.ts +++ b/x-pack/plugins/maps/common/i18n_getters.ts @@ -50,3 +50,9 @@ export function getEsSpatialRelationLabel(spatialRelation: $Values - - - - - - - - - ); - } - - _renderForm() { + render() { return ( ); } - - render() { - return ( - - {this._renderHeader()} - {this._renderForm()} - - ); - } } diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js index b4fe02d88e961..199c490cfba1f 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js @@ -9,10 +9,14 @@ import { EuiCallOut, EuiLoadingSpinner, EuiTextAlign, - EuiButtonIcon, EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiContextMenu, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { getApplyFilterLabel } from '../../../../common/i18n_getters'; export class FeatureProperties extends React.Component { state = { @@ -57,6 +61,10 @@ export class FeatureProperties extends React.Component { }); }; + _showFilterActions = (tooltipProperty) => { + this.props.showFilterActions(this._renderFilterActions(tooltipProperty)); + }; + _fetchProperties = async ({ nextLayerId, nextFeatureId, mbProperties }) => { if (this.prevLayerId === nextLayerId && this.prevFeatureId === nextFeatureId) { // do not reload same feature properties @@ -105,53 +113,138 @@ export class FeatureProperties extends React.Component { } }; + _renderFilterActions(tooltipProperty) { + const panel = { + id: 0, + items: [ + { + name: getApplyFilterLabel(), + icon: , + onClick: async () => { + this.props.onCloseTooltip(); + const filters = await tooltipProperty.getESFilters(); + this.props.addFilters(filters); + }, + }, + ...this.state.actions.map((action) => { + return { + name: action.getDisplayName(), + icon: , + onClick: async () => { + this.props.onCloseTooltip(); + const filters = await tooltipProperty.getESFilters(); + this.props.addFilters(filters, action.id); + }, + }; + }), + ], + }; + + return ( +
+ (this._node = node)} + > + + + + + +
+ {tooltipProperty.getPropertyName()} + +
+ +
+ ); + } + _renderFilterCell(tooltipProperty) { if (!this.props.showFilterButtons || !tooltipProperty.isFilterable()) { return null; } - const actionButtons = [ - { - this.props.onCloseTooltip(); - const filters = await tooltipProperty.getESFilters(); - this.props.addFilters(filters); - }} - aria-label={i18n.translate('xpack.maps.tooltip.filterOnPropertyAriaLabel', { - defaultMessage: 'Filter on property', - })} - data-test-subj="mapTooltipCreateFilterButton" - />, - ]; + const applyFilterButton = ( + + { + this.props.onCloseTooltip(); + const filters = await tooltipProperty.getESFilters(); + this.props.addFilters(filters); + }} + aria-label={i18n.translate('xpack.maps.tooltip.filterOnPropertyAriaLabel', { + defaultMessage: 'Filter on property', + })} + data-test-subj="mapTooltipCreateFilterButton" + /> + + ); + let applyFirstFilterAction; if (this.state.actions.length) { - this.state.actions.forEach((action) => { - actionButtons.push( -
- { - this.props.onCloseTooltip(); - const filters = await tooltipProperty.getESFilters(); - this.props.addFilters(filters, action.id); - }} - data-test-subj={`mapTooltipAddFilterButton_${action.id}`} - > - {action.getDisplayName()} - -
- ); - }); + const action = this.state.actions[0]; + applyFirstFilterAction = ( + + { + this.props.onCloseTooltip(); + const filters = await tooltipProperty.getESFilters(); + this.props.addFilters(filters, action.id); + }} + > + {action.getDisplayName()} + + + ); } - return {actionButtons}; + let showMoreFilterActions; + if (this.state.actions.length > 1) { + showMoreFilterActions = ( + + { + this._showFilterActions(tooltipProperty); + }} + aria-label={i18n.translate('xpack.maps.tooltip.moreActionsTitle', { + defaultMessage: 'More filter actions', + })} + /> + + ); + } + + return ( + + + {applyFilterButton} + {applyFirstFilterAction} + {showMoreFilterActions} + + + ); } render() { diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js index e46de0b40a179..82aa2def0d6f5 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js @@ -4,20 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; -import { EuiLink } from '@elastic/eui'; +import React, { Component, Fragment } from 'react'; +import { EuiIcon, EuiLink } from '@elastic/eui'; import { FeatureProperties } from './feature_properties'; -import { FormattedMessage } from '@kbn/i18n/react'; import { GEO_JSON_TYPE, ES_GEO_FIELD_TYPE } from '../../../../common/constants'; import { FeatureGeometryFilterForm } from './feature_geometry_filter_form'; import { TooltipHeader } from './tooltip_header'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; const VIEWS = { PROPERTIES_VIEW: 'PROPERTIES_VIEW', GEOMETRY_FILTER_VIEW: 'GEOMETRY_FILTER_VIEW', + FILTER_ACTIONS_VIEW: 'FILTER_ACTIONS_VIEW', }; -export class FeaturesTooltip extends React.Component { +export class FeaturesTooltip extends Component { state = {}; static getDerivedStateFromProps(nextProps, prevState) { @@ -41,7 +43,11 @@ export class FeaturesTooltip extends React.Component { }; _showPropertiesView = () => { - this.setState({ view: VIEWS.PROPERTIES_VIEW }); + this.setState({ view: VIEWS.PROPERTIES_VIEW, filterView: null }); + }; + + _showFilterActionsView = (filterView) => { + this.setState({ view: VIEWS.FILTER_ACTIONS_VIEW, filterView }); }; _renderActions(geoFields) { @@ -96,6 +102,22 @@ export class FeaturesTooltip extends React.Component { }); }; + _renderBackButton(label) { + return ( + + ); + } + render() { if (!this.state.currentFeature) { return null; @@ -109,15 +131,35 @@ export class FeaturesTooltip extends React.Component { if (this.state.view === VIEWS.GEOMETRY_FILTER_VIEW && currentFeatureGeometry) { return ( - + + {this._renderBackButton( + i18n.translate('xpack.maps.tooltip.showGeometryFilterViewLinkLabel', { + defaultMessage: 'Filter by geometry', + }) + )} + + + ); + } + + if (this.state.view === VIEWS.FILTER_ACTIONS_VIEW) { + return ( + + {this._renderBackButton( + i18n.translate('xpack.maps.tooltip.showAddFilterActionsViewLabel', { + defaultMessage: 'Filter actions', + }) + )} + {this.state.filterView} + ); } @@ -139,6 +181,7 @@ export class FeaturesTooltip extends React.Component { onCloseTooltip={this.props.closeTooltip} addFilters={this.props.addFilters} getFilterActions={this.props.getFilterActions} + showFilterActions={this._showFilterActionsView} /> {this._renderActions(geoFields)} From ef9cd159f56242798a2189b8ef8355e541d17a2f Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 20 Aug 2020 13:12:47 -0600 Subject: [PATCH 03/13] use i18n getter --- x-pack/plugins/maps/public/components/action_select.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/maps/public/components/action_select.tsx b/x-pack/plugins/maps/public/components/action_select.tsx index ff863e1a6f120..d0ba891868998 100644 --- a/x-pack/plugins/maps/public/components/action_select.tsx +++ b/x-pack/plugins/maps/public/components/action_select.tsx @@ -7,8 +7,8 @@ import React, { Component } from 'react'; import { EuiFormRow, EuiSuperSelect, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; import { UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; +import { getApplyFilterLabel } from '../../common/i18n_getters'; export const ADD_FILTER_MAPS_ACTION = 'ADD_FILTER_MAPS_ACTION'; @@ -58,10 +58,7 @@ export class ActionSelect extends Component { inputDisplay: (
- + {getApplyFilterLabel()}
), }, From aa418b64442a57b7b947eeb5551a461bcc83017e Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 20 Aug 2020 13:41:11 -0600 Subject: [PATCH 04/13] remove unused changed --- .../maps/public/components/geometry_filter_form.js | 8 -------- x-pack/plugins/maps/public/embeddable/map_embeddable.tsx | 4 ---- 2 files changed, 12 deletions(-) diff --git a/x-pack/plugins/maps/public/components/geometry_filter_form.js b/x-pack/plugins/maps/public/components/geometry_filter_form.js index 9d3d53c8baca0..25ea232fffca6 100644 --- a/x-pack/plugins/maps/public/components/geometry_filter_form.js +++ b/x-pack/plugins/maps/public/components/geometry_filter_form.js @@ -44,14 +44,6 @@ export class GeometryFilterForm extends Component { relation: ES_SPATIAL_RELATIONS.INTERSECTS, }; - componentDidMount() { - this._isMounted = true; - } - - componentWillUnmount() { - this._isMounted = false; - } - _onGeoFieldChange = (selectedField) => { this.setState({ selectedField }); }; diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index cb544764f0112..94d930feab90a 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -73,7 +73,6 @@ export class MapEmbeddable extends Embeddable this.onContainerStateChanged(input)); - this._actionContext = { - embeddable: this, - }; } supportedTriggers(): Array { From 20673fcf205f4d8e5659220c7ab022498ba15fce Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 20 Aug 2020 14:00:13 -0600 Subject: [PATCH 05/13] tslint fixes --- x-pack/plugins/maps/public/components/action_select.tsx | 5 +++-- .../plugins/maps/public/components/distance_filter_form.tsx | 4 +++- .../connected_components/map_container/map_container.tsx | 4 +++- x-pack/plugins/maps/public/embeddable/map_embeddable.tsx | 3 +-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/maps/public/components/action_select.tsx b/x-pack/plugins/maps/public/components/action_select.tsx index d0ba891868998..0899f7421c316 100644 --- a/x-pack/plugins/maps/public/components/action_select.tsx +++ b/x-pack/plugins/maps/public/components/action_select.tsx @@ -22,7 +22,7 @@ interface State { actions: UiActionsActionDefinition[]; } -export class ActionSelect extends Component { +export class ActionSelect extends Component { private _isMounted = false; state: State = { actions: [], @@ -63,11 +63,12 @@ export class ActionSelect extends Component { ), }, ...this.state.actions.map((action) => { + const iconType = action.getIconType(); return { value: action.id, inputDisplay: (
- + {iconType ? : null} {action.getDisplayName()}
), diff --git a/x-pack/plugins/maps/public/components/distance_filter_form.tsx b/x-pack/plugins/maps/public/components/distance_filter_form.tsx index d131cebeadb7b..93901a696d3dc 100644 --- a/x-pack/plugins/maps/public/components/distance_filter_form.tsx +++ b/x-pack/plugins/maps/public/components/distance_filter_form.tsx @@ -25,10 +25,12 @@ interface Props { geoFields: GeoFieldWithIndex[]; getFilterActions?: () => Promise; onSubmit: ({ + actionId, filterLabel, indexPatternId, geoFieldName, }: { + actionId: string; filterLabel: string; indexPatternId: string; geoFieldName: string; @@ -58,7 +60,7 @@ export class DistanceFilterForm extends Component { }); }; - _onActionIdChange = (value) => { + _onActionIdChange = (value: string) => { this.setState({ actionId: value }); }; diff --git a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx index 529845d1f7446..892b21f396976 100644 --- a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import uuid from 'uuid/v4'; import { Filter } from 'src/plugins/data/public'; +import { UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; // @ts-expect-error import { MBMap } from '../map/mb'; // @ts-expect-error @@ -35,7 +36,8 @@ import 'mapbox-gl/dist/mapbox-gl.css'; const RENDER_COMPLETE_EVENT = 'renderComplete'; interface Props { - addFilters: ((filters: Filter[]) => void) | null; + addFilters: ((filters: Filter[]) => Promise) | null; + getFilterActions?: () => Promise; areLayersLoaded: boolean; cancelAllInFlightRequests: () => void; exitFullScreen: () => void; diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 94d930feab90a..f308e8952040c 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -277,11 +277,10 @@ export class MapEmbeddable extends Embeddable { + getFilterActionContext = (filters: Filter[]) => { return { embeddable: this, filters, - trigger: { id: 'FILTER_TRIGGER' }, }; }; From a129eac1a0af05bbecf5fc8d3ec306258fc43283 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 21 Aug 2020 09:35:09 -0600 Subject: [PATCH 06/13] more tslint changes --- src/plugins/data/public/index.ts | 2 +- x-pack/plugins/maps/common/i18n_getters.ts | 6 --- .../maps/public/components/action_select.tsx | 44 ++++++++----------- .../components/distance_filter_form.tsx | 9 ++-- .../public/components/geometry_filter_form.js | 7 ++- .../feature_geometry_filter_form.js | 1 + .../features_tooltip/feature_properties.js | 41 +++++++---------- .../map/features_tooltip/features_tooltip.js | 2 + .../map/mb/tooltip_control/tooltip_control.js | 1 + .../map/mb/tooltip_control/tooltip_popover.js | 1 + .../connected_components/map/mb/view.js | 9 +--- .../map_container/map_container.tsx | 6 ++- .../toolbar_overlay/toolbar_overlay.js | 8 +++- .../tools_control/tools_control.js | 3 ++ .../maps/public/embeddable/map_embeddable.tsx | 31 ++++++++----- 15 files changed, 89 insertions(+), 82 deletions(-) diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index ecf076aa517fb..572f84b8f88c9 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -441,7 +441,7 @@ export { export { isTimeRange, isQuery, isFilter, isFilters } from '../common'; -export { ApplyGlobalFilterActionContext } from './actions'; +export { ACTION_GLOBAL_APPLY_FILTER, ApplyGlobalFilterActionContext } from './actions'; export * from '../common/field_mapping'; diff --git a/x-pack/plugins/maps/common/i18n_getters.ts b/x-pack/plugins/maps/common/i18n_getters.ts index e3b5df6628ce4..0008a119f1c7c 100644 --- a/x-pack/plugins/maps/common/i18n_getters.ts +++ b/x-pack/plugins/maps/common/i18n_getters.ts @@ -50,9 +50,3 @@ export function getEsSpatialRelationLabel(spatialRelation: $Values void; getFilterActions?: () => Promise; + getActionContext?: () => ActionExecutionContext; } interface State { @@ -38,7 +36,7 @@ export class ActionSelect extends Component { } async _loadActions() { - if (!this.props.getFilterActions) { + if (!this.props.getFilterActions || !this.props.getActionContext) { return; } const actions = await this.props.getFilterActions(); @@ -48,33 +46,27 @@ export class ActionSelect extends Component { } render() { - if (this.state.actions.length === 0) { + if (this.state.actions.length === 0 || !this.props.getActionContext) { + return null; + } + + if (this.state.actions.length === 1 && this.props.value === this.state.actions[0].id) { return null; } - const options = [ - { - value: ADD_FILTER_MAPS_ACTION, + const actionContext = this.props.getActionContext(); + const options = this.state.actions.map((action) => { + const iconType = action.getIconType ? action.getIconType(actionContext) : null; + return { + value: action.id, inputDisplay: (
- - {getApplyFilterLabel()} + {iconType ? : null} + {action.getDisplayName ? action.getDisplayName(actionContext) : action.id}
), - }, - ...this.state.actions.map((action) => { - const iconType = action.getIconType(); - return { - value: action.id, - inputDisplay: ( -
- {iconType ? : null} - {action.getDisplayName()} -
- ), - }; - }), - ]; + }; + }); return ( { diff --git a/x-pack/plugins/maps/public/components/distance_filter_form.tsx b/x-pack/plugins/maps/public/components/distance_filter_form.tsx index 93901a696d3dc..24b046f39eed6 100644 --- a/x-pack/plugins/maps/public/components/distance_filter_form.tsx +++ b/x-pack/plugins/maps/public/components/distance_filter_form.tsx @@ -14,16 +14,18 @@ import { EuiTextAlign, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; +import { ActionExecutionContext, UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; import { MultiIndexGeoFieldSelect } from './multi_index_geo_field_select'; import { GeoFieldWithIndex } from './geo_field_with_index'; -import { ActionSelect, ADD_FILTER_MAPS_ACTION } from './action_select'; +import { ActionSelect } from './action_select'; +import { ACTION_GLOBAL_APPLY_FILTER } from '../../../../../src/plugins/data/public'; interface Props { className?: string; buttonLabel: string; geoFields: GeoFieldWithIndex[]; getFilterActions?: () => Promise; + getActionContext?: () => ActionExecutionContext; onSubmit: ({ actionId, filterLabel, @@ -45,7 +47,7 @@ interface State { export class DistanceFilterForm extends Component { state: State = { - actionId: ADD_FILTER_MAPS_ACTION, + actionId: ACTION_GLOBAL_APPLY_FILTER, selectedField: this.props.geoFields.length ? this.props.geoFields[0] : undefined, filterLabel: '', }; @@ -100,6 +102,7 @@ export class DistanceFilterForm extends Component { diff --git a/x-pack/plugins/maps/public/components/geometry_filter_form.js b/x-pack/plugins/maps/public/components/geometry_filter_form.js index 25ea232fffca6..fde07e8c16bc5 100644 --- a/x-pack/plugins/maps/public/components/geometry_filter_form.js +++ b/x-pack/plugins/maps/public/components/geometry_filter_form.js @@ -20,13 +20,15 @@ import { i18n } from '@kbn/i18n'; import { ES_GEO_FIELD_TYPE, ES_SPATIAL_RELATIONS } from '../../common/constants'; import { getEsSpatialRelationLabel } from '../../common/i18n_getters'; import { MultiIndexGeoFieldSelect } from './multi_index_geo_field_select'; -import { ActionSelect, ADD_FILTER_MAPS_ACTION } from './action_select'; +import { ActionSelect } from './action_select'; +import { ACTION_GLOBAL_APPLY_FILTER } from '../../../../../src/plugins/data/public'; export class GeometryFilterForm extends Component { static propTypes = { buttonLabel: PropTypes.string.isRequired, geoFields: PropTypes.array.isRequired, getFilterActions: PropTypes.func, + getActionContext: PropTypes.func, intitialGeometryLabel: PropTypes.string.isRequired, onSubmit: PropTypes.func.isRequired, isFilterGeometryClosed: PropTypes.bool, @@ -38,7 +40,7 @@ export class GeometryFilterForm extends Component { }; state = { - actionId: ADD_FILTER_MAPS_ACTION, + actionId: ACTION_GLOBAL_APPLY_FILTER, selectedField: this.props.geoFields.length ? this.props.geoFields[0] : undefined, geometryLabel: this.props.intitialGeometryLabel, relation: ES_SPATIAL_RELATIONS.INTERSECTS, @@ -144,6 +146,7 @@ export class GeometryFilterForm extends Component { diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js index 59d6929c6434c..23861ef5ceca8 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js @@ -104,6 +104,7 @@ export class FeatureGeometryFilterForm extends Component { )} geoFields={this.props.geoFields} getFilterActions={this.props.getFilterActions} + getActionContext={this.props.getActionContext} intitialGeometryLabel={this.props.geometry.type.toLowerCase()} onSubmit={this._createFilter} isFilterGeometryClosed={ diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js index 199c490cfba1f..93ac3df1f0c4a 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js @@ -16,7 +16,7 @@ import { EuiContextMenu, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { getApplyFilterLabel } from '../../../../common/i18n_getters'; +import { ACTION_GLOBAL_APPLY_FILTER } from '../../../../../../../src/plugins/data/public'; export class FeatureProperties extends React.Component { state = { @@ -116,28 +116,19 @@ export class FeatureProperties extends React.Component { _renderFilterActions(tooltipProperty) { const panel = { id: 0, - items: [ - { - name: getApplyFilterLabel(), - icon: , + items: this.state.actions.map((action) => { + const actionContext = this.props.getActionContext(); + const iconType = action.getIconType ? action.getIconType(actionContext) : null; + return { + name: action.getDisplayName ? action.getDisplayName(actionContext) : action.id, + icon: iconType ? : null, onClick: async () => { this.props.onCloseTooltip(); const filters = await tooltipProperty.getESFilters(); - this.props.addFilters(filters); + this.props.addFilters(filters, action.id); }, - }, - ...this.state.actions.map((action) => { - return { - name: action.getDisplayName(), - icon: , - onClick: async () => { - this.props.onCloseTooltip(); - const filters = await tooltipProperty.getESFilters(); - this.props.addFilters(filters, action.id); - }, - }; - }), - ], + }; + }), }; return ( @@ -195,10 +186,12 @@ export class FeatureProperties extends React.Component { ); - let applyFirstFilterAction; - if (this.state.actions.length) { - const action = this.state.actions[0]; - applyFirstFilterAction = ( + const action = this.state.actions.find((action) => { + return action.id !== ACTION_GLOBAL_APPLY_FILTER; + }); + let applyAction; + if (action) { + applyAction = ( {applyFilterButton} - {applyFirstFilterAction} + {applyAction} {showMoreFilterActions} diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js index 82aa2def0d6f5..8547219b42e30 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js @@ -144,6 +144,7 @@ export class FeaturesTooltip extends Component { geoFields={geoFields} addFilters={this.props.addFilters} getFilterActions={this.props.getFilterActions} + getActionContext={this.props.getActionContext} loadPreIndexedShape={this._loadCurrentFeaturePreIndexedShape} /> @@ -181,6 +182,7 @@ export class FeaturesTooltip extends Component { onCloseTooltip={this.props.closeTooltip} addFilters={this.props.addFilters} getFilterActions={this.props.getFilterActions} + getActionContext={this.props.getActionContext} showFilterActions={this._showFilterActionsView} /> {this._renderActions(geoFields)} diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js index 91e1c12bce572..87d6f8e1d8e71 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js @@ -196,6 +196,7 @@ export class TooltipControl extends React.Component { layerList={this.props.layerList} addFilters={this.props.addFilters} getFilterActions={this.props.getFilterActions} + getActionContext={this.props.getActionContext} renderTooltipContent={this.props.renderTooltipContent} geoFields={this.props.geoFields} features={features} diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js index 537e135625449..4cfddf0034039 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js @@ -118,6 +118,7 @@ export class TooltipPopover extends Component { const publicProps = { addFilters: this.props.addFilters, getFilterActions: this.props.getFilterActions, + getActionContext: this.props.getActionContext, closeTooltip: this.props.closeTooltip, features: this.props.features, isLocked: this.props.isLocked, 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 8c8ad721544a2..6a0252ca9cfba 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 @@ -304,18 +304,13 @@ export class MBMap extends React.Component { let drawControl; let tooltipControl; if (this.state.mbMap) { - drawControl = ( - - ); + drawControl = ; tooltipControl = !this.props.disableTooltipControl ? ( diff --git a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx index 892b21f396976..52cf80646e788 100644 --- a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx @@ -11,7 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import uuid from 'uuid/v4'; import { Filter } from 'src/plugins/data/public'; -import { UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; +import { ActionExecutionContext, UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; // @ts-expect-error import { MBMap } from '../map/mb'; // @ts-expect-error @@ -38,6 +38,7 @@ const RENDER_COMPLETE_EVENT = 'renderComplete'; interface Props { addFilters: ((filters: Filter[]) => Promise) | null; getFilterActions?: () => Promise; + getActionContext?: () => ActionExecutionContext; areLayersLoaded: boolean; cancelAllInFlightRequests: () => void; exitFullScreen: () => void; @@ -186,6 +187,7 @@ export class MapContainer extends Component { const { addFilters, getFilterActions, + getActionContext, flyoutDisplay, isFullScreen, exitFullScreen, @@ -234,6 +236,7 @@ export class MapContainer extends Component { @@ -242,6 +245,7 @@ export class MapContainer extends Component { addFilters={addFilters} geoFields={this.state.geoFields} getFilterActions={getFilterActions} + getActionContext={getActionContext} /> )} 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 338cbc2e71eb3..a9dc3f822060c 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 @@ -12,14 +12,18 @@ import { FitToData } from './fit_to_data'; export class ToolbarOverlay extends React.Component { _renderToolsControl() { - const { addFilters, geoFields, getFilterActions } = this.props; + const { addFilters, geoFields, getFilterActions, getActionContext } = this.props; if (!addFilters || !geoFields.length) { return null; } return ( - + ); } diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js index a331aa70f7816..017f0369e0b73 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js @@ -124,6 +124,7 @@ export class ToolsControl extends Component { buttonLabel={DRAW_SHAPE_LABEL_SHORT} geoFields={this.props.geoFields} getFilterActions={this.props.getFilterActions} + getActionContext={this.props.getActionContext} intitialGeometryLabel={i18n.translate( 'xpack.maps.toolbarOverlay.drawShape.initialGeometryLabel', { @@ -143,6 +144,7 @@ export class ToolsControl extends Component { buttonLabel={DRAW_BOUNDS_LABEL_SHORT} geoFields={this.props.geoFields} getFilterActions={this.props.getFilterActions} + getActionContext={this.props.getActionContext} intitialGeometryLabel={i18n.translate( 'xpack.maps.toolbarOverlay.drawBounds.initialGeometryLabel', { @@ -164,6 +166,7 @@ export class ToolsControl extends Component { return geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT; })} getFilterActions={this.props.getFilterActions} + getActionContext={this.props.getActionContext} onSubmit={this._initiateDistanceDraw} /> ), diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index f308e8952040c..16fb6affd58cc 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -11,8 +11,10 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { Subscription } from 'rxjs'; import { Unsubscribe } from 'redux'; import { Embeddable, IContainer } from '../../../../../src/plugins/embeddable/public'; +import { ACTION_GLOBAL_APPLY_FILTER } from '../../../../../src/plugins/data/public'; import { APPLY_FILTER_TRIGGER, + ActionExecutionContext, TriggerContextMapping, } from '../../../../../src/plugins/ui_actions/public'; import { @@ -53,7 +55,6 @@ import { RenderToolTipContent } from '../classes/tooltips/tooltip_property'; import { getUiActions, getCoreI18n } from '../kibana_services'; import { LayerDescriptor } from '../../common/descriptor_types'; import { MapContainer } from '../connected_components/map_container'; -import { ADD_FILTER_MAPS_ACTION } from '../components/action_select'; import { MapEmbeddableConfig, MapEmbeddableInput, MapEmbeddableOutput } from './types'; export { MapEmbeddableInput, MapEmbeddableConfig }; @@ -235,6 +236,7 @@ export class MapEmbeddable extends Embeddable @@ -252,13 +254,17 @@ export class MapEmbeddable extends Embeddable(replaceLayerList(this._layerList)); } - addFilters = async (filters: Filter[], actionId: string = ADD_FILTER_MAPS_ACTION) => { - if (actionId === ADD_FILTER_MAPS_ACTION) { - const applyFilterAction = getUiActions().getAction('ACTION_APPLY_FILTER'); + addFilters = async (filters: Filter[], actionId: string = ACTION_GLOBAL_APPLY_FILTER) => { + const executeContext = { + ...this.getActionContext(), + filters, + }; + if (actionId === ACTION_GLOBAL_APPLY_FILTER) { + const applyFilterAction = getUiActions().getAction(ACTION_GLOBAL_APPLY_FILTER); if (!applyFilterAction) { throw new Error('Unable to apply filter, could not locate action'); } - applyFilterAction.execute(this.getFilterActionContext(filters)); + applyFilterAction.execute(executeContext); return; } @@ -268,20 +274,25 @@ export class MapEmbeddable extends Embeddable { - return await getUiActions().getTriggerCompatibleActions('FILTER_TRIGGER', { + return await getUiActions().getTriggerCompatibleActions(APPLY_FILTER_TRIGGER, { embeddable: this, + filters: [], }); }; - getFilterActionContext = (filters: Filter[]) => { + getActionContext = () => { + const trigger = getUiActions().getTrigger(APPLY_FILTER_TRIGGER); + if (!trigger) { + throw new Error('Unable to get context, could not locate trigger'); + } return { embeddable: this, - filters, - }; + trigger, + } as ActionExecutionContext; }; destroy() { From 27799826c065815861793f46daacc335026fd4dd Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 21 Aug 2020 10:13:41 -0600 Subject: [PATCH 07/13] clean-up and API doc changes --- ...lugins-data-public.action_global_apply_filter.md | 11 +++++++++++ .../public/kibana-plugin-plugins-data-public.md | 1 + src/plugins/data/public/public.api.md | 5 +++++ .../maps/public/embeddable/map_embeddable.tsx | 13 +------------ 4 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.action_global_apply_filter.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.action_global_apply_filter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.action_global_apply_filter.md new file mode 100644 index 0000000000000..14075ba1beba0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.action_global_apply_filter.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ACTION\_GLOBAL\_APPLY\_FILTER](./kibana-plugin-plugins-data-public.action_global_apply_filter.md) + +## ACTION\_GLOBAL\_APPLY\_FILTER variable + +Signature: + +```typescript +ACTION_GLOBAL_APPLY_FILTER = "ACTION_GLOBAL_APPLY_FILTER" +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index dc83cfb930d7d..1be1e775cbb6a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -89,6 +89,7 @@ | Variable | Description | | --- | --- | +| [ACTION\_GLOBAL\_APPLY\_FILTER](./kibana-plugin-plugins-data-public.action_global_apply_filter.md) | | | [AggGroupLabels](./kibana-plugin-plugins-data-public.agggrouplabels.md) | | | [AggGroupNames](./kibana-plugin-plugins-data-public.agggroupnames.md) | | | [baseFormattersPublic](./kibana-plugin-plugins-data-public.baseformatterspublic.md) | | diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 7defddb8f570a..1278fa43835fe 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -78,6 +78,11 @@ import { UnregisterCallback } from 'history'; import { UnwrapPromiseOrReturn } from '@kbn/utility-types'; import { UserProvidedValues } from 'src/core/server/types'; +// Warning: (ae-missing-release-tag) "ACTION_GLOBAL_APPLY_FILTER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const ACTION_GLOBAL_APPLY_FILTER = "ACTION_GLOBAL_APPLY_FILTER"; + // Warning: (ae-forgotten-export) The symbol "AggConfigSerialized" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "AggConfigOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 16fb6affd58cc..1cb393bede956 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -259,18 +259,7 @@ export class MapEmbeddable extends Embeddable { - return filterAction.id === actionId; - }); + const action = getUiActions().getAction(actionId); if (!action) { throw new Error('Unable to apply filter, could not locate action'); } From 553b6d92639248b7227101e89fae43105f71505a Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 21 Aug 2020 10:46:44 -0600 Subject: [PATCH 08/13] update snapshots --- .../geometry_filter_form.test.js.snap | 16 +++++++++++++ .../feature_properties.test.js.snap | 24 ++++++++++++------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap b/x-pack/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap index 85a073c8d9ace..2d39a52dfe974 100644 --- a/x-pack/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap +++ b/x-pack/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap @@ -38,6 +38,10 @@ exports[`should not render relation select when geo field is geo_point 1`] = ` } } /> + @@ -121,6 +125,10 @@ exports[`should not show "within" relation when filter geometry is not closed 1` value="INTERSECTS" /> + @@ -177,6 +185,10 @@ exports[`should render error message 1`] = ` } } /> + @@ -267,6 +279,10 @@ exports[`should render relation select when geo field is geo_shape 1`] = ` value="INTERSECTS" /> + diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap index 3b3d82c92fbb7..48e7b90d9e24a 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap @@ -78,14 +78,22 @@ exports[`FeatureProperties should show only filter button for filterable propert } /> - + + + + + Date: Mon, 24 Aug 2020 12:19:10 -0600 Subject: [PATCH 09/13] do not show first drilldown in tooltip --- .../feature_properties.test.js.snap | 68 +++++++++++- .../features_tooltip/feature_properties.js | 102 +++++++----------- .../feature_properties.test.js | 30 +++++- 3 files changed, 131 insertions(+), 69 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap index 48e7b90d9e24a..92552e3187c67 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`FeatureProperties should not show filter button 1`] = ` +exports[`FeatureProperties should render 1`] = ` @@ -56,7 +56,60 @@ exports[`FeatureProperties should show error message if unable to load tooltip c `; -exports[`FeatureProperties should show only filter button for filterable properties 1`] = ` +exports[`FeatureProperties should show filter button for filterable properties 1`] = ` +
+ + + + + + + + + +
+ prop1 + + + +
+ prop2 + +
+`; + +exports[`FeatureProperties should show view actions button when there are available actions 1`] = ` @@ -93,6 +146,17 @@ exports[`FeatureProperties should show only filter button for filterable propert title="Filter on property" /> + + + diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js index 93ac3df1f0c4a..66c70325c541d 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js @@ -166,75 +166,47 @@ export class FeatureProperties extends React.Component { } const applyFilterButton = ( - - { - this.props.onCloseTooltip(); - const filters = await tooltipProperty.getESFilters(); - this.props.addFilters(filters); - }} - aria-label={i18n.translate('xpack.maps.tooltip.filterOnPropertyAriaLabel', { - defaultMessage: 'Filter on property', - })} - data-test-subj="mapTooltipCreateFilterButton" - /> - + { + this.props.onCloseTooltip(); + const filters = await tooltipProperty.getESFilters(); + this.props.addFilters(filters); + }} + aria-label={i18n.translate('xpack.maps.tooltip.filterOnPropertyAriaLabel', { + defaultMessage: 'Filter on property', + })} + data-test-subj="mapTooltipCreateFilterButton" + /> ); - const action = this.state.actions.find((action) => { - return action.id !== ACTION_GLOBAL_APPLY_FILTER; - }); - let applyAction; - if (action) { - applyAction = ( - - { - this.props.onCloseTooltip(); - const filters = await tooltipProperty.getESFilters(); - this.props.addFilters(filters, action.id); - }} - > - {action.getDisplayName()} - - - ); - } - - let showMoreFilterActions; - if (this.state.actions.length > 1) { - showMoreFilterActions = ( - - { - this._showFilterActions(tooltipProperty); - }} - aria-label={i18n.translate('xpack.maps.tooltip.moreActionsTitle', { - defaultMessage: 'More filter actions', - })} - /> - - ); - } - - return ( + return this.state.actions.length === 0 || + (this.state.actions.length === 1 && + this.state.actions[0].id === ACTION_GLOBAL_APPLY_FILTER) ? ( + + ) : ( ); diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.test.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.test.js index c6db9cd96a429..ff0e1a9e2acd8 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.test.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.test.js @@ -7,6 +7,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { FeatureProperties } from './feature_properties'; +import { ACTION_GLOBAL_APPLY_FILTER } from '../../../../../../../src/plugins/data/public'; class MockTooltipProperty { constructor(key, value, isFilterable) { @@ -36,6 +37,9 @@ const defaultProps = { layerId: `layer`, onCloseTooltip: () => {}, showFilterButtons: false, + getFilterActions: () => { + return [{ id: ACTION_GLOBAL_APPLY_FILTER }]; + }, }; const mockTooltipProperties = [ @@ -44,10 +48,29 @@ const mockTooltipProperties = [ ]; describe('FeatureProperties', () => { - test('should not show filter button', async () => { + test('should render', async () => { + const component = shallow( + { + return mockTooltipProperties; + }} + /> + ); + + // 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 filter button for filterable properties', async () => { const component = shallow( { return mockTooltipProperties; }} @@ -62,7 +85,7 @@ describe('FeatureProperties', () => { expect(component).toMatchSnapshot(); }); - test('should show only filter button for filterable properties', async () => { + test('should show view actions button when there are available actions', async () => { const component = shallow( { loadFeatureProperties={() => { return mockTooltipProperties; }} + getFilterActions={() => { + return [{ id: 'drilldown1' }]; + }} /> ); From 6b1a221361846fd9557422cc94fdc6c81ade383c Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 25 Aug 2020 13:48:01 -0600 Subject: [PATCH 10/13] add light grey line to seperate feature property rows --- .../__snapshots__/feature_properties.test.js.snap | 10 ++++++++++ .../map/features_tooltip/_index.scss | 4 ++++ .../map/features_tooltip/feature_properties.js | 4 ++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap index 92552e3187c67..748e99a0c335c 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap @@ -6,6 +6,7 @@ exports[`FeatureProperties should render 1`] = ` >
{applyFilterButton} - {applyFilterButton} - {applyAction} - {showMoreFilterActions} + {applyFilterButton} + + { + this._showFilterActions(tooltipProperty); + }} + aria-label={i18n.translate('xpack.maps.tooltip.viewActionsTitle', { + defaultMessage: 'View filter actions', + })} + /> +
+
+
@@ -62,6 +66,7 @@ exports[`FeatureProperties should show filter button for filterable properties 1 > + @@ -115,6 +122,7 @@ exports[`FeatureProperties should show view actions button when there are availa > + diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss index fb75cc1e2db69..140197bb1cca0 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss @@ -8,6 +8,10 @@ } } +.mapFeatureTooltip_row { + border-bottom: 1px solid $euiColorLightestShade; +} + .mapFeatureTooltip_actionLinks { padding: $euiSizeXS; } diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js index 66c70325c541d..5c1d34c11dfa0 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js @@ -162,7 +162,7 @@ export class FeatureProperties extends React.Component { _renderFilterCell(tooltipProperty) { if (!this.props.showFilterButtons || !tooltipProperty.isFilterable()) { - return null; + return ; } const applyFilterButton = ( @@ -250,7 +250,7 @@ export class FeatureProperties extends React.Component { const rows = this.state.properties.map((tooltipProperty) => { const label = tooltipProperty.getPropertyName(); return ( - + {label} Date: Wed, 26 Aug 2020 14:51:40 +0100 Subject: [PATCH 11/13] Improving tooltip row styles (#36) * Improving tooltip actions row styles * Removind unecessary comment --- .../map/features_tooltip/_index.scss | 8 +++- .../features_tooltip/feature_properties.js | 44 +++++++++---------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss index 140197bb1cca0..abd747c8fa47a 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss @@ -1,6 +1,5 @@ .mapFeatureTooltip_table { width: 100%; - display: block; max-height: calc(49vh - #{$euiSizeXL * 2}); td { @@ -24,3 +23,10 @@ max-width: $euiSizeXL * 4; font-weight: $euiFontWeightSemiBold; } + +.mapFeatureTooltip_actionsRow { + > span { + display: flex; + justify-content: flex-end; + } +} diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js index 5c1d34c11dfa0..4dfb4850799b2 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js @@ -10,8 +10,6 @@ import { EuiLoadingSpinner, EuiTextAlign, EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, EuiIcon, EuiContextMenu, } from '@elastic/eui'; @@ -168,7 +166,6 @@ export class FeatureProperties extends React.Component { const applyFilterButton = ( + > + + ); return this.state.actions.length === 0 || @@ -189,25 +188,24 @@ export class FeatureProperties extends React.Component { this.state.actions[0].id === ACTION_GLOBAL_APPLY_FILTER) ? ( {applyFilterButton} ) : ( - - - {applyFilterButton} - - { - this._showFilterActions(tooltipProperty); - }} - aria-label={i18n.translate('xpack.maps.tooltip.viewActionsTitle', { - defaultMessage: 'View filter actions', - })} - /> - - + + + {applyFilterButton} + { + this._showFilterActions(tooltipProperty); + }} + aria-label={i18n.translate('xpack.maps.tooltip.viewActionsTitle', { + defaultMessage: 'View filter actions', + })} + > + + + ); } From abdac76a08150c834bc62a480ed4e71f42257d93 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 26 Aug 2020 09:51:22 -0600 Subject: [PATCH 12/13] update snapshot and add functional test --- .../feature_properties.test.js.snap | 54 +++++++++--------- .../features_tooltip/feature_properties.js | 5 +- .../maps/embeddable/tooltip_filter_actions.js | 57 ++++++++++++++----- .../es_archives/maps/kibana/data.json | 6 +- 4 files changed, 79 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap index 748e99a0c335c..29df06a64a3f2 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap @@ -86,11 +86,14 @@ exports[`FeatureProperties should show filter button for filterable properties 1 + > + + - - - + + - - - + - - - +
+ { const actionContext = this.props.getActionContext(); const iconType = action.getIconType ? action.getIconType(actionContext) : null; + const name = action.getDisplayName ? action.getDisplayName(actionContext) : action.id; return { - name: action.getDisplayName ? action.getDisplayName(actionContext) : action.id, + name, icon: iconType ? : null, onClick: async () => { this.props.onCloseTooltip(); const filters = await tooltipProperty.getESFilters(); this.props.addFilters(filters, action.id); }, + ['data-test-subj']: `mapFilterActionButton__${name}`, }; }), }; @@ -202,6 +204,7 @@ export class FeatureProperties extends React.Component { aria-label={i18n.translate('xpack.maps.tooltip.viewActionsTitle', { defaultMessage: 'View filter actions', })} + data-test-subj="mapTooltipMoreActionsButton" > diff --git a/x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js b/x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js index a996910d4787a..10754d20118e9 100644 --- a/x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js +++ b/x-pack/test/functional/apps/maps/embeddable/tooltip_filter_actions.js @@ -13,31 +13,62 @@ export default function ({ getPageObjects, getService }) { const filterBar = getService('filterBar'); describe('tooltip filter actions', () => { - before(async () => { + async function loadDashboardAndOpenTooltip() { await kibanaServer.uiSettings.replace({ defaultIndex: 'c698b940-e149-11e8-a35a-370a8516603a', }); await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.preserveCrossAppState(); await PageObjects.dashboard.loadSavedDashboard('dash for tooltip filter action test'); await PageObjects.maps.lockTooltipAtPosition(200, -200); - }); + } + + describe('apply filter to current view', () => { + before(async () => { + await loadDashboardAndOpenTooltip(); + }); + + it('should display create filter button when tooltip is locked', async () => { + const exists = await testSubjects.exists('mapTooltipCreateFilterButton'); + expect(exists).to.be(true); + }); + + it('should create filters when create filter button is clicked', async () => { + await testSubjects.click('mapTooltipCreateFilterButton'); + await testSubjects.click('applyFiltersPopoverButton'); + + // TODO: Fix me #64861 + // const hasSourceFilter = await filterBar.hasFilter('name', 'charlie'); + // expect(hasSourceFilter).to.be(true); - it('should display create filter button when tooltip is locked', async () => { - const exists = await testSubjects.exists('mapTooltipCreateFilterButton'); - expect(exists).to.be(true); + const hasJoinFilter = await filterBar.hasFilter('shape_name', 'charlie'); + expect(hasJoinFilter).to.be(true); + }); }); - it('should create filters when create filter button is clicked', async () => { - await testSubjects.click('mapTooltipCreateFilterButton'); - await testSubjects.click('applyFiltersPopoverButton'); + describe('panel actions', () => { + before(async () => { + await loadDashboardAndOpenTooltip(); + }); + + it('should display more actions button when tooltip is locked', async () => { + const exists = await testSubjects.exists('mapTooltipMoreActionsButton'); + expect(exists).to.be(true); + }); + + it('should trigger drilldown action when clicked', async () => { + await testSubjects.click('mapTooltipMoreActionsButton'); + await testSubjects.click('mapFilterActionButton__drilldown1'); - // TODO: Fix me #64861 - // const hasSourceFilter = await filterBar.hasFilter('name', 'charlie'); - // expect(hasSourceFilter).to.be(true); + // Assert on new dashboard with filter from action + await PageObjects.dashboard.waitForRenderComplete(); + const panelCount = await PageObjects.dashboard.getPanelCount(); + expect(panelCount).to.equal(2); - const hasJoinFilter = await filterBar.hasFilter('shape_name', 'charlie'); - expect(hasJoinFilter).to.be(true); + const hasJoinFilter = await filterBar.hasFilter('shape_name', 'charlie'); + expect(hasJoinFilter).to.be(true); + }); }); }); } diff --git a/x-pack/test/functional/es_archives/maps/kibana/data.json b/x-pack/test/functional/es_archives/maps/kibana/data.json index 198174bccb286..0f1fd3c09d706 100644 --- a/x-pack/test/functional/es_archives/maps/kibana/data.json +++ b/x-pack/test/functional/es_archives/maps/kibana/data.json @@ -1048,7 +1048,7 @@ "title" : "dash for tooltip filter action test", "hits" : 0, "description" : "Zoomed in so entire screen is covered by filter so click to open tooltip can not miss.", - "panelsJSON" : "[{\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":26,\"i\":\"1\"},\"version\":\"8.0.0\",\"panelIndex\":\"1\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":-1.31919,\"lon\":59.53306,\"zoom\":9.67},\"isLayerTOCOpen\":false,\"openTOCDetails\":[\"n1t6f\"]},\"panelRefName\":\"panel_0\"}]", + "panelsJSON" : "[{\"version\":\"8.0.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":48,\"h\":26,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":-1.31919,\"lon\":59.53306,\"zoom\":9.67},\"isLayerTOCOpen\":false,\"openTOCDetails\":[\"n1t6f\"],\"hiddenLayers\":[],\"enhancements\":{\"dynamicActions\":{\"events\":[{\"eventId\":\"669a3521-1215-4228-9ced-77e2edf5ad17\",\"triggers\":[\"FILTER_TRIGGER\"],\"action\":{\"name\":\"drilldown1\",\"config\":{\"dashboardId\":\"19906970-2e40-11e9-85cb-6965aae20f13\",\"useCurrentFilters\":true,\"useCurrentDateRange\":true},\"factoryId\":\"DASHBOARD_TO_DASHBOARD_DRILLDOWN\"}}]}}},\"panelRefName\":\"panel_0\"}]", "optionsJSON" : "{\"useMargins\":true,\"hidePanelTitles\":false}", "version" : 1, "timeRestore" : true, @@ -1071,9 +1071,9 @@ } ], "migrationVersion" : { - "dashboard" : "7.0.0" + "dashboard" : "7.3.0" }, - "updated_at" : "2019-06-14T14:09:25.039Z" + "updated_at" : "2020-08-26T14:32:27.854Z" } } } From e6f548ebfbabc203c5408afae3042f7a1ed5bcaf Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 28 Aug 2020 09:08:44 -0600 Subject: [PATCH 13/13] switch UiActionsActionDefinition to Action and remove unneeded checks --- .../plugins/maps/public/components/action_select.tsx | 10 +++++----- .../maps/public/components/distance_filter_form.tsx | 4 ++-- .../map/features_tooltip/feature_properties.js | 4 ++-- .../map_container/map_container.tsx | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/maps/public/components/action_select.tsx b/x-pack/plugins/maps/public/components/action_select.tsx index 82f0e0ab78123..ad61a6a129974 100644 --- a/x-pack/plugins/maps/public/components/action_select.tsx +++ b/x-pack/plugins/maps/public/components/action_select.tsx @@ -7,17 +7,17 @@ import React, { Component } from 'react'; import { EuiFormRow, EuiSuperSelect, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ActionExecutionContext, UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; +import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public'; interface Props { value?: string; onChange: (value: string) => void; - getFilterActions?: () => Promise; + getFilterActions?: () => Promise; getActionContext?: () => ActionExecutionContext; } interface State { - actions: UiActionsActionDefinition[]; + actions: Action[]; } export class ActionSelect extends Component { @@ -56,13 +56,13 @@ export class ActionSelect extends Component { const actionContext = this.props.getActionContext(); const options = this.state.actions.map((action) => { - const iconType = action.getIconType ? action.getIconType(actionContext) : null; + const iconType = action.getIconType(actionContext); return { value: action.id, inputDisplay: (
{iconType ? : null} - {action.getDisplayName ? action.getDisplayName(actionContext) : action.id} + {action.getDisplayName(actionContext)}
), }; diff --git a/x-pack/plugins/maps/public/components/distance_filter_form.tsx b/x-pack/plugins/maps/public/components/distance_filter_form.tsx index 24b046f39eed6..24d9aec5b77b4 100644 --- a/x-pack/plugins/maps/public/components/distance_filter_form.tsx +++ b/x-pack/plugins/maps/public/components/distance_filter_form.tsx @@ -14,7 +14,7 @@ import { EuiTextAlign, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ActionExecutionContext, UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; +import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public'; import { MultiIndexGeoFieldSelect } from './multi_index_geo_field_select'; import { GeoFieldWithIndex } from './geo_field_with_index'; import { ActionSelect } from './action_select'; @@ -24,7 +24,7 @@ interface Props { className?: string; buttonLabel: string; geoFields: GeoFieldWithIndex[]; - getFilterActions?: () => Promise; + getFilterActions?: () => Promise; getActionContext?: () => ActionExecutionContext; onSubmit: ({ actionId, diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js index 740f4a0d61406..edd501f266690 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js @@ -116,8 +116,8 @@ export class FeatureProperties extends React.Component { id: 0, items: this.state.actions.map((action) => { const actionContext = this.props.getActionContext(); - const iconType = action.getIconType ? action.getIconType(actionContext) : null; - const name = action.getDisplayName ? action.getDisplayName(actionContext) : action.id; + const iconType = action.getIconType(actionContext); + const name = action.getDisplayName(actionContext); return { name, icon: iconType ? : null, diff --git a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx index 52cf80646e788..bf75c86ac249d 100644 --- a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx @@ -11,7 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import uuid from 'uuid/v4'; import { Filter } from 'src/plugins/data/public'; -import { ActionExecutionContext, UiActionsActionDefinition } from 'src/plugins/ui_actions/public'; +import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public'; // @ts-expect-error import { MBMap } from '../map/mb'; // @ts-expect-error @@ -37,7 +37,7 @@ const RENDER_COMPLETE_EVENT = 'renderComplete'; interface Props { addFilters: ((filters: Filter[]) => Promise) | null; - getFilterActions?: () => Promise; + getFilterActions?: () => Promise; getActionContext?: () => ActionExecutionContext; areLayersLoaded: boolean; cancelAllInFlightRequests: () => void;