From 212b9b7b5a3f085424fc8b9863efcd722a840dac Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 29 Jan 2020 16:53:06 -0500 Subject: [PATCH 01/10] [Maps] type ahead for stop values for custom color maps and custom icon maps --- .../plugins/maps/public/kibana_services.js | 2 + .../maps/public/layers/sources/es_source.js | 22 ++++++ .../maps/public/layers/sources/source.js | 4 + .../styles/vector/components/stop_input.js | 75 +++++++++++++++++++ .../components/symbol/dynamic_icon_form.js | 1 + .../components/symbol/icon_map_select.js | 2 + .../vector/components/symbol/icon_stops.js | 17 +++-- .../properties/dynamic_size_property.js | 12 ++- .../properties/dynamic_style_property.js | 10 ++- .../layers/styles/vector/vector_style.js | 12 ++- 10 files changed, 146 insertions(+), 11 deletions(-) create mode 100644 x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js diff --git a/x-pack/legacy/plugins/maps/public/kibana_services.js b/x-pack/legacy/plugins/maps/public/kibana_services.js index 60fda398b4f3e..a8903c4911364 100644 --- a/x-pack/legacy/plugins/maps/public/kibana_services.js +++ b/x-pack/legacy/plugins/maps/public/kibana_services.js @@ -14,6 +14,8 @@ import { npStart } from 'ui/new_platform'; export const SPATIAL_FILTER_TYPE = esFilters.FILTERS.SPATIAL_FILTER; export { SearchSource } from '../../../../../src/plugins/data/public'; export const indexPatternService = npStart.plugins.data.indexPatterns; +console.log(npStart.plugins.data); +export const autocompleteService = npStart.plugins.data.autocomplete; let licenseId; export const setLicenseId = latestLicenseId => (licenseId = latestLicenseId); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js index 26cc7ece66753..d78d3038f870d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js @@ -6,6 +6,7 @@ import { AbstractVectorSource } from './vector_source'; import { + autocompleteService, fetchSearchSourceAndRecordWithInspector, indexPatternService, SearchSource, @@ -344,4 +345,25 @@ export class AbstractESSource extends AbstractVectorSource { return resp.aggregations; } + + getValueSuggestions = async (fieldName, query) => { + if (!fieldName) { + return []; + } + + try { + const indexPattern = await this.getIndexPattern(); + const field = indexPattern.fields.getByName(fieldName); + return await autocompleteService.getValueSuggestions({ + indexPattern, + field, + query, + }); + } catch (error) { + console.warn( + `Unable to fetch suggestions for field: ${fieldName}, query: ${query}, error: ${error.message}` + ); + return []; + } + }; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source.js b/x-pack/legacy/plugins/maps/public/layers/sources/source.js index cc5d62bbdfeef..3c6ddb74bedeb 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/source.js @@ -139,4 +139,8 @@ export class AbstractSource { async loadStylePropsMeta() { throw new Error(`Source#loadStylePropsMeta not implemented`); } + + async getValueSuggestions(/* fieldName, query */) { + return []; + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js new file mode 100644 index 0000000000000..a2951d11cd61e --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js @@ -0,0 +1,75 @@ +/* + * 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 _ from 'lodash'; +import React, { Component } from 'react'; + +import { EuiFieldText } from '@elastic/eui'; + +export class StopInput extends Component { + state = { + localValue: '', + suggestions: [], + isLoadingSuggestions: false, + }; + + static getDerivedStateFromProps(nextProps, prevState) { + if (nextProps.value === prevState.prevValue) { + return null; + } + + return { + prevValue: nextProps.value, + localValue: nextProps.value, + suggestions: [], + isLoadingSuggestions: false, + }; + } + + componentDidMount() { + this._isMounted = true; + } + + componentWillUnmount() { + this._isMounted = false; + } + + _onChange = e => { + this.setState({ localValue: e.target.value }, this._debouncedOnChange); + }; + + _debouncedOnChange = _.debounce(() => { + this._loadSuggestions(); + this.props.onChange(this.state.localValue); + }, 300); + + async _loadSuggestions() { + this.setState({ isLoadingSuggestions: true }); + + let suggestions = []; + try { + suggestions = await this.props.getValueSuggestions(this.state.localValue); + } catch (error) { + // ignore suggestions error + } + + if (this._isMounted) { + console.log(suggestions); + this.setState({ isLoadingSuggestions: false, suggestions }); + } + } + + render() { + const { + onChange, // eslint-disable-line no-unused-vars + getValueSuggestions, // eslint-disable-line no-unused-vars + value, // eslint-disable-line no-unused-vars + ...rest + } = this.props; + + return ; + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js index 9a0d73cef616c..477041141e836 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js @@ -43,6 +43,7 @@ export function DynamicIconForm({ return ( { @@ -23,7 +24,13 @@ const DEFAULT_ICON_STOPS = [ { stop: '', icon: DEFAULT_ICON }, ]; -export function IconStops({ iconStops = DEFAULT_ICON_STOPS, isDarkMode, onChange, symbolOptions }) { +export function IconStops({ + getValueSuggestions, + iconStops = DEFAULT_ICON_STOPS, + isDarkMode, + onChange, + symbolOptions, +}) { return iconStops.map(({ stop, icon }, index) => { const onIconSelect = selectedIconId => { const newIconStops = [...iconStops]; @@ -33,8 +40,7 @@ export function IconStops({ iconStops = DEFAULT_ICON_STOPS, isDarkMode, onChange }; onChange({ customMapStops: newIconStops }); }; - const onStopChange = e => { - const newStopValue = e.target.value; + const onStopChange = newStopValue => { const newIconStops = [...iconStops]; newIconStops[index] = { ...iconStops[index], @@ -95,7 +101,8 @@ export function IconStops({ iconStops = DEFAULT_ICON_STOPS, isDarkMode, onChange
- { + const fieldName = this.getFieldName(); + return this._getValueSuggestions && fieldName + ? this._getValueSuggestions(fieldName, query) + : []; + }; + getFieldMeta() { return this._getFieldMeta && this._field ? this._getFieldMeta(this._field.getName()) : null; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index 97259a908f1e4..80692093c9dfa 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -160,6 +160,8 @@ export class VectorStyle extends AbstractStyle { styleProperties[styleProperty.getStyleName()] = styleProperty; }); + this._source.getValueSuggestions('machine.os.keyword', ''); + return ( Date: Thu, 30 Jan 2020 11:00:28 -0500 Subject: [PATCH 02/10] use Popover to show type ahead suggestions --- .../styles/vector/components/stop_input.js | 98 +++++++++++++++++-- 1 file changed, 89 insertions(+), 9 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js index a2951d11cd61e..e1d8554071aeb 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js @@ -7,12 +7,14 @@ import _ from 'lodash'; import React, { Component } from 'react'; -import { EuiFieldText } from '@elastic/eui'; +import { EuiFieldText, EuiPopover, EuiLoadingSpinner, EuiText, EuiSelectable } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; export class StopInput extends Component { state = { + isPopoverOpen: false, localValue: '', - suggestions: [], + suggestionOptions: [], isLoadingSuggestions: false, }; @@ -24,8 +26,6 @@ export class StopInput extends Component { return { prevValue: nextProps.value, localValue: nextProps.value, - suggestions: [], - isLoadingSuggestions: false, }; } @@ -37,8 +37,28 @@ export class StopInput extends Component { this._isMounted = false; } + _closePopover = () => { + this.setState({ isPopoverOpen: false }); + }; + + _openPopover = () => { + this.setState({ isPopoverOpen: true }); + }; + + _togglePopover = () => { + this.setState(prevState => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + _onChange = e => { - this.setState({ localValue: e.target.value }, this._debouncedOnChange); + this.setState( + { + localValue: e.target.value, + isPopoverOpen: true, + }, + this._debouncedOnChange + ); }; _debouncedOnChange = _.debounce(() => { @@ -46,7 +66,7 @@ export class StopInput extends Component { this.props.onChange(this.state.localValue); }, 300); - async _loadSuggestions() { + _loadSuggestions = async () => { this.setState({ isLoadingSuggestions: true }); let suggestions = []; @@ -57,9 +77,49 @@ export class StopInput extends Component { } if (this._isMounted) { - console.log(suggestions); - this.setState({ isLoadingSuggestions: false, suggestions }); + this.setState({ + isLoadingSuggestions: false, + suggestionOptions: suggestions.map(suggestion => { + return { + value: suggestion, + label: suggestion, + }; + }), + }); + } + }; + + _onSuggestionSelection = options => { + const selectedOption = options.find(option => { + return option.checked === 'on'; + }); + + if (selectedOption) { + this.props.onChange(selectedOption.value); } + this._closePopover(); + }; + + _renderSuggestions() { + if (this.state.isLoadingSuggestions) { + + + + ; + } + + if (this.state.suggestionOptions.length === 0) { + return null; + } + + return ( + + {list => list} + + ); } render() { @@ -70,6 +130,26 @@ export class StopInput extends Component { ...rest } = this.props; - return ; + const input = ( + + ); + + return ( + + {this._renderSuggestions()} + + ); } } From b3c8aaa05c74b748f525f2a745c535c49d471b02 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 30 Jan 2020 15:44:53 -0500 Subject: [PATCH 03/10] datalist version --- .../styles/vector/components/stop_input.js | 117 +++++++----------- 1 file changed, 44 insertions(+), 73 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js index e1d8554071aeb..a5b2ef11297fd 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js @@ -5,21 +5,24 @@ */ import _ from 'lodash'; -import React, { Component } from 'react'; +import React, { Component, Fragment } from 'react'; +import uuid from 'uuid/v4'; import { EuiFieldText, EuiPopover, EuiLoadingSpinner, EuiText, EuiSelectable } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; export class StopInput extends Component { + state = { - isPopoverOpen: false, - localValue: '', - suggestionOptions: [], + suggestions: [], isLoadingSuggestions: false, + hasPrevFocus: false, }; + _datalistId = uuid(); + static getDerivedStateFromProps(nextProps, prevState) { - if (nextProps.value === prevState.prevValue) { + if (nextProps.value === prevState.prevValue || nextProps.value === prevState.localValue) { return null; } @@ -35,40 +38,40 @@ export class StopInput extends Component { componentWillUnmount() { this._isMounted = false; + this._debouncedOnChange.cancel(); } - _closePopover = () => { - this.setState({ isPopoverOpen: false }); - }; - - _openPopover = () => { - this.setState({ isPopoverOpen: true }); - }; - - _togglePopover = () => { - this.setState(prevState => ({ - isPopoverOpen: !prevState.isPopoverOpen, - })); - }; - _onChange = e => { this.setState( { localValue: e.target.value, - isPopoverOpen: true, + suggestionsFilter: e.target.value, + isLoadingSuggestions: true, }, this._debouncedOnChange ); }; _debouncedOnChange = _.debounce(() => { - this._loadSuggestions(); + this._loadSuggestions(this.state.suggestionsFilter); this.props.onChange(this.state.localValue); }, 300); - _loadSuggestions = async () => { - this.setState({ isLoadingSuggestions: true }); + _onFocus = () => { + // populate suggestions on initial focus + if (!this.state.hasPrevFocus) { + this.setState( + { + hasPrevFocus: true, + isLoadingSuggestions: true, + suggestionsFilter: '', + }, + () => { this._loadSuggestions(this.state.suggestionsFilter); } + ); + } + } + _loadSuggestions = async (filter) => { let suggestions = []; try { suggestions = await this.props.getValueSuggestions(this.state.localValue); @@ -76,49 +79,25 @@ export class StopInput extends Component { // ignore suggestions error } - if (this._isMounted) { + if (this._isMounted && filter === this.state.suggestionsFilter) { this.setState({ isLoadingSuggestions: false, - suggestionOptions: suggestions.map(suggestion => { - return { - value: suggestion, - label: suggestion, - }; - }), + suggestions, }); } }; - _onSuggestionSelection = options => { - const selectedOption = options.find(option => { - return option.checked === 'on'; - }); - - if (selectedOption) { - this.props.onChange(selectedOption.value); - } - this._closePopover(); - }; - _renderSuggestions() { - if (this.state.isLoadingSuggestions) { - - - - ; - } - - if (this.state.suggestionOptions.length === 0) { + if (this.state.isLoadingSuggestions|| this.state.suggestions.length === 0) { return null; } return ( - - {list => list} - + + {this.state.suggestions.map(suggestion => { + return ); } @@ -130,26 +109,18 @@ export class StopInput extends Component { ...rest } = this.props; - const input = ( - - ); - return ( - + + {this._renderSuggestions()} - + ); } } From 3bda35f28318213705d66f1d073cd785cebe3dda Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 30 Jan 2020 16:28:40 -0500 Subject: [PATCH 04/10] use EuiComboBox --- .../plugins/maps/public/kibana_services.js | 1 - .../styles/vector/components/stop_input.js | 126 ++++++++---------- .../vector/components/symbol/icon_stops.js | 34 ++--- 3 files changed, 74 insertions(+), 87 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/kibana_services.js b/x-pack/legacy/plugins/maps/public/kibana_services.js index a8903c4911364..a1b1c9ec1518e 100644 --- a/x-pack/legacy/plugins/maps/public/kibana_services.js +++ b/x-pack/legacy/plugins/maps/public/kibana_services.js @@ -14,7 +14,6 @@ import { npStart } from 'ui/new_platform'; export const SPATIAL_FILTER_TYPE = esFilters.FILTERS.SPATIAL_FILTER; export { SearchSource } from '../../../../../src/plugins/data/public'; export const indexPatternService = npStart.plugins.data.indexPatterns; -console.log(npStart.plugins.data); export const autocompleteService = npStart.plugins.data.autocomplete; let licenseId; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js index a5b2ef11297fd..2d3dd84dd1cd3 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js @@ -5,101 +5,68 @@ */ import _ from 'lodash'; -import React, { Component, Fragment } from 'react'; -import uuid from 'uuid/v4'; +import React, { Component } from 'react'; -import { EuiFieldText, EuiPopover, EuiLoadingSpinner, EuiText, EuiSelectable } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiComboBox } from '@elastic/eui'; export class StopInput extends Component { - state = { suggestions: [], isLoadingSuggestions: false, hasPrevFocus: false, }; - _datalistId = uuid(); - - static getDerivedStateFromProps(nextProps, prevState) { - if (nextProps.value === prevState.prevValue || nextProps.value === prevState.localValue) { - return null; - } - - return { - prevValue: nextProps.value, - localValue: nextProps.value, - }; - } - componentDidMount() { this._isMounted = true; } componentWillUnmount() { this._isMounted = false; - this._debouncedOnChange.cancel(); + this._loadSuggestions.cancel(); } - _onChange = e => { + _onFocus = () => { + if (!this.state.hasPrevFocus) { + this.setState({ hasPrevFocus: true }); + this._onSearchChange(''); + } + }; + + _onChange = selectedOptions => { + this.props.onChange(_.get(selectedOptions, '[0].label', '')); + }; + + _onCreateOption = newValue => { + this.props.onChange(newValue); + }; + + _onSearchChange = async searchValue => { this.setState( { - localValue: e.target.value, - suggestionsFilter: e.target.value, isLoadingSuggestions: true, + searchValue, }, - this._debouncedOnChange + () => { + this._loadSuggestions(searchValue); + } ); }; - _debouncedOnChange = _.debounce(() => { - this._loadSuggestions(this.state.suggestionsFilter); - this.props.onChange(this.state.localValue); - }, 300); - - _onFocus = () => { - // populate suggestions on initial focus - if (!this.state.hasPrevFocus) { - this.setState( - { - hasPrevFocus: true, - isLoadingSuggestions: true, - suggestionsFilter: '', - }, - () => { this._loadSuggestions(this.state.suggestionsFilter); } - ); - } - } - - _loadSuggestions = async (filter) => { + _loadSuggestions = _.debounce(async searchValue => { let suggestions = []; try { - suggestions = await this.props.getValueSuggestions(this.state.localValue); + suggestions = await this.props.getValueSuggestions(searchValue); } catch (error) { // ignore suggestions error } - if (this._isMounted && filter === this.state.suggestionsFilter) { + if (this._isMounted && searchValue === this.state.searchValue) { this.setState({ isLoadingSuggestions: false, suggestions, }); } - }; - - _renderSuggestions() { - if (this.state.isLoadingSuggestions|| this.state.suggestions.length === 0) { - return null; - } - - return ( - - {this.state.suggestions.map(suggestion => { - return - ); - } + }, 300); render() { const { @@ -109,18 +76,35 @@ export class StopInput extends Component { ...rest } = this.props; + const suggestionOptions = this.state.suggestions.map(suggestion => { + return { label: suggestion }; + }); + + const selectedOptions = []; + if (this.props.value) { + let option = suggestionOptions.find(({ label }) => { + return label === this.props.value; + }); + if (!option) { + option = { label: this.props.value }; + suggestionOptions.unshift(option); + } + selectedOptions.push(option); + } + return ( - - - {this._renderSuggestions()} - + ); } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js index 7dcada8d08bfe..a8e208bd3a0a9 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js @@ -8,7 +8,7 @@ import React from 'react'; import { DEFAULT_ICON } from '../../../../../../common/constants'; import { i18n } from '@kbn/i18n'; import { getOtherCategoryLabel } from '../../style_util'; -import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldText } from '@elastic/eui'; import { IconSelect } from './icon_select'; import { StopInput } from '../stop_input'; @@ -89,7 +89,23 @@ export function IconStops({ const errors = []; // TODO check for duplicate values and add error messages here - const isOtherCategoryRow = index === 0; + const stopInput = + index === 0 ? ( + + ) : ( + + ); + return (
- - - + {stopInput} Date: Thu, 30 Jan 2020 16:36:15 -0500 Subject: [PATCH 05/10] clean up --- .../public/layers/styles/vector/components/stop_input.js | 9 +-------- .../layers/styles/vector/components/symbol/icon_stops.js | 7 +------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js index 2d3dd84dd1cd3..942e13d6c5666 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js @@ -69,13 +69,6 @@ export class StopInput extends Component { }, 300); render() { - const { - onChange, // eslint-disable-line no-unused-vars - getValueSuggestions, // eslint-disable-line no-unused-vars - value, // eslint-disable-line no-unused-vars - ...rest - } = this.props; - const suggestionOptions = this.state.suggestions.map(suggestion => { return { label: suggestion }; }); @@ -94,7 +87,6 @@ export class StopInput extends Component { return ( ); } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js index a8e208bd3a0a9..4541770f4dd3a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js @@ -98,12 +98,7 @@ export function IconStops({ compressed /> ) : ( - + ); return ( From 6e44f0cdfa89de0ede91c3ee207bc5afbdbd2ff1 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 31 Jan 2020 08:21:33 -0500 Subject: [PATCH 06/10] wire ColorStopsCategorical to use StopInput component for autocomplete --- .../components/color/color_map_select.js | 1 + .../vector/components/color/color_stops.js | 11 ++--- .../color/color_stops_categorical.js | 42 +++++++------------ .../components/color/color_stops_ordinal.js | 13 +++--- .../components/color/dynamic_color_form.js | 4 +- 5 files changed, 28 insertions(+), 43 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js index fde088ab4475e..a56295e2e1e81 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js @@ -73,6 +73,7 @@ export class ColorMapSelect extends Component { ); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js index 6b403ff61532d..47c2d037e0c79 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js @@ -59,26 +59,23 @@ export const ColorStops = ({ onChange, colorStops, isStopsInvalid, - sanitizeStopInput, getStopError, renderStopInput, addNewRow, canDeleteStop, }) => { function getStopInput(stop, index) { - const onStopChange = e => { + const onStopChange = newStopValue => { const newColorStops = _.cloneDeep(colorStops); - newColorStops[index].stop = sanitizeStopInput(e.target.value); - const invalid = isStopsInvalid(newColorStops); + newColorStops[index].stop = newStopValue; onChange({ colorStops: newColorStops, - isInvalid: invalid, + isInvalid: isStopsInvalid(newColorStops), }); }; - const error = getStopError(stop, index); return { - stopError: error, + stopError: getStopError(stop, index), stopInput: renderStopInput(stop, onStopChange, index), }; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js index d52c3dbcfa1df..a55873c8f65bf 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js @@ -17,6 +17,7 @@ import { import { i18n } from '@kbn/i18n'; import { ColorStops } from './color_stops'; import { getOtherCategoryLabel } from '../../style_util'; +import { StopInput } from '../stop_input'; export const ColorStopsCategorical = ({ colorStops = [ @@ -24,11 +25,8 @@ export const ColorStopsCategorical = ({ { stop: '', color: DEFAULT_NEXT_COLOR }, ], onChange, + getValueSuggestions, }) => { - const sanitizeStopInput = value => { - return value; - }; - const getStopError = (stop, index) => { let count = 0; for (let i = 1; i < colorStops.length; i++) { @@ -49,34 +47,21 @@ export const ColorStopsCategorical = ({ if (index === 0) { return ( - ); - } else { - return ( - ); } + + return ( + + ); }; const canDeleteStop = (colorStops, index) => { @@ -88,7 +73,6 @@ export const ColorStopsCategorical = ({ onChange={onChange} colorStops={colorStops} isStopsInvalid={isCategoricalStopsInvalid} - sanitizeStopInput={sanitizeStopInput} getStopError={getStopError} renderStopInput={renderStopInput} canDeleteStop={canDeleteStop} @@ -114,4 +98,8 @@ ColorStopsCategorical.propTypes = { * Callback for when the color stops changes. Called with { colorStops, isInvalid } */ onChange: PropTypes.func.isRequired, + /** + * Callback for fetching stop value suggestions. Called with query. + */ + getValueSuggestions: PropTypes.func.isRequired, }; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js index 61fbb376ad601..0f6a0583d3dbc 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js @@ -21,11 +21,6 @@ export const ColorStopsOrdinal = ({ colorStops = [{ stop: 0, color: DEFAULT_CUSTOM_COLOR }], onChange, }) => { - const sanitizeStopInput = value => { - const sanitizedValue = parseFloat(value); - return isNaN(sanitizedValue) ? '' : sanitizedValue; - }; - const getStopError = (stop, index) => { let error; if (isOrdinalStopInvalid(stop)) { @@ -44,13 +39,18 @@ export const ColorStopsOrdinal = ({ }; const renderStopInput = (stop, onStopChange) => { + function handleOnChangeEvent(event) { + const sanitizedValue = parseFloat(event.target.value); + const newStopValue = isNaN(sanitizedValue) ? '' : sanitizedValue; + onStopChange(newStopValue); + } return ( ); @@ -65,7 +65,6 @@ export const ColorStopsOrdinal = ({ onChange={onChange} colorStops={colorStops} isStopsInvalid={isOrdinalStopsInvalid} - sanitizeStopInput={sanitizeStopInput} getStopError={getStopError} renderStopInput={renderStopInput} canDeleteStop={canDeleteStop} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js index 5491d5d567f84..bc16b28954764 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js @@ -67,7 +67,7 @@ export function DynamicColorForm({ color={styleOptions.color} customColorMap={styleOptions.customColorRamp} useCustomColorMap={_.get(styleOptions, 'useCustomColorRamp', false)} - compressed + getValueSuggestions={styleProperty.getValueSuggestions} /> ); } @@ -83,7 +83,7 @@ export function DynamicColorForm({ color={styleOptions.colorCategory} customColorMap={styleOptions.customColorPalette} useCustomColorMap={_.get(styleOptions, 'useCustomColorPalette', false)} - compressed + getValueSuggestions={styleProperty.getValueSuggestions} /> ); }; From cf8526ecbd5332066cd36dc8cf14b36139a91da9 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 31 Jan 2020 08:26:00 -0500 Subject: [PATCH 07/10] clean up --- .../plugins/maps/public/layers/styles/vector/vector_style.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index 80692093c9dfa..915c80fad37e1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -160,8 +160,6 @@ export class VectorStyle extends AbstractStyle { styleProperties[styleProperty.getStyleName()] = styleProperty; }); - this._source.getValueSuggestions('machine.os.keyword', ''); - return ( Date: Mon, 3 Feb 2020 17:34:03 -0500 Subject: [PATCH 08/10] cast suggestion values to string so boolean fields work --- .../maps/public/layers/styles/vector/components/stop_input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js index 942e13d6c5666..77ff3fb3f44db 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js @@ -70,7 +70,7 @@ export class StopInput extends Component { render() { const suggestionOptions = this.state.suggestions.map(suggestion => { - return { label: suggestion }; + return { label: `${suggestion}` }; }); const selectedOptions = []; From c03cb7c122a94e9f73c6062235a15fd6697421e9 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Sun, 9 Feb 2020 05:52:25 -0700 Subject: [PATCH 09/10] review feedback --- .../components/color/color_map_select.js | 3 +- .../color/color_stops_categorical.js | 2 + .../components/color/dynamic_color_form.js | 4 +- .../styles/vector/components/stop_input.js | 69 +++++++++++++++++-- .../components/symbol/dynamic_icon_form.js | 2 +- .../components/symbol/icon_map_select.js | 5 +- .../vector/components/symbol/icon_stops.js | 8 ++- .../properties/dynamic_style_property.js | 8 +-- .../layers/styles/vector/vector_style.js | 8 +-- 9 files changed, 86 insertions(+), 23 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js index a56295e2e1e81..e8d5754ef4206 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js @@ -72,8 +72,9 @@ export class ColorMapSelect extends Component { ); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js index a55873c8f65bf..5e79bebba2cf3 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js @@ -24,6 +24,7 @@ export const ColorStopsCategorical = ({ { stop: null, color: DEFAULT_CUSTOM_COLOR }, //first stop is the "other" color { stop: '', color: DEFAULT_NEXT_COLOR }, ], + field, onChange, getValueSuggestions, }) => { @@ -57,6 +58,7 @@ export const ColorStopsCategorical = ({ return ( ); } @@ -83,7 +83,7 @@ export function DynamicColorForm({ color={styleOptions.colorCategory} customColorMap={styleOptions.customColorPalette} useCustomColorMap={_.get(styleOptions, 'useCustomColorPalette', false)} - getValueSuggestions={styleProperty.getValueSuggestions} + styleProperty={styleProperty} /> ); }; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js index 77ff3fb3f44db..a8528a5d943b9 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js @@ -7,17 +7,28 @@ import _ from 'lodash'; import React, { Component } from 'react'; -import { EuiComboBox } from '@elastic/eui'; +import { EuiComboBox, EuiFieldText } from '@elastic/eui'; export class StopInput extends Component { - state = { - suggestions: [], - isLoadingSuggestions: false, - hasPrevFocus: false, - }; + constructor(props) { + super(props); + this.state = { + suggestions: [], + isLoadingSuggestions: false, + hasPrevFocus: false, + fieldDataType: undefined, + localFieldTextValue: props.value, + }; + } componentDidMount() { this._isMounted = true; + this._prevFieldName = undefined; + this._loadFieldDataType(); + } + + componentDidUpdate() { + this._loadFieldDataType(); } componentWillUnmount() { @@ -25,6 +36,18 @@ export class StopInput extends Component { this._loadSuggestions.cancel(); } + async _loadFieldDataType() { + if (this.props.field.getName() === this._prevFieldName) { + return; + } + + this._prevFieldName = this.props.field.getName(); + const fieldDataType = await this.props.field.getDataType(); + if (this._isMounted) { + this.setState({ fieldDataType }); + } + } + _onFocus = () => { if (!this.state.hasPrevFocus) { this.setState({ hasPrevFocus: true }); @@ -68,7 +91,17 @@ export class StopInput extends Component { } }, 300); - render() { + _onFieldTextChange = event => { + this.setState({ localFieldTextValue: event.target.value }); + // onChange can cause UI lag, ensure smooth input typing by debouncing onChange + this._debouncedOnFieldTextChange(); + }; + + _debouncedOnFieldTextChange = _.debounce(() => { + this.props.onChange(this.state.localFieldTextValue); + }, 500); + + _renderSuggestionInput() { const suggestionOptions = this.state.suggestions.map(suggestion => { return { label: `${suggestion}` }; }); @@ -100,4 +133,26 @@ export class StopInput extends Component { /> ); } + + _renderTextInput() { + return ( + + ); + } + + render() { + if (!this.state.fieldDataType) { + return null; + } + + // autocomplete service can not provide suggestions for non string fields (and boolean) because it uses + // term aggregation include parameter. Include paramerter uses a regular expressions that only supports string type + return this.state.fieldDataType === 'string' || this.state.fieldDataType === 'boolean' + ? this._renderSuggestionInput() + : this._renderTextInput(); + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js index 477041141e836..afa11daf45217 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js @@ -43,7 +43,7 @@ export function DynamicIconForm({ return ( ) : ( - + ); return ( diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js index 88adab86094d0..ef19e9b23b10d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -17,19 +17,17 @@ import { OrdinalFieldMetaOptionsPopover } from '../components/ordinal_field_meta export class DynamicStyleProperty extends AbstractStyleProperty { static type = STYLE_TYPE.DYNAMIC; - constructor(options, styleName, field, getFieldMeta, getFieldFormatter, getValueSuggestions) { + constructor(options, styleName, field, getFieldMeta, getFieldFormatter, source) { super(options, styleName); this._field = field; this._getFieldMeta = getFieldMeta; this._getFieldFormatter = getFieldFormatter; - this._getValueSuggestions = getValueSuggestions; + this._source = source; } getValueSuggestions = query => { const fieldName = this.getFieldName(); - return this._getValueSuggestions && fieldName - ? this._getValueSuggestions(fieldName, query) - : []; + return this._source && fieldName ? this._source.getValueSuggestions(fieldName, query) : []; }; getFieldMeta() { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index 915c80fad37e1..1f96c37c9d286 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -612,7 +612,7 @@ export class VectorStyle extends AbstractStyle { field, this._getFieldMeta, this._getFieldFormatter, - this._source.getValueSuggestions, + this._source, isSymbolizedAsIcon ); } else { @@ -633,7 +633,7 @@ export class VectorStyle extends AbstractStyle { field, this._getFieldMeta, this._getFieldFormatter, - this._source.getValueSuggestions + this._source ); } else { throw new Error(`${descriptor} not implemented`); @@ -666,7 +666,7 @@ export class VectorStyle extends AbstractStyle { field, this._getFieldMeta, this._getFieldFormatter, - this._source.getValueSuggestions + this._source ); } else { throw new Error(`${descriptor} not implemented`); @@ -686,7 +686,7 @@ export class VectorStyle extends AbstractStyle { field, this._getFieldMeta, this._getFieldFormatter, - this._source.getValueSuggestions + this._source ); } else { throw new Error(`${descriptor} not implemented`); From 6bf86a38022a229f2fee844b22b06e16cd175f8c Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 12 Feb 2020 12:34:45 -0700 Subject: [PATCH 10/10] fix problem with stall suggestions from previous field --- .../vector/components/color/color_stops_categorical.js | 1 + .../layers/styles/vector/components/stop_input.js | 10 ---------- .../styles/vector/components/symbol/icon_stops.js | 1 + 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js index 5e79bebba2cf3..124c2bf0cff55 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js @@ -58,6 +58,7 @@ export const ColorStopsCategorical = ({ return ( ) : (