[Maps] show field type icons in data driven styling field select (#55166)

* [Maps] show field icons in data driven styling field select

* only show origin group label when there is more then one origin

* review feedback

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2020-01-21 18:52:13 -05:00 committed by GitHub
parent b78c1b1042
commit 04374e665c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 165 additions and 187 deletions

View file

@ -14,9 +14,7 @@ import { ColorStopsCategorical } from './color_stops_categorical';
const CUSTOM_COLOR_MAP = 'CUSTOM_COLOR_MAP'; const CUSTOM_COLOR_MAP = 'CUSTOM_COLOR_MAP';
export class ColorMapSelect extends Component { export class ColorMapSelect extends Component {
state = { state = {};
selected: '',
};
static getDerivedStateFromProps(nextProps, prevState) { static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.customColorMap === prevState.prevPropsCustomColorMap) { if (nextProps.customColorMap === prevState.prevPropsCustomColorMap) {
@ -41,10 +39,7 @@ export class ColorMapSelect extends Component {
_onCustomColorMapChange = ({ colorStops, isInvalid }) => { _onCustomColorMapChange = ({ colorStops, isInvalid }) => {
// Manage invalid custom color map in local state // Manage invalid custom color map in local state
if (isInvalid) { if (isInvalid) {
const newState = { this.setState({ customColorMap: colorStops });
customColorMap: colorStops,
};
this.setState(newState);
return; return;
} }
@ -56,10 +51,12 @@ export class ColorMapSelect extends Component {
}; };
_renderColorStopsInput() { _renderColorStopsInput() {
let colorStopsInput; if (!this.props.useCustomColorMap) {
if (this.props.useCustomColorMap) { return null;
}
if (this.props.colorMapType === COLOR_MAP_TYPE.ORDINAL) { if (this.props.colorMapType === COLOR_MAP_TYPE.ORDINAL) {
colorStopsInput = ( return (
<Fragment> <Fragment>
<EuiSpacer size="s" /> <EuiSpacer size="s" />
<ColorStopsOrdinal <ColorStopsOrdinal
@ -68,8 +65,9 @@ export class ColorMapSelect extends Component {
/> />
</Fragment> </Fragment>
); );
} else if (this.props.colorMapType === COLOR_MAP_TYPE.CATEGORICAL) { }
colorStopsInput = (
return (
<Fragment> <Fragment>
<EuiSpacer size="s" /> <EuiSpacer size="s" />
<ColorStopsCategorical <ColorStopsCategorical
@ -79,12 +77,8 @@ export class ColorMapSelect extends Component {
</Fragment> </Fragment>
); );
} }
}
return colorStopsInput;
}
render() { render() {
const colorStopsInput = this._renderColorStopsInput();
const colorMapOptionsWithCustom = [ const colorMapOptionsWithCustom = [
{ {
value: CUSTOM_COLOR_MAP, value: CUSTOM_COLOR_MAP,
@ -110,7 +104,7 @@ export class ColorMapSelect extends Component {
valueOfSelected={valueOfSelected} valueOfSelected={valueOfSelected}
hasDividers={true} hasDividers={true}
/> />
{colorStopsInput} {this._renderColorStopsInput()}
</Fragment> </Fragment>
); );
} }

View file

@ -13,87 +13,56 @@ import { CATEGORICAL_DATA_TYPES, COLOR_MAP_TYPE } from '../../../../../../common
import { COLOR_GRADIENTS, COLOR_PALETTES } from '../../../color_utils'; import { COLOR_GRADIENTS, COLOR_PALETTES } from '../../../color_utils';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
export class DynamicColorForm extends React.Component { export function DynamicColorForm({
state = { fields,
colorMapType: COLOR_MAP_TYPE.ORDINAL, onDynamicStyleChange,
}; staticDynamicSelect,
styleProperty,
constructor() { }) {
super();
this._isMounted = false;
}
componentWillUnmount() {
this._isMounted = false;
}
componentDidMount() {
this._isMounted = true;
this._loadColorMapType();
}
componentDidUpdate() {
this._loadColorMapType();
}
async _loadColorMapType() {
const field = this.props.styleProperty.getField();
if (!field) {
return;
}
const dataType = await field.getDataType();
const colorMapType = CATEGORICAL_DATA_TYPES.includes(dataType)
? COLOR_MAP_TYPE.CATEGORICAL
: COLOR_MAP_TYPE.ORDINAL;
if (this._isMounted && this.state.colorMapType !== colorMapType) {
this.setState({ colorMapType }, () => {
const options = this.props.styleProperty.getOptions();
this.props.onDynamicStyleChange(this.props.styleProperty.getStyleName(), {
...options,
type: colorMapType,
});
});
}
}
_getColorSelector() {
const { onDynamicStyleChange, styleProperty } = this.props;
const styleOptions = styleProperty.getOptions(); const styleOptions = styleProperty.getOptions();
if (!styleOptions.field || !styleOptions.field.name) { const onColorMapSelect = ({ color, customColorMap, type, useCustomColorMap }) => {
return;
}
let colorSelect;
const onColorChange = colorOptions => {
const newColorOptions = { const newColorOptions = {
type: colorOptions.type, ...styleOptions,
type,
}; };
if (colorOptions.type === COLOR_MAP_TYPE.ORDINAL) { if (type === COLOR_MAP_TYPE.ORDINAL) {
newColorOptions.useCustomColorRamp = colorOptions.useCustomColorMap; newColorOptions.useCustomColorRamp = useCustomColorMap;
newColorOptions.customColorRamp = colorOptions.customColorMap; newColorOptions.customColorRamp = customColorMap;
newColorOptions.color = colorOptions.color; newColorOptions.color = color;
} else { } else {
newColorOptions.useCustomColorPalette = colorOptions.useCustomColorMap; newColorOptions.useCustomColorPalette = useCustomColorMap;
newColorOptions.customColorPalette = colorOptions.customColorMap; newColorOptions.customColorPalette = customColorMap;
newColorOptions.colorCategory = colorOptions.color; newColorOptions.colorCategory = color;
} }
onDynamicStyleChange(styleProperty.getStyleName(), newColorOptions);
};
const onFieldChange = async ({ field }) => {
const { name, origin, type } = field;
onDynamicStyleChange(styleProperty.getStyleName(), { onDynamicStyleChange(styleProperty.getStyleName(), {
...styleOptions, ...styleOptions,
...newColorOptions, field: { name, origin },
type: CATEGORICAL_DATA_TYPES.includes(type)
? COLOR_MAP_TYPE.CATEGORICAL
: COLOR_MAP_TYPE.ORDINAL,
}); });
}; };
if (this.state.colorMapType === COLOR_MAP_TYPE.ORDINAL) { const renderColorMapSelect = () => {
const customOptionLabel = i18n.translate('xpack.maps.style.customColorRampLabel', { if (!styleOptions.field || !styleOptions.field.name) {
defaultMessage: 'Custom color ramp', return null;
}); }
colorSelect = (
if (styleOptions.type === COLOR_MAP_TYPE.ORDINAL) {
return (
<ColorMapSelect <ColorMapSelect
colorMapOptions={COLOR_GRADIENTS} colorMapOptions={COLOR_GRADIENTS}
customOptionLabel={customOptionLabel} customOptionLabel={i18n.translate('xpack.maps.style.customColorRampLabel', {
onChange={options => onColorChange(options)} defaultMessage: 'Custom color ramp',
})}
onChange={onColorMapSelect}
colorMapType={COLOR_MAP_TYPE.ORDINAL} colorMapType={COLOR_MAP_TYPE.ORDINAL}
color={styleOptions.color} color={styleOptions.color}
customColorMap={styleOptions.customColorRamp} customColorMap={styleOptions.customColorRamp}
@ -101,15 +70,15 @@ export class DynamicColorForm extends React.Component {
compressed compressed
/> />
); );
} else if (this.state.colorMapType === COLOR_MAP_TYPE.CATEGORICAL) { }
const customOptionLabel = i18n.translate('xpack.maps.style.customColorPaletteLabel', {
defaultMessage: 'Custom color palette', return (
});
colorSelect = (
<ColorMapSelect <ColorMapSelect
colorMapOptions={COLOR_PALETTES} colorMapOptions={COLOR_PALETTES}
customOptionLabel={customOptionLabel} customOptionLabel={i18n.translate('xpack.maps.style.customColorPaletteLabel', {
onChange={options => onColorChange(options)} defaultMessage: 'Custom color palette',
})}
onChange={onColorMapSelect}
colorMapType={COLOR_MAP_TYPE.CATEGORICAL} colorMapType={COLOR_MAP_TYPE.CATEGORICAL}
color={styleOptions.colorCategory} color={styleOptions.colorCategory}
customColorMap={styleOptions.customColorPalette} customColorMap={styleOptions.customColorPalette}
@ -117,20 +86,8 @@ export class DynamicColorForm extends React.Component {
compressed compressed
/> />
); );
}
return colorSelect;
}
render() {
const { fields, onDynamicStyleChange, staticDynamicSelect, styleProperty } = this.props;
const styleOptions = styleProperty.getOptions();
const onFieldChange = options => {
const field = options.field;
onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field });
}; };
const colorSelect = this._getColorSelector();
return ( return (
<Fragment> <Fragment>
<EuiFlexGroup gutterSize="none" justifyContent="flexEnd"> <EuiFlexGroup gutterSize="none" justifyContent="flexEnd">
@ -145,8 +102,7 @@ export class DynamicColorForm extends React.Component {
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
<EuiSpacer size="s" /> <EuiSpacer size="s" />
{colorSelect} {renderColorMapSelect()}
</Fragment> </Fragment>
); );
}
} }

View file

@ -7,18 +7,22 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { EuiComboBox } from '@elastic/eui'; import { EuiComboBox, EuiHighlight } from '@elastic/eui';
import { SOURCE_DATA_ID_ORIGIN } from '../../../../../common/constants'; import { FIELD_ORIGIN } from '../../../../../common/constants';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FieldIcon } from '../../../../../../../../../src/plugins/kibana_react/public';
export function FieldSelect({ fields, selectedFieldName, onChange, ...rest }) { function renderOption(option, searchValue, contentClassName) {
const onFieldChange = selectedFields => { return (
onChange({ <span className={contentClassName}>
field: selectedFields.length > 0 ? selectedFields[0].value : null, <FieldIcon type={option.value.type} size="m" useColor />
}); &nbsp;
}; <EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
</span>
);
}
const groupFieldsByOrigin = () => { function groupFieldsByOrigin(fields) {
const fieldsByOriginMap = new Map(); const fieldsByOriginMap = new Map();
fields.forEach(field => { fields.forEach(field => {
if (fieldsByOriginMap.has(field.origin)) { if (fieldsByOriginMap.has(field.origin)) {
@ -30,17 +34,31 @@ export function FieldSelect({ fields, selectedFieldName, onChange, ...rest }) {
} }
}); });
const optionGroups = []; function fieldsListToOptions(fieldsList) {
fieldsByOriginMap.forEach((fieldsList, fieldOrigin) => { return fieldsList
optionGroups.push({
label: fieldOrigin,
options: fieldsList
.map(field => { .map(field => {
return { value: field, label: field.label }; return { value: field, label: field.label };
}) })
.sort((a, b) => { .sort((a, b) => {
return a.label.toLowerCase().localeCompare(b.label.toLowerCase()); return a.label.toLowerCase().localeCompare(b.label.toLowerCase());
});
}
if (fieldsByOriginMap.size === 1) {
// do not show origin group if all fields are from same origin
const onlyOriginKey = fieldsByOriginMap.keys().next().value;
const fieldsList = fieldsByOriginMap.get(onlyOriginKey);
return fieldsListToOptions(fieldsList);
}
const optionGroups = [];
fieldsByOriginMap.forEach((fieldsList, fieldOrigin) => {
optionGroups.push({
label: i18n.translate('xpack.maps.style.fieldSelect.OriginLabel', {
defaultMessage: 'Fields from {fieldOrigin}',
values: { fieldOrigin },
}), }),
options: fieldsListToOptions(fieldsList),
}); });
}); });
@ -49,6 +67,13 @@ export function FieldSelect({ fields, selectedFieldName, onChange, ...rest }) {
}); });
return optionGroups; return optionGroups;
}
export function FieldSelect({ fields, selectedFieldName, onChange, ...rest }) {
const onFieldChange = selectedFields => {
onChange({
field: selectedFields.length > 0 ? selectedFields[0].value : null,
});
}; };
let selectedOption; let selectedOption;
@ -61,7 +86,7 @@ export function FieldSelect({ fields, selectedFieldName, onChange, ...rest }) {
return ( return (
<EuiComboBox <EuiComboBox
selectedOptions={selectedOption ? [selectedOption] : []} selectedOptions={selectedOption ? [selectedOption] : []}
options={groupFieldsByOrigin()} options={groupFieldsByOrigin(fields)}
onChange={onFieldChange} onChange={onFieldChange}
singleSelection={{ asPlainText: true }} singleSelection={{ asPlainText: true }}
isClearable={false} isClearable={false}
@ -69,6 +94,7 @@ export function FieldSelect({ fields, selectedFieldName, onChange, ...rest }) {
placeholder={i18n.translate('xpack.maps.styles.vector.selectFieldPlaceholder', { placeholder={i18n.translate('xpack.maps.styles.vector.selectFieldPlaceholder', {
defaultMessage: 'Select a field', defaultMessage: 'Select a field',
})} })}
renderOption={renderOption}
{...rest} {...rest}
/> />
); );
@ -76,7 +102,8 @@ export function FieldSelect({ fields, selectedFieldName, onChange, ...rest }) {
export const fieldShape = PropTypes.shape({ export const fieldShape = PropTypes.shape({
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
origin: PropTypes.oneOf(['join', SOURCE_DATA_ID_ORIGIN]).isRequired, origin: PropTypes.oneOf(Object.values(FIELD_ORIGIN)).isRequired,
type: PropTypes.string.isRequired,
}); });
FieldSelect.propTypes = { FieldSelect.propTypes = {

View file

@ -61,6 +61,7 @@ export class VectorStyleEditor extends Component {
label: await field.getLabel(), label: await field.getLabel(),
name: field.getName(), name: field.getName(),
origin: field.getOrigin(), origin: field.getOrigin(),
type: await field.getDataType(),
}; };
}; };