[Maps] show empty tooltips with actions on click (#99337)

* [Maps] show tooltips with actions on click

* clean up

* call canShowTooltip in _getTooltipFeatures

* move view into action

* cleanup

* i18n

* add comment to clarify if statement

* tslint

* fix security tslint

* fix jest tests

* clean up

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2021-05-10 17:46:33 -06:00 committed by GitHub
parent b5380697cc
commit b248472e82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 249 additions and 239 deletions

View file

@ -44,6 +44,7 @@ storiesOf('app/RumDashboard/VisitorsRegionMap', module)
'__kbnjoin__count__3657625d-17b0-41ef-99ba-3a2b2938655c': 439145,
'__kbnjoin__avg_of_transaction.duration.us__3657625d-17b0-41ef-99ba-3a2b2938655c': 2041665.6350131081,
},
actions: [],
},
]}
/>

View file

@ -7,7 +7,9 @@
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import { ReactNode } from 'react';
import { GeoJsonProperties } from 'geojson';
import { Geometry } from 'geojson';
import { Query } from '../../../../../src/plugins/data/common';
import { DRAW_TYPE, ES_GEO_FIELD_TYPE, ES_SPATIAL_RELATIONS } from '../constants';
@ -41,10 +43,20 @@ export type Goto = {
center?: MapCenterAndZoom;
};
export const GEOMETRY_FILTER_ACTION = 'GEOMETRY_FILTER_ACTION';
export type TooltipFeatureAction = {
label: string;
id: typeof GEOMETRY_FILTER_ACTION;
form: ReactNode;
};
export type TooltipFeature = {
id?: number | string;
layerId: string;
geometry?: Geometry;
mbProperties: GeoJsonProperties;
actions: TooltipFeatureAction[];
};
export type TooltipState = {

View file

@ -6,7 +6,6 @@
*/
import _ from 'lodash';
import uuid from 'uuid/v4';
import { Dispatch } from 'redux';
import { Feature } from 'geojson';
import { getOpenTooltips } from '../selectors/map_selectors';
@ -36,11 +35,7 @@ export function openOnClickTooltip(tooltipState: TooltipState) {
);
});
openTooltips.push({
...tooltipState,
isLocked: true,
id: uuid(),
});
openTooltips.push(tooltipState);
dispatch({
type: SET_OPEN_TOOLTIPS,
@ -63,13 +58,7 @@ export function closeOnHoverTooltip() {
export function openOnHoverTooltip(tooltipState: TooltipState) {
return {
type: SET_OPEN_TOOLTIPS,
openTooltips: [
{
...tooltipState,
isLocked: false,
id: uuid(),
},
],
openTooltips: [tooltipState],
};
}

View file

@ -77,7 +77,6 @@ export interface ILayer {
getMbLayerIds(): string[];
ownsMbLayerId(mbLayerId: string): boolean;
ownsMbSourceId(mbSourceId: string): boolean;
canShowTooltip(): boolean;
syncLayerWithMB(mbMap: MbMap): void;
getLayerTypeIconName(): string;
isInitialDataLoadComplete(): boolean;
@ -452,10 +451,6 @@ export class AbstractLayer implements ILayer {
throw new Error('Should implement AbstractLayer#ownsMbSourceId');
}
canShowTooltip() {
return false;
}
syncLayerWithMB(mbMap: MbMap) {
throw new Error('Should implement AbstractLayer#syncLayerWithMB');
}

View file

@ -88,6 +88,7 @@ export interface IVectorLayer extends ILayer {
getFeatureById(id: string | number): Feature | null;
getPropertiesForTooltip(properties: GeoJsonProperties): Promise<ITooltipProperty[]>;
hasJoins(): boolean;
canShowTooltip(): boolean;
}
export class VectorLayer extends AbstractLayer implements IVectorLayer {
@ -1033,10 +1034,7 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
}
canShowTooltip() {
return (
this.isVisible() &&
(this.getSource().canFormatFeatureProperties() || this.getJoins().length > 0)
);
return this.getSource().hasTooltipProperties() || this.getJoins().length > 0;
}
getFeatureById(id: string | number) {

View file

@ -196,7 +196,7 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc
return fields.map((f) => this.createField({ fieldName: f.name }));
}
canFormatFeatureProperties() {
hasTooltipProperties() {
return this._tooltipFields.length > 0;
}

View file

@ -471,7 +471,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements ITiledSingle
}
}
canFormatFeatureProperties(): boolean {
hasTooltipProperties(): boolean {
return true;
}

View file

@ -359,7 +359,7 @@ export class ESGeoLineSource extends AbstractESAggSource {
return true;
}
canFormatFeatureProperties() {
hasTooltipProperties() {
return true;
}

View file

@ -226,7 +226,7 @@ export class ESPewPewSource extends AbstractESAggSource {
return turfBboxToBounds(turfBbox(multiPoint(corners)));
}
canFormatFeatureProperties() {
hasTooltipProperties() {
return true;
}
}

View file

@ -464,7 +464,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
};
}
canFormatFeatureProperties(): boolean {
hasTooltipProperties(): boolean {
return this._tooltipFields.length > 0;
}

View file

@ -121,7 +121,7 @@ export class GeoJsonFileSource extends AbstractVectorSource {
return (this._descriptor as GeojsonFileSourceDescriptor).name;
}
canFormatFeatureProperties() {
hasTooltipProperties() {
return true;
}

View file

@ -104,7 +104,7 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
return this._descriptor.name;
}
canFormatFeatureProperties() {
hasTooltipProperties() {
return true;
}
}

View file

@ -30,10 +30,10 @@ describe('getUrlTemplateWithMeta', () => {
});
});
describe('canFormatFeatureProperties', () => {
describe('hasTooltipProperties', () => {
it('false if no tooltips', async () => {
const source = new MVTSingleLayerVectorSource(descriptor);
expect(source.canFormatFeatureProperties()).toEqual(false);
expect(source.hasTooltipProperties()).toEqual(false);
});
it('true if tooltip', async () => {
const descriptorWithTooltips = {
@ -42,7 +42,7 @@ describe('canFormatFeatureProperties', () => {
tooltipProperties: ['foobar'],
};
const source = new MVTSingleLayerVectorSource(descriptorWithTooltips);
expect(source.canFormatFeatureProperties()).toEqual(true);
expect(source.hasTooltipProperties()).toEqual(true);
});
});

View file

@ -169,7 +169,7 @@ export class MVTSingleLayerVectorSource
return [VECTOR_SHAPE_TYPE.POINT, VECTOR_SHAPE_TYPE.LINE, VECTOR_SHAPE_TYPE.POLYGON];
}
canFormatFeatureProperties(): boolean {
hasTooltipProperties(): boolean {
return !!this._tooltipFields.length;
}

View file

@ -143,7 +143,7 @@ export class TableSource extends AbstractVectorSource implements ITermJoinSource
});
}
canFormatFeatureProperties(): boolean {
hasTooltipProperties(): boolean {
return false;
}

View file

@ -60,7 +60,7 @@ export interface IVectorSource extends ISource {
getSyncMeta(): VectorSourceSyncMeta | null;
getFieldNames(): string[];
createField({ fieldName }: { fieldName: string }): IField;
canFormatFeatureProperties(): boolean;
hasTooltipProperties(): boolean;
getSupportedShapeTypes(): Promise<VECTOR_SHAPE_TYPE[]>;
isBoundsAware(): boolean;
getSourceTooltipContent(sourceDataRequest?: DataRequest): SourceTooltipConfig;
@ -115,7 +115,7 @@ export class AbstractVectorSource extends AbstractSource implements IVectorSourc
throw new Error('Should implement VectorSource#getGeoJson');
}
canFormatFeatureProperties() {
hasTooltipProperties() {
return false;
}

View file

@ -8,26 +8,21 @@
import React, { Component, Fragment, ReactNode } from 'react';
import { EuiIcon, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public';
import { GeoJsonProperties, Geometry } from 'geojson';
import { Filter } from 'src/plugins/data/public';
import { FeatureProperties } from './feature_properties';
import { GEO_JSON_TYPE, ES_GEO_FIELD_TYPE, RawValue } from '../../../../common/constants';
import { FeatureGeometryFilterForm } from './feature_geometry_filter_form';
import { RawValue } from '../../../../common/constants';
import { Footer } from './footer';
import { Header } from './header';
import { PreIndexedShape } from '../../../../common/elasticsearch_util';
import { GeoFieldWithIndex } from '../../../components/geo_field_with_index';
import { TooltipFeature } from '../../../../common/descriptor_types';
import { GEOMETRY_FILTER_ACTION, TooltipFeature } from '../../../../common/descriptor_types';
import { ITooltipProperty } from '../../../classes/tooltips/tooltip_property';
import { ILayer } from '../../../classes/layers/layer';
enum VIEWS {
PROPERTIES_VIEW = 'PROPERTIES_VIEW',
GEOMETRY_FILTER_VIEW = 'GEOMETRY_FILTER_VIEW',
FILTER_ACTIONS_VIEW = 'FILTER_ACTIONS_VIEW',
}
const PROPERTIES_VIEW = 'PROPERTIES_VIEW';
const FILTER_ACTIONS_VIEW = 'FILTER_ACTIONS_VIEW';
type VIEWS = typeof PROPERTIES_VIEW | typeof FILTER_ACTIONS_VIEW | typeof GEOMETRY_FILTER_ACTION;
interface Props {
addFilters: ((filters: Filter[], actionId: string) => Promise<void>) | null;
@ -55,14 +50,6 @@ interface Props {
}) => Geometry | null;
getLayerName: (layerId: string) => Promise<string | null>;
findLayerById: (layerId: string) => ILayer | undefined;
geoFields: GeoFieldWithIndex[];
loadPreIndexedShape: ({
layerId,
featureId,
}: {
layerId: string;
featureId?: string | number;
}) => Promise<PreIndexedShape | null>;
}
interface State {
@ -77,14 +64,14 @@ export class FeaturesTooltip extends Component<Props, State> {
currentFeature: null,
filterView: null,
prevFeatures: [],
view: VIEWS.PROPERTIES_VIEW,
view: PROPERTIES_VIEW,
};
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
if (nextProps.features !== prevState.prevFeatures) {
return {
currentFeature: nextProps.features ? nextProps.features[0] : null,
view: VIEWS.PROPERTIES_VIEW,
view: PROPERTIES_VIEW,
prevFeatures: nextProps.features,
};
}
@ -96,69 +83,37 @@ export class FeaturesTooltip extends Component<Props, State> {
this.setState({ currentFeature: feature });
};
_showGeometryFilterView = () => {
this.setState({ view: VIEWS.GEOMETRY_FILTER_VIEW });
};
_showPropertiesView = () => {
this.setState({ view: VIEWS.PROPERTIES_VIEW, filterView: null });
this.setState({ view: PROPERTIES_VIEW, filterView: null });
};
_showFilterActionsView = (filterView: ReactNode) => {
this.setState({ view: VIEWS.FILTER_ACTIONS_VIEW, filterView });
this.setState({ view: FILTER_ACTIONS_VIEW, filterView });
};
_renderActions(geoFields: GeoFieldWithIndex[]) {
if (!this.props.isLocked || geoFields.length === 0) {
_renderActions() {
if (
!this.props.isLocked ||
!this.state.currentFeature ||
this.state.currentFeature.actions.length === 0
) {
return null;
}
return (
<EuiLink className="mapFeatureTooltip_actionLinks" onClick={this._showGeometryFilterView}>
<FormattedMessage
id="xpack.maps.tooltip.showGeometryFilterViewLinkLabel"
defaultMessage="Filter by geometry"
/>
</EuiLink>
);
}
_filterGeoFields(featureGeometry: Geometry | null) {
if (!featureGeometry) {
return [];
}
// line geometry can only create filters for geo_shape fields.
if (
featureGeometry.type === GEO_JSON_TYPE.LINE_STRING ||
featureGeometry.type === GEO_JSON_TYPE.MULTI_LINE_STRING
) {
return this.props.geoFields.filter(({ geoFieldType }) => {
return geoFieldType === ES_GEO_FIELD_TYPE.GEO_SHAPE;
});
}
// TODO support geo distance filters for points
if (
featureGeometry.type === GEO_JSON_TYPE.POINT ||
featureGeometry.type === GEO_JSON_TYPE.MULTI_POINT
) {
return [];
}
return this.props.geoFields;
}
_loadCurrentFeaturePreIndexedShape = async () => {
if (!this.state.currentFeature) {
return null;
}
return this.props.loadPreIndexedShape({
layerId: this.state.currentFeature.layerId,
featureId: this.state.currentFeature.id,
return this.state.currentFeature.actions.map((action) => {
return (
<EuiLink
className="mapFeatureTooltip_actionLinks"
onClick={() => {
this.setState({ view: action.id });
}}
key={action.id}
>
{action.label}
</EuiLink>
);
});
};
}
_renderBackButton(label: string) {
return (
@ -181,38 +136,20 @@ export class FeaturesTooltip extends Component<Props, State> {
return null;
}
const currentFeatureGeometry = this.props.loadFeatureGeometry({
layerId: this.state.currentFeature.layerId,
featureId: this.state.currentFeature.id,
const action = this.state.currentFeature.actions.find(({ id }) => {
return id === this.state.view;
});
const geoFields = this._filterGeoFields(currentFeatureGeometry);
if (
this.state.view === VIEWS.GEOMETRY_FILTER_VIEW &&
currentFeatureGeometry &&
this.props.addFilters
) {
if (action) {
return (
<Fragment>
{this._renderBackButton(
i18n.translate('xpack.maps.tooltip.showGeometryFilterViewLinkLabel', {
defaultMessage: 'Filter by geometry',
})
)}
<FeatureGeometryFilterForm
onClose={this.props.closeTooltip}
geometry={currentFeatureGeometry}
geoFields={geoFields}
addFilters={this.props.addFilters}
getFilterActions={this.props.getFilterActions}
getActionContext={this.props.getActionContext}
loadPreIndexedShape={this._loadCurrentFeaturePreIndexedShape}
/>
{this._renderBackButton(action.label)}
{action.form}
</Fragment>
);
}
if (this.state.view === VIEWS.FILTER_ACTIONS_VIEW) {
if (this.state.view === FILTER_ACTIONS_VIEW) {
return (
<Fragment>
{this._renderBackButton(
@ -247,7 +184,7 @@ export class FeaturesTooltip extends Component<Props, State> {
onSingleValueTrigger={this.props.onSingleValueTrigger}
showFilterActions={this._showFilterActionsView}
/>
{this._renderActions(geoFields)}
{this._renderActions()}
<Footer
features={this.props.features}
isLocked={this.props.isLocked}

View file

@ -32,6 +32,7 @@ describe('Footer', () => {
id: 'feature1',
layerId: 'layer1',
mbProperties: {},
actions: [],
},
];
describe('mouseover (unlocked)', () => {
@ -54,11 +55,13 @@ describe('Footer', () => {
id: 'feature1',
layerId: 'layer1',
mbProperties: {},
actions: [],
},
{
id: 'feature2',
layerId: 'layer1',
mbProperties: {},
actions: [],
},
];
describe('mouseover (unlocked)', () => {
@ -97,16 +100,19 @@ describe('Footer', () => {
id: 'feature1',
layerId: 'layer1',
mbProperties: {},
actions: [],
},
{
id: 'feature2',
layerId: 'layer1',
mbProperties: {},
actions: [],
},
{
id: 'feature1',
layerId: 'layer2',
mbProperties: {},
actions: [],
},
];
describe('mouseover (unlocked)', () => {

View file

@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { FeatureGeometryFilterForm } from './feature_geometry_filter_form';
export { FeaturesTooltip } from './features_tooltip';

View file

@ -15,23 +15,10 @@ exports[`TooltipControl render should render hover tooltip 1`] = `
},
]
}
geoFields={
Array [
Object {},
]
}
findLayerById={[Function]}
index={0}
isLocked={false}
key="1"
layerList={
Array [
Object {
"canShowTooltip": [Function],
"getId": [Function],
"getMbLayerIds": [Function],
},
]
}
location={
Array [
-120,
@ -62,23 +49,10 @@ exports[`TooltipControl render should render locked tooltip 1`] = `
},
]
}
geoFields={
Array [
Object {},
]
}
findLayerById={[Function]}
index={0}
isLocked={true}
key="2"
layerList={
Array [
Object {
"canShowTooltip": [Function],
"getId": [Function],
"getMbLayerIds": [Function],
},
]
}
location={
Array [
-120,

View file

@ -48,17 +48,9 @@ exports[`TooltipPopover render should render tooltip popover 1`] = `
},
]
}
findLayerById={[Function]}
geoFields={
Array [
Object {},
]
}
getLayerName={[Function]}
isLocked={false}
loadFeatureGeometry={[Function]}
loadFeatureProperties={[Function]}
loadPreIndexedShape={[Function]}
/>
</EuiText>
</EuiPopover>
@ -106,7 +98,6 @@ exports[`TooltipPopover render should render tooltip popover with custom tooltip
}
getLayerName={[Function]}
isLocked={false}
loadFeatureGeometry={[Function]}
loadFeatureProperties={[Function]}
>
Custom tooltip content

View file

@ -7,8 +7,17 @@
import _ from 'lodash';
import React from 'react';
import { FEATURE_ID_PROPERTY_NAME, LON_INDEX } from '../../../../common/constants';
import { i18n } from '@kbn/i18n';
import uuid from 'uuid/v4';
import {
ES_GEO_FIELD_TYPE,
FEATURE_ID_PROPERTY_NAME,
GEO_JSON_TYPE,
LON_INDEX,
} from '../../../../common/constants';
import { GEOMETRY_FILTER_ACTION } from '../../../../common/descriptor_types';
import { TooltipPopover } from './tooltip_popover';
import { FeatureGeometryFilterForm } from '../features_tooltip';
import { EXCLUDE_TOO_MANY_FEATURES_BOX } from '../../../classes/util/mb_filter_expressions';
function justifyAnchorLocation(mbLngLat, targetFeature) {
@ -48,6 +57,28 @@ export class TooltipControl extends React.Component {
}
};
_findLayerById = (layerId) => {
return this.props.layerList.find((layer) => {
return layer.getId() === layerId;
});
};
// Must load original geometry instead of using geometry from mapbox feature.
// Mapbox feature geometry is from vector tile and is not the same as the original geometry.
_getFeatureGeometry = ({ layerId, featureId }) => {
const tooltipLayer = this._findLayerById(layerId);
if (!tooltipLayer || featureId === undefined) {
return null;
}
const targetFeature = tooltipLayer.getFeatureById(featureId);
if (!targetFeature) {
return null;
}
return targetFeature.geometry;
};
_getLayerByMbLayerId(mbLayerId) {
return this.props.layerList.find((layer) => {
const mbLayerIds = layer.getMbLayerIds();
@ -55,7 +86,76 @@ export class TooltipControl extends React.Component {
});
}
_getTooltipFeatures(mbFeatures) {
_loadPreIndexedShape = async ({ layerId, featureId }) => {
const tooltipLayer = this._findLayerById(layerId);
if (!tooltipLayer || typeof featureId === 'undefined') {
return null;
}
const targetFeature = tooltipLayer.getFeatureById(featureId);
if (!targetFeature) {
return null;
}
return await tooltipLayer.getSource().getPreIndexedShape(targetFeature.properties);
};
_getFeatureActions({ layerId, featureId, tooltipId }) {
const actions = [];
const geometry = this._getFeatureGeometry({ layerId, featureId });
const geoFieldsForFeature = this._filterGeoFieldsByFeatureGeometry(geometry);
if (geoFieldsForFeature.length && this.props.addFilters) {
actions.push({
label: i18n.translate('xpack.maps.tooltip.action.filterByGeometryLabel', {
defaultMessage: 'Filter by geometry',
}),
id: GEOMETRY_FILTER_ACTION,
form: (
<FeatureGeometryFilterForm
onClose={() => {
this.props.closeOnClickTooltip(tooltipId);
}}
geometry={geometry}
geoFields={geoFieldsForFeature}
addFilters={this.props.addFilters}
getFilterActions={this.props.getFilterActions}
getActionContext={this.props.getActionContext}
loadPreIndexedShape={async () => {
return this._loadPreIndexedShape({ layerId, featureId });
}}
/>
),
});
}
return actions;
}
_filterGeoFieldsByFeatureGeometry(geometry) {
if (!geometry) {
return [];
}
// line geometry can only create filters for geo_shape fields.
if (
geometry.type === GEO_JSON_TYPE.LINE_STRING ||
geometry.type === GEO_JSON_TYPE.MULTI_LINE_STRING
) {
return this.props.geoFields.filter(({ geoFieldType }) => {
return geoFieldType === ES_GEO_FIELD_TYPE.GEO_SHAPE;
});
}
// TODO support geo distance filters for points
if (geometry.type === GEO_JSON_TYPE.POINT || geometry.type === GEO_JSON_TYPE.MULTI_POINT) {
return [];
}
return this.props.geoFields;
}
_getTooltipFeatures(mbFeatures, isLocked, tooltipId) {
const uniqueFeatures = [];
//there may be duplicates in the results from mapbox
//this is because mapbox returns the results per tile
@ -81,12 +181,18 @@ export class TooltipControl extends React.Component {
// - As empty object literal
// To avoid ambiguity, normalize properties to empty object literal.
const mbProperties = mbFeature.properties ? mbFeature.properties : {};
//This keeps track of first properties (assuming these will be identical for features in different tiles)
uniqueFeatures.push({
id: featureId,
layerId: layerId,
mbProperties,
});
const actions = isLocked ? this._getFeatureActions({ layerId, featureId, tooltipId }) : [];
const hasActions = isLocked && actions.length;
if (hasActions || layer.canShowTooltip()) {
//This keeps track of first feature (assuming these will be identical for features in different tiles)
uniqueFeatures.push({
id: featureId,
layerId: layerId,
mbProperties,
actions,
});
}
}
}
return uniqueFeatures;
@ -109,10 +215,17 @@ export class TooltipControl extends React.Component {
const targetMbFeataure = mbFeatures[0];
const popupAnchorLocation = justifyAnchorLocation(e.lngLat, targetMbFeataure);
const features = this._getTooltipFeatures(mbFeatures);
const isLocked = true;
const tooltipId = uuid();
const features = this._getTooltipFeatures(mbFeatures, isLocked, tooltipId);
if (features.length === 0) {
return;
}
this.props.openOnClickTooltip({
features,
location: popupAnchorLocation,
isLocked,
id: tooltipId,
});
};
@ -129,25 +242,36 @@ export class TooltipControl extends React.Component {
}
const targetMbFeature = mbFeatures[0];
if (this.props.openTooltips[0]) {
if (this.props.openTooltips[0] && this.props.openTooltips[0].features.length) {
const firstFeature = this.props.openTooltips[0].features[0];
if (targetMbFeature.properties[FEATURE_ID_PROPERTY_NAME] === firstFeature.id) {
// ignore hover events when hover tooltip is all ready opened for feature
return;
}
}
const popupAnchorLocation = justifyAnchorLocation(e.lngLat, targetMbFeature);
const features = this._getTooltipFeatures(mbFeatures);
const isLocked = false;
const tooltipId = uuid();
const features = this._getTooltipFeatures(mbFeatures, isLocked, tooltipId);
if (features.length === 0) {
return;
}
this.props.openOnHoverTooltip({
features: features,
location: popupAnchorLocation,
isLocked,
id: tooltipId,
});
}, 100);
_getMbLayerIdsForTooltips() {
const mbLayerIds = this.props.layerList.reduce((mbLayerIds, layer) => {
return layer.canShowTooltip() ? mbLayerIds.concat(layer.getMbLayerIds()) : mbLayerIds;
// tooltips are only supported for vector layers, filter out all other layer types
const isVectorLayer = layer.canShowTooltip !== undefined;
return layer.isVisible() && isVectorLayer
? mbLayerIds.concat(layer.getMbLayerIds())
: mbLayerIds;
}, []);
//Ensure that all layers are actually on the map.
@ -198,13 +322,12 @@ export class TooltipControl extends React.Component {
<TooltipPopover
key={id}
mbMap={this.props.mbMap}
layerList={this.props.layerList}
findLayerById={this._findLayerById}
addFilters={this.props.addFilters}
getFilterActions={this.props.getFilterActions}
getActionContext={this.props.getActionContext}
onSingleValueTrigger={this.props.onSingleValueTrigger}
renderTooltipContent={this.props.renderTooltipContent}
geoFields={this.props.geoFields}
features={features}
location={location}
closeTooltip={closeTooltip}

View file

@ -28,9 +28,28 @@ const mockLayer = {
getId: () => {
return layerId;
},
isVisible: () => {
return true;
},
canShowTooltip: () => {
return true;
},
getFeatureById: () => {
return {
geometry: {
coordinates: [
[
[-67.5, 40.9799],
[-90, 40.9799],
[-90, 21.94305],
[-67.5, 21.94305],
[-67.5, 40.9799],
],
],
type: 'Polygon',
},
};
},
};
const mockMbMapHandlers = {};
@ -236,10 +255,9 @@ describe('TooltipControl', () => {
mockMbMapHandlers.click(mockMapMouseEvent);
sinon.assert.notCalled(closeOnClickTooltipStub);
sinon.assert.calledWith(openOnClickTooltipStub, {
features: [{ id: 1, layerId: 'tfi3f', mbProperties: { __kbn__feature_id__: 1 } }],
location: [100, 30],
});
const tooltipState = openOnClickTooltipStub.getCalls()[0].args[0];
expect(tooltipState.features.length).toBe(1);
expect(tooltipState.location).toEqual([100, 30]);
});
});
});

View file

@ -7,7 +7,7 @@
import React, { Component } from 'react';
import { LAT_INDEX, LON_INDEX } from '../../../../common/constants';
import { FeaturesTooltip } from '../features_tooltip/features_tooltip';
import { FeaturesTooltip } from '../features_tooltip';
import { EuiPopover, EuiText } from '@elastic/eui';
const noop = () => {};
@ -55,24 +55,8 @@ export class TooltipPopover extends Component {
});
};
// Must load original geometry instead of using geometry from mapbox feature.
// Mapbox feature geometry is from vector tile and is not the same as the original geometry.
_loadFeatureGeometry = ({ layerId, featureId }) => {
const tooltipLayer = this._findLayerById(layerId);
if (!tooltipLayer || typeof featureId === 'undefined') {
return null;
}
const targetFeature = tooltipLayer.getFeatureById(featureId);
if (!targetFeature) {
return null;
}
return targetFeature.geometry;
};
_loadFeatureProperties = async ({ layerId, featureId, mbProperties }) => {
const tooltipLayer = this._findLayerById(layerId);
const tooltipLayer = this.props.findLayerById(layerId);
if (!tooltipLayer) {
return [];
}
@ -86,28 +70,8 @@ export class TooltipPopover extends Component {
return await tooltipLayer.getPropertiesForTooltip(properties);
};
_loadPreIndexedShape = async ({ layerId, featureId }) => {
const tooltipLayer = this._findLayerById(layerId);
if (!tooltipLayer || typeof featureId === 'undefined') {
return null;
}
const targetFeature = tooltipLayer.getFeatureById(featureId);
if (!targetFeature) {
return null;
}
return await tooltipLayer.getSource().getPreIndexedShape(targetFeature.properties);
};
_findLayerById = (layerId) => {
return this.props.layerList.find((layer) => {
return layer.getId() === layerId;
});
};
_getLayerName = async (layerId) => {
const layer = this._findLayerById(layerId);
const layer = this.props.findLayerById(layerId);
if (!layer) {
return null;
}
@ -125,7 +89,6 @@ export class TooltipPopover extends Component {
features: this.props.features,
isLocked: this.props.isLocked,
loadFeatureProperties: this._loadFeatureProperties,
loadFeatureGeometry: this._loadFeatureGeometry,
getLayerName: this._getLayerName,
};
@ -135,12 +98,7 @@ export class TooltipPopover extends Component {
return (
<EuiText size="xs" style={{ maxWidth: '425px' }}>
<FeaturesTooltip
{...publicProps}
findLayerById={this._findLayerById}
geoFields={this.props.geoFields}
loadPreIndexedShape={this._loadPreIndexedShape}
/>
<FeaturesTooltip {...publicProps} findLayerById={this.props.findLayerById} />
</EuiText>
);
};

View file

@ -25,6 +25,7 @@ describe('MapToolTip', () => {
id: 1,
layerId: 'layerId',
mbProperties: {},
actions: [],
},
];
const getLayerName = jest.fn();

View file

@ -14088,7 +14088,6 @@
"xpack.maps.tooltip.loadingMsg": "読み込み中",
"xpack.maps.tooltip.pageNumerText": "{total} 個中 {pageNumber} 個の機能",
"xpack.maps.tooltip.showAddFilterActionsViewLabel": "フィルターアクション",
"xpack.maps.tooltip.showGeometryFilterViewLinkLabel": "ジオメトリでフィルタリング",
"xpack.maps.tooltip.toolsControl.cancelDrawButtonLabel": "キャンセル",
"xpack.maps.tooltip.unableToLoadContentTitle": "ツールヒントのコンテンツを読み込めません",
"xpack.maps.tooltip.viewActionsTitle": "フィルターアクションを表示",

View file

@ -14269,7 +14269,6 @@
"xpack.maps.tooltip.loadingMsg": "正在加载",
"xpack.maps.tooltip.pageNumerText": "第 {pageNumber} 页,共 {total} 页",
"xpack.maps.tooltip.showAddFilterActionsViewLabel": "筛选操作",
"xpack.maps.tooltip.showGeometryFilterViewLinkLabel": "按几何筛选",
"xpack.maps.tooltip.toolsControl.cancelDrawButtonLabel": "取消",
"xpack.maps.tooltip.unableToLoadContentTitle": "无法加载工具提示内容",
"xpack.maps.tooltip.viewActionsTitle": "查看筛选操作",