diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/__snapshots__/layer_template.test.tsx.snap b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/__snapshots__/layer_template.test.tsx.snap
new file mode 100644
index 0000000000000..3a301a951ed57
--- /dev/null
+++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/__snapshots__/layer_template.test.tsx.snap
@@ -0,0 +1,107 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render EMS UI when left source is BOUNDARIES_SOURCE.EMS 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`should render elasticsearch UI when left source is BOUNDARIES_SOURCE.ELASTICSEARCH 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/choropleth_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/choropleth_layer_wizard.tsx
new file mode 100644
index 0000000000000..6e806f4530df2
--- /dev/null
+++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/choropleth_layer_wizard.tsx
@@ -0,0 +1,25 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { i18n } from '@kbn/i18n';
+import { LAYER_WIZARD_CATEGORY } from '../../../../common/constants';
+import { LayerWizard, RenderWizardArguments } from '../layer_wizard_registry';
+import { LayerTemplate } from './layer_template';
+
+export const choroplethLayerWizardConfig: LayerWizard = {
+ categories: [LAYER_WIZARD_CATEGORY.ELASTICSEARCH],
+ description: i18n.translate('xpack.maps.choropleth.desc', {
+ defaultMessage: 'Shaded areas to compare statistics across boundaries',
+ }),
+ icon: 'logoElasticsearch',
+ renderWizard: (renderWizardArguments: RenderWizardArguments) => {
+ return ;
+ },
+ title: i18n.translate('xpack.maps.choropleth.title', {
+ defaultMessage: 'Choropleth',
+ }),
+};
diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts
new file mode 100644
index 0000000000000..61fb6ef54c207
--- /dev/null
+++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts
@@ -0,0 +1,144 @@
+/*
+ * 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 uuid from 'uuid/v4';
+import {
+ AGG_TYPE,
+ COLOR_MAP_TYPE,
+ FIELD_ORIGIN,
+ SCALING_TYPES,
+ SOURCE_TYPES,
+ STYLE_TYPE,
+ VECTOR_STYLES,
+} from '../../../../common/constants';
+import { getJoinAggKey } from '../../../../common/get_agg_key';
+import {
+ AggDescriptor,
+ ColorDynamicOptions,
+ EMSFileSourceDescriptor,
+ ESSearchSourceDescriptor,
+} from '../../../../common/descriptor_types';
+import { VectorStyle } from '../../styles/vector/vector_style';
+import { VectorLayer } from '../vector_layer/vector_layer';
+import { EMSFileSource } from '../../sources/ems_file_source';
+// @ts-ignore
+import { ESSearchSource } from '../../sources/es_search_source';
+import { getDefaultDynamicProperties } from '../../styles/vector/vector_style_defaults';
+
+const defaultDynamicProperties = getDefaultDynamicProperties();
+
+function createChoroplethLayerDescriptor({
+ sourceDescriptor,
+ leftField,
+ rightIndexPatternId,
+ rightIndexPatternTitle,
+ rightTermField,
+}: {
+ sourceDescriptor: EMSFileSourceDescriptor | ESSearchSourceDescriptor;
+ leftField: string;
+ rightIndexPatternId: string;
+ rightIndexPatternTitle: string;
+ rightTermField: string;
+}) {
+ const metricsDescriptor: AggDescriptor = { type: AGG_TYPE.COUNT };
+ const joinId = uuid();
+ const joinKey = getJoinAggKey({
+ aggType: metricsDescriptor.type,
+ aggFieldName: metricsDescriptor.field ? metricsDescriptor.field : '',
+ rightSourceId: joinId,
+ });
+ return VectorLayer.createDescriptor({
+ joins: [
+ {
+ leftField,
+ right: {
+ type: SOURCE_TYPES.ES_TERM_SOURCE,
+ id: joinId,
+ indexPatternId: rightIndexPatternId,
+ indexPatternTitle: rightIndexPatternTitle,
+ term: rightTermField,
+ metrics: [metricsDescriptor],
+ },
+ },
+ ],
+ sourceDescriptor,
+ style: VectorStyle.createDescriptor({
+ [VECTOR_STYLES.FILL_COLOR]: {
+ type: STYLE_TYPE.DYNAMIC,
+ options: {
+ ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions),
+ field: {
+ name: joinKey,
+ origin: FIELD_ORIGIN.JOIN,
+ },
+ color: 'Yellow to Red',
+ type: COLOR_MAP_TYPE.ORDINAL,
+ },
+ },
+ [VECTOR_STYLES.LINE_COLOR]: {
+ type: STYLE_TYPE.STATIC,
+ options: {
+ color: '#3d3d3d',
+ },
+ },
+ }),
+ });
+}
+
+export function createEmsChoroplethLayerDescriptor({
+ leftEmsFileId,
+ leftEmsField,
+ rightIndexPatternId,
+ rightIndexPatternTitle,
+ rightTermField,
+}: {
+ leftEmsFileId: string;
+ leftEmsField: string;
+ rightIndexPatternId: string;
+ rightIndexPatternTitle: string;
+ rightTermField: string;
+}) {
+ return createChoroplethLayerDescriptor({
+ sourceDescriptor: EMSFileSource.createDescriptor({
+ id: leftEmsFileId,
+ tooltipProperties: [leftEmsField],
+ }),
+ leftField: leftEmsField,
+ rightIndexPatternId,
+ rightIndexPatternTitle,
+ rightTermField,
+ });
+}
+
+export function createEsChoroplethLayerDescriptor({
+ leftIndexPatternId,
+ leftGeoField,
+ leftJoinField,
+ rightIndexPatternId,
+ rightIndexPatternTitle,
+ rightTermField,
+}: {
+ leftIndexPatternId: string;
+ leftGeoField: string;
+ leftJoinField: string;
+ rightIndexPatternId: string;
+ rightIndexPatternTitle: string;
+ rightTermField: string;
+}) {
+ return createChoroplethLayerDescriptor({
+ sourceDescriptor: ESSearchSource.createDescriptor({
+ indexPatternId: leftIndexPatternId,
+ geoField: leftGeoField,
+ scalingType: SCALING_TYPES.LIMIT,
+ tooltipProperties: [leftJoinField],
+ applyGlobalQuery: false,
+ }),
+ leftField: leftJoinField,
+ rightIndexPatternId,
+ rightIndexPatternTitle,
+ rightTermField,
+ });
+}
diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/index.ts b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/index.ts
new file mode 100644
index 0000000000000..afabfea0c8d46
--- /dev/null
+++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/index.ts
@@ -0,0 +1,7 @@
+/*
+ * 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.
+ */
+
+export { choroplethLayerWizardConfig } from './choropleth_layer_wizard';
diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/layer_template.test.tsx b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/layer_template.test.tsx
new file mode 100644
index 0000000000000..ecb86756e1ca7
--- /dev/null
+++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/layer_template.test.tsx
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+jest.mock('../../../kibana_services', () => {
+ const MockIndexPatternSelect = (props: unknown) => {
+ return
;
+ };
+ return {
+ getIndexPatternSelectComponent: () => {
+ return MockIndexPatternSelect;
+ },
+ };
+});
+
+import React from 'react';
+import { shallow } from 'enzyme';
+import { BOUNDARIES_SOURCE, LayerTemplate } from './layer_template';
+
+const renderWizardArguments = {
+ previewLayers: () => {},
+ mapColors: [],
+ currentStepId: null,
+ enableNextBtn: () => {},
+ disableNextBtn: () => {},
+ startStepLoading: () => {},
+ stopStepLoading: () => {},
+ advanceToNextStep: () => {},
+};
+
+test('should render elasticsearch UI when left source is BOUNDARIES_SOURCE.ELASTICSEARCH', async () => {
+ const component = shallow();
+ component.setState({ leftSource: BOUNDARIES_SOURCE.ELASTICSEARCH });
+ expect(component).toMatchSnapshot();
+});
+
+test('should render EMS UI when left source is BOUNDARIES_SOURCE.EMS', async () => {
+ const component = shallow();
+ component.setState({ leftSource: BOUNDARIES_SOURCE.EMS });
+ expect(component).toMatchSnapshot();
+});
diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/layer_template.tsx b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/layer_template.tsx
new file mode 100644
index 0000000000000..72618781902d2
--- /dev/null
+++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/layer_template.tsx
@@ -0,0 +1,459 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { FileLayer } from '@elastic/ems-client';
+import {
+ EuiComboBox,
+ EuiComboBoxOptionOption,
+ EuiFormRow,
+ EuiPanel,
+ EuiRadioGroup,
+ EuiSpacer,
+ EuiTitle,
+} from '@elastic/eui';
+import { IFieldType, IndexPattern } from 'src/plugins/data/public';
+import { RenderWizardArguments } from '../layer_wizard_registry';
+import { EMSFileSelect } from '../../../components/ems_file_select';
+import { GeoIndexPatternSelect } from '../../../components/geo_index_pattern_select';
+import { SingleFieldSelect } from '../../../components/single_field_select';
+import { getGeoFields, getSourceFields, getTermsFields } from '../../../index_pattern_util';
+import { getEmsFileLayers } from '../../../meta';
+import { getIndexPatternSelectComponent, getIndexPatternService } from '../../../kibana_services';
+import {
+ createEmsChoroplethLayerDescriptor,
+ createEsChoroplethLayerDescriptor,
+} from './create_choropleth_layer_descriptor';
+
+export enum BOUNDARIES_SOURCE {
+ ELASTICSEARCH = 'ELASTICSEARCH',
+ EMS = 'EMS',
+}
+
+const BOUNDARIES_OPTIONS = [
+ {
+ id: BOUNDARIES_SOURCE.EMS,
+ label: i18n.translate('xpack.maps.choropleth.boundaries.ems', {
+ defaultMessage: 'Administrative boundaries from Elastic Maps Service',
+ }),
+ },
+ {
+ id: BOUNDARIES_SOURCE.ELASTICSEARCH,
+ label: i18n.translate('xpack.maps.choropleth.boundaries.elasticsearch', {
+ defaultMessage: 'Points, lines, and polygons from Elasticsearch',
+ }),
+ },
+];
+
+interface State {
+ leftSource: BOUNDARIES_SOURCE;
+ leftEmsFileId: string | null;
+ leftEmsFields: Array>;
+ leftIndexPattern: IndexPattern | null;
+ leftGeoFields: IFieldType[];
+ leftJoinFields: IFieldType[];
+ leftGeoField: string | null;
+ leftEmsJoinField: string | null;
+ leftElasticsearchJoinField: string | null;
+ rightIndexPatternId: string | null;
+ rightIndexPatternTitle: string | null;
+ rightTermsFields: IFieldType[];
+ rightJoinField: string | null;
+}
+
+export class LayerTemplate extends Component {
+ private _isMounted: boolean = false;
+
+ state = {
+ leftSource: BOUNDARIES_SOURCE.EMS,
+ leftEmsFileId: null,
+ leftEmsFields: [],
+ leftIndexPattern: null,
+ leftGeoFields: [],
+ leftJoinFields: [],
+ leftGeoField: null,
+ leftEmsJoinField: null,
+ leftElasticsearchJoinField: null,
+ rightIndexPatternId: null,
+ rightIndexPatternTitle: null,
+ rightTermsFields: [],
+ rightJoinField: null,
+ };
+
+ componentWillUnmount() {
+ this._isMounted = false;
+ }
+
+ componentDidMount() {
+ this._isMounted = true;
+ }
+
+ _loadRightFields = async (indexPatternId: string) => {
+ this.setState({ rightTermsFields: [], rightIndexPatternTitle: null });
+
+ let indexPattern;
+ try {
+ indexPattern = await getIndexPatternService().get(indexPatternId);
+ } catch (err) {
+ return;
+ }
+
+ // method may be called again before 'get' returns
+ // ignore response when fetched index pattern does not match active index pattern
+ if (!this._isMounted || indexPatternId !== this.state.rightIndexPatternId) {
+ return;
+ }
+
+ this.setState({
+ rightTermsFields: getTermsFields(indexPattern.fields),
+ rightIndexPatternTitle: indexPattern.title,
+ });
+ };
+
+ _loadEmsFileFields = async () => {
+ const emsFileLayers = await getEmsFileLayers();
+ const emsFileLayer = emsFileLayers.find((fileLayer: FileLayer) => {
+ return fileLayer.getId() === this.state.leftEmsFileId;
+ });
+
+ if (!this._isMounted || !emsFileLayer) {
+ return;
+ }
+
+ const leftEmsFields = emsFileLayer
+ .getFieldsInLanguage()
+ .filter((field) => {
+ return field.type === 'id';
+ })
+ .map((field) => {
+ return {
+ value: field.name,
+ label: field.description,
+ };
+ });
+ this.setState(
+ {
+ leftEmsFields,
+ leftEmsJoinField: leftEmsFields.length ? leftEmsFields[0].value : null,
+ },
+ this._previewLayer
+ );
+ };
+
+ _onLeftSourceChange = (optionId: string) => {
+ this.setState(
+ { leftSource: optionId as BOUNDARIES_SOURCE, rightJoinField: null },
+ this._previewLayer
+ );
+ };
+
+ _onLeftIndexPatternChange = (indexPattern: IndexPattern) => {
+ this.setState(
+ {
+ leftIndexPattern: indexPattern,
+ leftGeoFields: getGeoFields(indexPattern.fields),
+ leftJoinFields: getSourceFields(indexPattern.fields),
+ leftGeoField: null,
+ leftElasticsearchJoinField: null,
+ rightJoinField: null,
+ },
+ () => {
+ // make default geo field selection
+ if (this.state.leftGeoFields.length) {
+ // @ts-expect-error - avoid wrong "Property 'name' does not exist on type 'never'." compile error
+ this._onLeftGeoFieldSelect(this.state.leftGeoFields[0].name);
+ }
+ }
+ );
+ };
+
+ _onLeftGeoFieldSelect = (geoField?: string) => {
+ if (!geoField) {
+ return;
+ }
+ this.setState({ leftGeoField: geoField }, this._previewLayer);
+ };
+
+ _onLeftJoinFieldSelect = (joinField?: string) => {
+ if (!joinField) {
+ return;
+ }
+ this.setState({ leftElasticsearchJoinField: joinField }, this._previewLayer);
+ };
+
+ _onLeftEmsFileChange = (emFileId: string) => {
+ this.setState({ leftEmsFileId: emFileId, leftEmsJoinField: null, rightJoinField: null }, () => {
+ this._previewLayer();
+ this._loadEmsFileFields();
+ });
+ };
+
+ _onLeftEmsFieldChange = (selectedOptions: Array>) => {
+ if (selectedOptions.length === 0) {
+ return;
+ }
+
+ this.setState({ leftEmsJoinField: selectedOptions[0].value! }, this._previewLayer);
+ };
+
+ _onRightIndexPatternChange = (indexPatternId: string) => {
+ if (!indexPatternId) {
+ return;
+ }
+
+ this.setState(
+ {
+ rightIndexPatternId: indexPatternId,
+ rightJoinField: null,
+ },
+ () => {
+ this._previewLayer();
+ this._loadRightFields(indexPatternId);
+ }
+ );
+ };
+
+ _onRightJoinFieldSelect = (joinField?: string) => {
+ if (!joinField) {
+ return;
+ }
+ this.setState({ rightJoinField: joinField }, this._previewLayer);
+ };
+
+ _isLeftConfigComplete() {
+ if (this.state.leftSource === BOUNDARIES_SOURCE.ELASTICSEARCH) {
+ return (
+ !!this.state.leftIndexPattern &&
+ !!this.state.leftGeoField &&
+ !!this.state.leftElasticsearchJoinField
+ );
+ } else {
+ return !!this.state.leftEmsFileId && !!this.state.leftEmsJoinField;
+ }
+ }
+
+ _isRightConfigComplete() {
+ return !!this.state.rightIndexPatternId && !!this.state.rightJoinField;
+ }
+
+ _previewLayer() {
+ if (!this._isLeftConfigComplete() || !this._isRightConfigComplete()) {
+ this.props.previewLayers([]);
+ return;
+ }
+
+ const layerDescriptor =
+ this.state.leftSource === BOUNDARIES_SOURCE.ELASTICSEARCH
+ ? createEsChoroplethLayerDescriptor({
+ // @ts-expect-error - avoid wrong "Property 'id' does not exist on type 'never'." compile error
+ leftIndexPatternId: this.state.leftIndexPattern!.id,
+ leftGeoField: this.state.leftGeoField!,
+ leftJoinField: this.state.leftElasticsearchJoinField!,
+ rightIndexPatternId: this.state.rightIndexPatternId!,
+ rightIndexPatternTitle: this.state.rightIndexPatternTitle!,
+ rightTermField: this.state.rightJoinField!,
+ })
+ : createEmsChoroplethLayerDescriptor({
+ leftEmsFileId: this.state.leftEmsFileId!,
+ leftEmsField: this.state.leftEmsJoinField!,
+ rightIndexPatternId: this.state.rightIndexPatternId!,
+ rightIndexPatternTitle: this.state.rightIndexPatternTitle!,
+ rightTermField: this.state.rightJoinField!,
+ });
+
+ this.props.previewLayers([layerDescriptor]);
+ }
+
+ _renderLeftSourceForm() {
+ if (this.state.leftSource === BOUNDARIES_SOURCE.ELASTICSEARCH) {
+ let geoFieldSelect;
+ if (this.state.leftGeoFields.length) {
+ geoFieldSelect = (
+
+
+
+ );
+ }
+ let joinFieldSelect;
+ if (this.state.leftJoinFields.length) {
+ joinFieldSelect = (
+
+
+
+ );
+ }
+ return (
+ <>
+
+ {geoFieldSelect}
+ {joinFieldSelect}
+ >
+ );
+ } else {
+ let emsFieldSelect;
+ if (this.state.leftEmsFields.length) {
+ let selectedOption;
+ if (this.state.leftEmsJoinField) {
+ selectedOption = this.state.leftEmsFields.find(
+ (option: EuiComboBoxOptionOption) => {
+ return this.state.leftEmsJoinField === option.value;
+ }
+ );
+ }
+ emsFieldSelect = (
+
+
+
+ );
+ }
+ return (
+ <>
+
+ {emsFieldSelect}
+ >
+ );
+ }
+ }
+
+ _renderLeftPanel() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {this._renderLeftSourceForm()}
+
+ );
+ }
+
+ _renderRightPanel() {
+ if (!this._isLeftConfigComplete()) {
+ return null;
+ }
+ const IndexPatternSelect = getIndexPatternSelectComponent();
+
+ let joinFieldSelect;
+ if (this.state.rightTermsFields.length) {
+ joinFieldSelect = (
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {joinFieldSelect}
+
+ );
+ }
+
+ render() {
+ return (
+ <>
+ {this._renderLeftPanel()}
+
+
+
+ {this._renderRightPanel()}
+ >
+ );
+ }
+}
diff --git a/x-pack/plugins/maps/public/classes/layers/load_layer_wizards.ts b/x-pack/plugins/maps/public/classes/layers/load_layer_wizards.ts
index 8357971a3778f..37bcb3b23c072 100644
--- a/x-pack/plugins/maps/public/classes/layers/load_layer_wizards.ts
+++ b/x-pack/plugins/maps/public/classes/layers/load_layer_wizards.ts
@@ -26,6 +26,7 @@ import { wmsLayerWizardConfig } from '../sources/wms_source';
import { mvtVectorSourceWizardConfig } from '../sources/mvt_single_layer_vector_source';
import { ObservabilityLayerWizardConfig } from './solution_layers/observability';
import { SecurityLayerWizardConfig } from './solution_layers/security';
+import { choroplethLayerWizardConfig } from './choropleth_layer_wizard';
import { getEnableVectorTiles } from '../../kibana_services';
let registered = false;
@@ -41,6 +42,7 @@ export function registerLayerWizards() {
// @ts-ignore
registerLayerWizard(esDocumentsLayerWizardConfig);
// @ts-ignore
+ registerLayerWizard(choroplethLayerWizardConfig);
registerLayerWizard(clustersLayerWizardConfig);
// @ts-ignore
registerLayerWizard(heatmapLayerWizardConfig);
diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/observability_layer_template.tsx b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/observability_layer_template.tsx
index 3f3c556dcae1e..3fe640a135aa6 100644
--- a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/observability_layer_template.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/observability_layer_template.tsx
@@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { Component, Fragment } from 'react';
+import React, { Component } from 'react';
+import { EuiPanel } from '@elastic/eui';
import { RenderWizardArguments } from '../../layer_wizard_registry';
import { LayerSelect, OBSERVABILITY_LAYER_TYPE } from './layer_select';
import { getMetricOptionsForLayer, MetricSelect, OBSERVABILITY_METRIC_TYPE } from './metric_select';
@@ -63,7 +64,7 @@ export class ObservabilityLayerTemplate extends Component
+
-
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_template.tsx b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_template.tsx
index eda489c88fda2..b20f57f4c276d 100644
--- a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_template.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/security_layer_template.tsx
@@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { Component, Fragment } from 'react';
+import React, { Component } from 'react';
+import { EuiPanel } from '@elastic/eui';
import { RenderWizardArguments } from '../../layer_wizard_registry';
import { IndexPatternSelect } from './index_pattern_select';
import { createSecurityLayerDescriptors } from './create_layer_descriptors';
@@ -44,12 +45,12 @@ export class SecurityLayerTemplate extends Component
+
-
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/client_file_source/create_client_file_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/client_file_source/create_client_file_source_editor.tsx
index 344bdc92489e0..cf2692393192d 100644
--- a/x-pack/plugins/maps/public/classes/sources/client_file_source/create_client_file_source_editor.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/client_file_source/create_client_file_source_editor.tsx
@@ -5,6 +5,7 @@
*/
import React, { Component } from 'react';
+import { EuiPanel } from '@elastic/eui';
import { IFieldType } from 'src/plugins/data/public';
import {
ES_GEO_FIELD_TYPE,
@@ -146,15 +147,17 @@ export class ClientFileCreateSourceEditor extends Component
+
+
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/create_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/create_source_editor.tsx
index a78a49032503b..ef15a8d786607 100644
--- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/create_source_editor.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/create_source_editor.tsx
@@ -5,95 +5,33 @@
*/
import React, { Component } from 'react';
-import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui';
-
-import { i18n } from '@kbn/i18n';
-import { FileLayer } from '@elastic/ems-client';
-import { getEmsFileLayers } from '../../../meta';
-import { getEmsUnavailableMessage } from '../ems_unavailable_message';
+import { EuiPanel } from '@elastic/eui';
import { EMSFileSourceDescriptor } from '../../../../common/descriptor_types';
+import { EMSFileSelect } from '../../../components/ems_file_select';
interface Props {
onSourceConfigChange: (sourceConfig: Partial) => void;
}
interface State {
- hasLoadedOptions: boolean;
- emsFileOptions: Array>;
- selectedOption: EuiComboBoxOptionOption | null;
+ emsFileId: string | null;
}
export class EMSFileCreateSourceEditor extends Component {
- private _isMounted: boolean = false;
-
state = {
- hasLoadedOptions: false,
- emsFileOptions: [],
- selectedOption: null,
- };
-
- _loadFileOptions = async () => {
- const fileLayers: FileLayer[] = await getEmsFileLayers();
- const options = fileLayers.map((fileLayer) => {
- return {
- value: fileLayer.getId(),
- label: fileLayer.getDisplayName(),
- };
- });
- if (this._isMounted) {
- this.setState({
- hasLoadedOptions: true,
- emsFileOptions: options,
- });
- }
+ emsFileId: null,
};
- componentWillUnmount() {
- this._isMounted = false;
- }
-
- componentDidMount() {
- this._isMounted = true;
- this._loadFileOptions();
- }
-
- _onChange = (selectedOptions: Array>) => {
- if (selectedOptions.length === 0) {
- return;
- }
-
- this.setState({ selectedOption: selectedOptions[0] });
-
- const emsFileId = selectedOptions[0].value;
+ _onChange = (emsFileId: string) => {
+ this.setState({ emsFileId });
this.props.onSourceConfigChange({ id: emsFileId });
};
render() {
- if (!this.state.hasLoadedOptions) {
- // TODO display loading message
- return null;
- }
-
return (
-
-
-
+
+
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.js
index 3931e441ff254..2b54e00cae739 100644
--- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.js
+++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.js
@@ -5,10 +5,10 @@
*/
import React from 'react';
-import { EuiSelect, EuiFormRow } from '@elastic/eui';
+import { EuiSelect, EuiFormRow, EuiPanel } from '@elastic/eui';
import { getEmsTmsServices } from '../../../meta';
-import { getEmsUnavailableMessage } from '../ems_unavailable_message';
+import { getEmsUnavailableMessage } from '../../../components/ems_unavailable_message';
import { i18n } from '@kbn/i18n';
export const AUTO_SELECT = 'auto_select';
@@ -71,23 +71,25 @@ export class TileServiceSelect extends React.Component {
}
return (
-
-
-
+
+
+
+
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js
index 24edf0251c153..ebe312d73ecce 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js
@@ -4,17 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import _ from 'lodash';
-import React, { Fragment, Component } from 'react';
+import React, { Component } from 'react';
import PropTypes from 'prop-types';
-import { ES_GEO_FIELD_TYPES } from '../../../../common/constants';
import { SingleFieldSelect } from '../../../components/single_field_select';
-import { getIndexPatternService, getIndexPatternSelectComponent } from '../../../kibana_services';
-import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout';
+import { GeoIndexPatternSelect } from '../../../components/geo_index_pattern_select';
import { i18n } from '@kbn/i18n';
-import { EuiFormRow, EuiSpacer } from '@elastic/eui';
+import { EuiFormRow, EuiPanel } from '@elastic/eui';
import {
getFieldsWithGeoTileAgg,
getGeoFields,
@@ -33,76 +30,26 @@ export class CreateSourceEditor extends Component {
};
state = {
- isLoadingIndexPattern: false,
- indexPatternId: '',
+ indexPattern: null,
geoField: '',
requestType: this.props.requestType,
- noGeoIndexPatternsExist: false,
};
- componentWillUnmount() {
- this._isMounted = false;
- }
-
- componentDidMount() {
- this._isMounted = true;
- }
-
- onIndexPatternSelect = (indexPatternId) => {
+ onIndexPatternSelect = (indexPattern) => {
this.setState(
{
- indexPatternId,
+ indexPattern,
},
- this.loadIndexPattern.bind(null, indexPatternId)
+ () => {
+ //make default selection
+ const geoFieldsWithGeoTileAgg = getFieldsWithGeoTileAgg(indexPattern.fields);
+ if (geoFieldsWithGeoTileAgg[0]) {
+ this._onGeoFieldSelect(geoFieldsWithGeoTileAgg[0].name);
+ }
+ }
);
};
- loadIndexPattern = (indexPatternId) => {
- this.setState(
- {
- isLoadingIndexPattern: true,
- indexPattern: undefined,
- geoField: undefined,
- },
- this.debouncedLoad.bind(null, indexPatternId)
- );
- };
-
- debouncedLoad = _.debounce(async (indexPatternId) => {
- if (!indexPatternId || indexPatternId.length === 0) {
- return;
- }
-
- let indexPattern;
- try {
- indexPattern = await getIndexPatternService().get(indexPatternId);
- } catch (err) {
- // index pattern no longer exists
- return;
- }
-
- if (!this._isMounted) {
- return;
- }
-
- // props.indexPatternId may be updated before getIndexPattern returns
- // ignore response when fetched index pattern does not match active index pattern
- if (indexPattern.id !== indexPatternId) {
- return;
- }
-
- this.setState({
- isLoadingIndexPattern: false,
- indexPattern: indexPattern,
- });
-
- //make default selection
- const geoFieldsWithGeoTileAgg = getFieldsWithGeoTileAgg(indexPattern.fields);
- if (geoFieldsWithGeoTileAgg[0]) {
- this._onGeoFieldSelect(geoFieldsWithGeoTileAgg[0].name);
- }
- }, 300);
-
_onGeoFieldSelect = (geoField) => {
this.setState(
{
@@ -122,17 +69,13 @@ export class CreateSourceEditor extends Component {
};
previewLayer = () => {
- const { indexPatternId, geoField, requestType } = this.state;
+ const { indexPattern, geoField, requestType } = this.state;
const sourceConfig =
- indexPatternId && geoField ? { indexPatternId, geoField, requestType } : null;
+ indexPattern && geoField ? { indexPatternId: indexPattern.id, geoField, requestType } : null;
this.props.onSourceConfigChange(sourceConfig);
};
- _onNoIndexPatterns = () => {
- this.setState({ noGeoIndexPatternsExist: true });
- };
-
_renderGeoSelect() {
if (!this.state.indexPattern) {
return null;
@@ -170,50 +113,16 @@ export class CreateSourceEditor extends Component {
);
}
- _renderIndexPatternSelect() {
- const IndexPatternSelect = getIndexPatternSelectComponent();
-
+ render() {
return (
-
-
+
-
- );
- }
-
- _renderNoIndexPatternWarning() {
- if (!this.state.noGeoIndexPatternsExist) {
- return null;
- }
-
- return (
-
-
-
-
- );
- }
-
- render() {
- return (
-
- {this._renderNoIndexPatternWarning()}
- {this._renderIndexPatternSelect()}
{this._renderGeoSelect()}
{this._renderRenderAsSelect()}
-
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/create_source_editor.js
index 38a5850537200..3fe3d536ff809 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/create_source_editor.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/create_source_editor.js
@@ -13,7 +13,7 @@ import { getIndexPatternService, getIndexPatternSelectComponent } from '../../..
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { EuiFormRow, EuiCallOut } from '@elastic/eui';
+import { EuiFormRow, EuiCallOut, EuiPanel } from '@elastic/eui';
import { getFieldsWithGeoTileAgg } from '../../../index_pattern_util';
import { ES_GEO_FIELD_TYPE } from '../../../../common/constants';
@@ -200,11 +200,11 @@ export class CreateSourceEditor extends Component {
}
return (
-
+
{callout}
{this._renderIndexPatternSelect()}
{this._renderGeoSelects()}
-
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap
index 2b04da9251756..8ebb389472f74 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap
+++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap
@@ -210,8 +210,10 @@ exports[`should render top hits form when scaling type is TOP_HITS 1`] = `
diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.js
index 73abfd87e4797..0423d70b3f427 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.js
@@ -4,16 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import _ from 'lodash';
import React, { Fragment, Component } from 'react';
import PropTypes from 'prop-types';
-import { EuiFormRow, EuiSpacer } from '@elastic/eui';
+import { EuiFormRow, EuiPanel, EuiSpacer } from '@elastic/eui';
import { SingleFieldSelect } from '../../../components/single_field_select';
-import { getIndexPatternService, getIndexPatternSelectComponent } from '../../../kibana_services';
-import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout';
+import { GeoIndexPatternSelect } from '../../../components/geo_index_pattern_select';
import { i18n } from '@kbn/i18n';
-import { ES_GEO_FIELD_TYPES, SCALING_TYPES } from '../../../../common/constants';
+import { SCALING_TYPES } from '../../../../common/constants';
import { DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants';
import { ScalingForm } from './scaling_form';
import {
@@ -45,80 +43,33 @@ export class CreateSourceEditor extends Component {
};
state = {
- isLoadingIndexPattern: false,
- noGeoIndexPatternsExist: false,
...RESET_INDEX_PATTERN_STATE,
};
- componentWillUnmount() {
- this._isMounted = false;
- }
-
- componentDidMount() {
- this._isMounted = true;
- }
-
- _onIndexPatternSelect = (indexPatternId) => {
- this.setState(
- {
- indexPatternId,
- },
- this._loadIndexPattern(indexPatternId)
- );
- };
+ _onIndexPatternSelect = (indexPattern) => {
+ const geoFields = getGeoFields(indexPattern.fields);
- _loadIndexPattern = (indexPatternId) => {
this.setState(
{
- isLoadingIndexPattern: true,
...RESET_INDEX_PATTERN_STATE,
+ indexPattern,
+ geoFields,
},
- this._debouncedLoad.bind(null, indexPatternId)
+ () => {
+ if (geoFields.length) {
+ // make default selection, prefer aggregatable field over the first available
+ const firstAggregatableGeoField = geoFields.find((geoField) => {
+ return geoField.aggregatable;
+ });
+ const defaultGeoFieldName = firstAggregatableGeoField
+ ? firstAggregatableGeoField
+ : geoFields[0];
+ this._onGeoFieldSelect(defaultGeoFieldName.name);
+ }
+ }
);
};
- _debouncedLoad = _.debounce(async (indexPatternId) => {
- if (!indexPatternId || indexPatternId.length === 0) {
- return;
- }
-
- let indexPattern;
- try {
- indexPattern = await getIndexPatternService().get(indexPatternId);
- } catch (err) {
- // index pattern no longer exists
- return;
- }
-
- if (!this._isMounted) {
- return;
- }
-
- // props.indexPatternId may be updated before getIndexPattern returns
- // ignore response when fetched index pattern does not match active index pattern
- if (indexPattern.id !== indexPatternId) {
- return;
- }
-
- const geoFields = getGeoFields(indexPattern.fields);
- this.setState({
- isLoadingIndexPattern: false,
- indexPattern: indexPattern,
- geoFields,
- });
-
- if (geoFields.length) {
- // make default selection, prefer aggregatable field over the first available
- const firstAggregatableGeoField = geoFields.find((geoField) => {
- return geoField.aggregatable;
- });
- const defaultGeoFieldName = firstAggregatableGeoField
- ? firstAggregatableGeoField
- : geoFields[0];
- this._onGeoFieldSelect(defaultGeoFieldName.name);
- }
- }, 300);
-
_onGeoFieldSelect = (geoFieldName) => {
// Respect previous scaling type selection unless newly selected geo field does not support clustering.
const scalingType =
@@ -146,7 +97,7 @@ export class CreateSourceEditor extends Component {
_previewLayer = () => {
const {
- indexPatternId,
+ indexPattern,
geoFieldName,
filterByMapBounds,
scalingType,
@@ -155,9 +106,9 @@ export class CreateSourceEditor extends Component {
} = this.state;
const sourceConfig =
- indexPatternId && geoFieldName
+ indexPattern && geoFieldName
? {
- indexPatternId,
+ indexPatternId: indexPattern.id,
geoField: geoFieldName,
filterByMapBounds,
scalingType,
@@ -168,10 +119,6 @@ export class CreateSourceEditor extends Component {
this.props.onSourceConfigChange(sourceConfig);
};
- _onNoIndexPatterns = () => {
- this.setState({ noGeoIndexPatternsExist: true });
- };
-
_renderGeoSelect() {
if (!this.state.indexPattern) {
return;
@@ -205,7 +152,7 @@ export class CreateSourceEditor extends Component {
-
-
-
- );
- }
-
render() {
- const IndexPatternSelect = getIndexPatternSelectComponent();
-
return (
-
- {this._renderNoIndexPatternWarning()}
-
-
-
-
+
+
{this._renderGeoSelect()}
{this._renderScalingPanel()}
-
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx
index 4598b1467229d..1ec6d2a1ff671 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx
@@ -26,7 +26,7 @@ export function createDefaultLayerDescriptor(sourceConfig: unknown, mapColors: s
export const esDocumentsLayerWizardConfig: LayerWizard = {
categories: [LAYER_WIZARD_CATEGORY.ELASTICSEARCH],
description: i18n.translate('xpack.maps.source.esSearchDescription', {
- defaultMessage: 'Vector data from a Kibana index pattern',
+ defaultMessage: 'Points, lines, and polygons from Elasticsearch',
}),
icon: 'logoElasticsearch',
renderWizard: ({ previewLayers, mapColors }: RenderWizardArguments) => {
diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.test.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.test.tsx
index 3ec746223c7cf..6e56c179b4ead 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.test.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.test.tsx
@@ -25,6 +25,7 @@ const defaultProps = {
scalingType: SCALING_TYPES.LIMIT,
supportsClustering: true,
termFields: [],
+ topHitsSplitField: null,
topHitsSize: 1,
};
diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx
index a998fe3569835..816db6a98d593 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx
@@ -40,7 +40,7 @@ interface Props {
supportsClustering: boolean;
clusteringDisabledReason?: string | null;
termFields: IFieldType[];
- topHitsSplitField?: string;
+ topHitsSplitField: string | null;
topHitsSize: number;
}
@@ -90,6 +90,9 @@ export class ScalingForm extends Component {
};
_onTopHitsSplitFieldChange = (topHitsSplitField?: string) => {
+ if (!topHitsSplitField) {
+ return;
+ }
this.props.onChange({ propName: 'topHitsSplitField', value: topHitsSplitField });
};
@@ -141,6 +144,7 @@ export class ScalingForm extends Component {
value={this.props.topHitsSplitField}
onChange={this._onTopHitsSplitFieldChange}
fields={this.props.termFields}
+ isClearable={false}
compressed
/>
diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/create_source_editor.js
index 5e28916e79f3f..82f80e7fe484b 100644
--- a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/create_source_editor.js
+++ b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/create_source_editor.js
@@ -6,7 +6,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { EuiSelect, EuiFormRow } from '@elastic/eui';
+import { EuiSelect, EuiFormRow, EuiPanel } from '@elastic/eui';
import { getKibanaRegionList } from '../../../meta';
import { i18n } from '@kbn/i18n';
@@ -31,19 +31,21 @@ export function CreateSourceEditor({ onSourceConfigChange }) {
: null;
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/create_source_editor.js
index a0a507ff9d32d..1cbf4c1a87de3 100644
--- a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/create_source_editor.js
+++ b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/create_source_editor.js
@@ -6,7 +6,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { EuiFieldText, EuiFormRow } from '@elastic/eui';
+import { EuiFieldText, EuiFormRow, EuiPanel } from '@elastic/eui';
import { getKibanaTileMap } from '../../../meta';
import { i18n } from '@kbn/i18n';
@@ -19,21 +19,23 @@ export function CreateSourceEditor({ onSourceConfigChange }) {
}
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source_editor.tsx
index 7a4b8d43811da..760b8c676cb37 100644
--- a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source_editor.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source_editor.tsx
@@ -5,9 +5,9 @@
*/
/* eslint-disable @typescript-eslint/consistent-type-definitions */
-import React, { Fragment, Component, ChangeEvent } from 'react';
+import React, { Component, ChangeEvent } from 'react';
import _ from 'lodash';
-import { EuiFieldText, EuiFormRow } from '@elastic/eui';
+import { EuiFieldText, EuiFormRow, EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { MAX_ZOOM, MIN_ZOOM } from '../../../../common/constants';
import { ValidatedDualRange, Value } from '../../../../../../../src/plugins/kibana_react/public';
@@ -85,7 +85,7 @@ export class MVTSingleLayerVectorSourceEditor extends Component {
render() {
return (
-
+
{
}
)}
/>
-
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_create_source_editor.js
index df00faf43daa3..ce9af42117683 100644
--- a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_create_source_editor.js
+++ b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_create_source_editor.js
@@ -13,7 +13,7 @@ import {
EuiComboBox,
EuiFieldText,
EuiFormRow,
- EuiForm,
+ EuiPanel,
EuiSpacer,
} from '@elastic/eui';
import { WmsClient } from './wms_client';
@@ -289,7 +289,7 @@ export class WMSCreateSourceEditor extends Component {
render() {
return (
-
+
+
);
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.tsx b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.tsx
index 715ff0e4c2fdd..bf5f2c3dfe04d 100644
--- a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.tsx
@@ -5,9 +5,9 @@
*/
/* eslint-disable @typescript-eslint/consistent-type-definitions */
-import React, { Fragment, Component, ChangeEvent } from 'react';
+import React, { Component, ChangeEvent } from 'react';
import _ from 'lodash';
-import { EuiFormRow, EuiFieldText } from '@elastic/eui';
+import { EuiFormRow, EuiFieldText, EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { AttributionDescriptor } from '../../../../common/descriptor_types';
@@ -77,7 +77,7 @@ export class XYZTMSEditor extends Component {
render() {
const { attributionText, attributionUrl } = this.state;
return (
-
+
{
}
/>
-
+
);
}
}
diff --git a/x-pack/plugins/maps/public/components/__snapshots__/geo_index_pattern_select.test.tsx.snap b/x-pack/plugins/maps/public/components/__snapshots__/geo_index_pattern_select.test.tsx.snap
new file mode 100644
index 0000000000000..809fe61862513
--- /dev/null
+++ b/x-pack/plugins/maps/public/components/__snapshots__/geo_index_pattern_select.test.tsx.snap
@@ -0,0 +1,104 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render 1`] = `
+
+
+
+
+
+`;
+
+exports[`should render no index pattern warning when there are no matching index patterns 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/x-pack/plugins/maps/public/components/ems_file_select.tsx b/x-pack/plugins/maps/public/components/ems_file_select.tsx
new file mode 100644
index 0000000000000..f66e813608ce1
--- /dev/null
+++ b/x-pack/plugins/maps/public/components/ems_file_select.tsx
@@ -0,0 +1,105 @@
+/*
+ * 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 { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiSelect } from '@elastic/eui';
+
+import { i18n } from '@kbn/i18n';
+import { FileLayer } from '@elastic/ems-client';
+import { getEmsFileLayers } from '../meta';
+import { getEmsUnavailableMessage } from './ems_unavailable_message';
+
+interface Props {
+ onChange: (emsFileId: string) => void;
+ value: string | null;
+}
+
+interface State {
+ hasLoadedOptions: boolean;
+ emsFileOptions: Array>;
+}
+
+export class EMSFileSelect extends Component {
+ private _isMounted: boolean = false;
+
+ state = {
+ hasLoadedOptions: false,
+ emsFileOptions: [],
+ };
+
+ _loadFileOptions = async () => {
+ const fileLayers: FileLayer[] = await getEmsFileLayers();
+ const options = fileLayers.map((fileLayer) => {
+ return {
+ value: fileLayer.getId(),
+ label: fileLayer.getDisplayName(),
+ };
+ });
+ if (this._isMounted) {
+ this.setState({
+ hasLoadedOptions: true,
+ emsFileOptions: options,
+ });
+ }
+ };
+
+ componentWillUnmount() {
+ this._isMounted = false;
+ }
+
+ componentDidMount() {
+ this._isMounted = true;
+ this._loadFileOptions();
+ }
+
+ _onChange = (selectedOptions: Array>) => {
+ if (selectedOptions.length === 0) {
+ return;
+ }
+
+ this.props.onChange(selectedOptions[0].value!);
+ };
+
+ _renderSelect() {
+ if (!this.state.hasLoadedOptions) {
+ return ;
+ }
+
+ const selectedOption = this.state.emsFileOptions.find(
+ (option: EuiComboBoxOptionOption) => {
+ return option.value === this.props.value;
+ }
+ );
+
+ return (
+
+ );
+ }
+
+ render() {
+ return (
+
+ {this._renderSelect()}
+
+ );
+ }
+}
diff --git a/x-pack/plugins/maps/public/classes/sources/ems_unavailable_message.ts b/x-pack/plugins/maps/public/components/ems_unavailable_message.tsx
similarity index 93%
rename from x-pack/plugins/maps/public/classes/sources/ems_unavailable_message.ts
rename to x-pack/plugins/maps/public/components/ems_unavailable_message.tsx
index 748016cf889e2..dea161fafd609 100644
--- a/x-pack/plugins/maps/public/classes/sources/ems_unavailable_message.ts
+++ b/x-pack/plugins/maps/public/components/ems_unavailable_message.tsx
@@ -6,7 +6,7 @@
import { i18n } from '@kbn/i18n';
// @ts-ignore
-import { getIsEmsEnabled } from '../../kibana_services';
+import { getIsEmsEnabled } from '../kibana_services';
export function getEmsUnavailableMessage(): string {
const isEmsEnabled = getIsEmsEnabled();
diff --git a/x-pack/plugins/maps/public/components/geo_index_pattern_select.test.tsx b/x-pack/plugins/maps/public/components/geo_index_pattern_select.test.tsx
new file mode 100644
index 0000000000000..74d29e7d7e59a
--- /dev/null
+++ b/x-pack/plugins/maps/public/components/geo_index_pattern_select.test.tsx
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+jest.mock('../kibana_services', () => {
+ const MockIndexPatternSelect = (props: unknown) => {
+ return ;
+ };
+ const MockHttp = {
+ basePath: {
+ prepend: (path: string) => {
+ return `abc/${path}`;
+ },
+ },
+ };
+ return {
+ getIndexPatternSelectComponent: () => {
+ return MockIndexPatternSelect;
+ },
+ getHttp: () => {
+ return MockHttp;
+ },
+ };
+});
+
+import React from 'react';
+import { shallow } from 'enzyme';
+import { GeoIndexPatternSelect } from './geo_index_pattern_select';
+
+test('should render', async () => {
+ const component = shallow( {}} value={'indexPatternId'} />);
+
+ expect(component).toMatchSnapshot();
+});
+
+test('should render no index pattern warning when there are no matching index patterns', async () => {
+ const component = shallow( {}} value={'indexPatternId'} />);
+ component.setState({ noGeoIndexPatternsExist: true });
+ expect(component).toMatchSnapshot();
+});
diff --git a/x-pack/plugins/maps/public/components/geo_index_pattern_select.tsx b/x-pack/plugins/maps/public/components/geo_index_pattern_select.tsx
new file mode 100644
index 0000000000000..ae23d9d97de86
--- /dev/null
+++ b/x-pack/plugins/maps/public/components/geo_index_pattern_select.tsx
@@ -0,0 +1,139 @@
+/*
+ * 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 { EuiCallOut, EuiFormRow, EuiLink, EuiSpacer } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { IndexPattern } from 'src/plugins/data/public';
+import {
+ getIndexPatternSelectComponent,
+ getIndexPatternService,
+ getHttp,
+} from '../kibana_services';
+import { ES_GEO_FIELD_TYPES } from '../../common/constants';
+
+interface Props {
+ onChange: (indexPattern: IndexPattern) => void;
+ value: string | null;
+}
+
+interface State {
+ noGeoIndexPatternsExist: boolean;
+}
+
+export class GeoIndexPatternSelect extends Component {
+ private _isMounted: boolean = false;
+
+ state = {
+ noGeoIndexPatternsExist: false,
+ };
+
+ componentWillUnmount() {
+ this._isMounted = false;
+ }
+
+ componentDidMount() {
+ this._isMounted = true;
+ }
+
+ _onIndexPatternSelect = async (indexPatternId: string) => {
+ if (!indexPatternId || indexPatternId.length === 0) {
+ return;
+ }
+
+ let indexPattern;
+ try {
+ indexPattern = await getIndexPatternService().get(indexPatternId);
+ } catch (err) {
+ return;
+ }
+
+ // method may be called again before 'get' returns
+ // ignore response when fetched index pattern does not match active index pattern
+ if (this._isMounted && indexPattern.id === indexPatternId) {
+ this.props.onChange(indexPattern);
+ }
+ };
+
+ _onNoIndexPatterns = () => {
+ this.setState({ noGeoIndexPatternsExist: true });
+ };
+
+ _renderNoIndexPatternWarning() {
+ if (!this.state.noGeoIndexPatternsExist) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+ }
+
+ render() {
+ const IndexPatternSelect = getIndexPatternSelectComponent();
+ return (
+ <>
+ {this._renderNoIndexPatternWarning()}
+
+
+
+
+ >
+ );
+ }
+}
diff --git a/x-pack/plugins/maps/public/components/no_index_pattern_callout.js b/x-pack/plugins/maps/public/components/no_index_pattern_callout.js
deleted file mode 100644
index 2daab8c6322dd..0000000000000
--- a/x-pack/plugins/maps/public/components/no_index_pattern_callout.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { getHttp } from '../kibana_services';
-import React from 'react';
-import { EuiCallOut, EuiLink } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-
-export function NoIndexPatternCallout() {
- const http = getHttp();
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/x-pack/plugins/maps/public/components/single_field_select.tsx b/x-pack/plugins/maps/public/components/single_field_select.tsx
index eb3a28be0efc0..2895479c4fd0e 100644
--- a/x-pack/plugins/maps/public/components/single_field_select.tsx
+++ b/x-pack/plugins/maps/public/components/single_field_select.tsx
@@ -49,7 +49,7 @@ type Props = Omit<
> & {
fields?: IFieldType[];
onChange: (fieldName?: string) => void;
- value?: string; // index pattern field name
+ value: string | null; // index pattern field name
isFieldDisabled?: (field: IFieldType) => boolean;
getFieldDisabledReason?: (field: IFieldType) => string | null;
};
diff --git a/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/flyout_body.tsx b/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/flyout_body.tsx
index 38474b84114fa..3f493ef7d4355 100644
--- a/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/flyout_body.tsx
+++ b/x-pack/plugins/maps/public/connected_components/add_layer_panel/flyout_body/flyout_body.tsx
@@ -5,7 +5,7 @@
*/
import React, { Fragment } from 'react';
-import { EuiButtonEmpty, EuiPanel, EuiSpacer } from '@elastic/eui';
+import { EuiButtonEmpty, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { LayerWizardSelect } from './layer_wizard_select';
import { LayerWizard, RenderWizardArguments } from '../../../classes/layers/layer_wizard_registry';
@@ -50,7 +50,7 @@ export const FlyoutBody = (props: Props) => {
return (
{backButton}
- {props.layerWizard.renderWizard(renderWizardArgs)}
+ {props.layerWizard.renderWizard(renderWizardArgs)}
);
}
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 29ea4426ac5f6..cb2d3e60be9ac 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -9155,7 +9155,6 @@
"xpack.maps.source.ems.disabledDescription": "Elastic Maps Service へのアクセスが無効になっています。システム管理者に問い合わせるか、kibana.yml で「map.includeElasticMapsService」を設定してください。",
"xpack.maps.source.ems.noAccessDescription": "Kibana が Elastic Maps Service にアクセスできません。システム管理者にお問い合わせください",
"xpack.maps.source.emsFile.layerLabel": "レイヤー",
- "xpack.maps.source.emsFile.selectPlaceholder": "EMS ベクターシェイプを選択",
"xpack.maps.source.emsFile.unableToFindIdErrorMessage": "ID {id} の EMS ベクターシェイプが見つかりません",
"xpack.maps.source.emsFileDescription": "Elastic Maps Service の行政区画のベクターシェイプ",
"xpack.maps.source.emsFileTitle": "ベクターシェイプ",
@@ -9171,8 +9170,6 @@
"xpack.maps.source.esGeoGrid.geofieldLabel": "地理空間フィールド",
"xpack.maps.source.esGeoGrid.geofieldPlaceholder": "ジオフィールドを選択",
"xpack.maps.source.esGeoGrid.gridRectangleDropdownOption": "グリッド",
- "xpack.maps.source.esGeoGrid.indexPatternLabel": "インデックスパターン",
- "xpack.maps.source.esGeoGrid.indexPatternPlaceholder": "インデックスパターンを選択",
"xpack.maps.source.esGeoGrid.pointsDropdownOption": "クラスター",
"xpack.maps.source.esGeoGrid.showAsLabel": "表示形式",
"xpack.maps.source.esGrid.coarseDropdownOption": "粗い",
@@ -9206,7 +9203,6 @@
"xpack.maps.source.esSearch.limitScalingLabel": "結果を {maxResultWindow} に限定。",
"xpack.maps.source.esSearch.loadErrorMessage": "インデックスパターン {id} が見つかりません",
"xpack.maps.source.esSearch.loadTooltipPropertiesErrorMsg": "ドキュメントが見つかりません。_id: {docId}",
- "xpack.maps.source.esSearch.selectIndexPatternPlaceholder": "インデックスパターンを選択",
"xpack.maps.source.esSearch.selectLabel": "ジオフィールドを選択",
"xpack.maps.source.esSearch.sortFieldLabel": "フィールド",
"xpack.maps.source.esSearch.sortFieldSelectPlaceholder": "ソートフィールドを選択",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 84a4fb41cdd76..b63212b11b1d6 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -9159,7 +9159,6 @@
"xpack.maps.source.ems.disabledDescription": "已禁用对 Elastic 地图服务的访问。让您的系统管理员在 kibana.yml 中设置“map.includeElasticMapsService”。",
"xpack.maps.source.ems.noAccessDescription": "Kibana 无法访问 Elastic 地图服务。请联系您的系统管理员",
"xpack.maps.source.emsFile.layerLabel": "图层",
- "xpack.maps.source.emsFile.selectPlaceholder": "选择 EMS 矢量形状",
"xpack.maps.source.emsFile.unableToFindIdErrorMessage": "找不到 ID {id} 的 EMS 矢量形状",
"xpack.maps.source.emsFileDescription": "来自 Elastic 地图服务的管理边界的矢量形状",
"xpack.maps.source.emsFileTitle": "矢量形状",
@@ -9175,8 +9174,6 @@
"xpack.maps.source.esGeoGrid.geofieldLabel": "地理空间字段",
"xpack.maps.source.esGeoGrid.geofieldPlaceholder": "选择地理字段",
"xpack.maps.source.esGeoGrid.gridRectangleDropdownOption": "网格",
- "xpack.maps.source.esGeoGrid.indexPatternLabel": "索引模式",
- "xpack.maps.source.esGeoGrid.indexPatternPlaceholder": "选择索引模式",
"xpack.maps.source.esGeoGrid.pointsDropdownOption": "集群",
"xpack.maps.source.esGeoGrid.showAsLabel": "显示为",
"xpack.maps.source.esGrid.coarseDropdownOption": "粗糙",
@@ -9210,7 +9207,6 @@
"xpack.maps.source.esSearch.limitScalingLabel": "将结果数限制到 {maxResultWindow}。",
"xpack.maps.source.esSearch.loadErrorMessage": "找不到索引模式 {id}",
"xpack.maps.source.esSearch.loadTooltipPropertiesErrorMsg": "找不到文档,_id:{docId}",
- "xpack.maps.source.esSearch.selectIndexPatternPlaceholder": "选择索引模式",
"xpack.maps.source.esSearch.selectLabel": "选择地理字段",
"xpack.maps.source.esSearch.sortFieldLabel": "字段",
"xpack.maps.source.esSearch.sortFieldSelectPlaceholder": "选择排序字段",