From 34c54ed31b70e4b6ffaf9cec003e3878ad68583f Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 14 Jul 2020 15:19:51 -0600 Subject: [PATCH] [Maps] fix custom icon palettes UI not being displayed (#71482) * [Maps] fix custom icon palettes UI not being displayed * cleanup test * remove uneeded change to vector style defaults * fix jest tests * review feedback * fix jest tests --- .../style_property_descriptor_types.ts | 2 +- .../create_layer_descriptor.test.ts | 9 +- .../security/create_layer_descriptors.test.ts | 9 +- .../tiled_vector_layer.test.tsx | 8 +- .../sources/ems_tms_source/ems_tms_source.js | 5 +- .../vector/components/style_map_select.js | 100 ------------- .../icon_map_select.test.tsx.snap | 124 ++++++++++++++++ .../components/symbol/dynamic_icon_form.js | 5 - .../components/symbol/icon_map_select.js | 59 -------- .../symbol/icon_map_select.test.tsx | 78 ++++++++++ .../components/symbol/icon_map_select.tsx | 136 ++++++++++++++++++ .../vector/components/symbol/icon_select.js | 16 +-- .../components/symbol/icon_select.test.js | 31 ++-- .../vector/components/symbol/icon_stops.js | 38 ++--- .../components/symbol/icon_stops.test.js | 34 ++++- .../components/symbol/static_icon_form.js | 15 +- .../symbol/vector_style_icon_editor.js | 14 +- .../properties/dynamic_style_property.d.ts | 1 + .../classes/styles/vector/symbol_utils.js | 4 +- .../vector/vector_style_defaults.test.ts | 9 +- .../styles/vector/vector_style_defaults.ts | 5 +- .../plugins/maps/public/kibana_services.d.ts | 1 + x-pack/plugins/maps/public/kibana_services.js | 3 + 23 files changed, 428 insertions(+), 278 deletions(-) delete mode 100644 x-pack/plugins/maps/public/classes/styles/vector/components/style_map_select.js create mode 100644 x-pack/plugins/maps/public/classes/styles/vector/components/symbol/__snapshots__/icon_map_select.test.tsx.snap delete mode 100644 x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.js create mode 100644 x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.test.tsx create mode 100644 x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.tsx diff --git a/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.ts index 4846054ca26cbd..ce6539c9c45205 100644 --- a/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.ts @@ -95,7 +95,7 @@ export type ColorStylePropertyDescriptor = | ColorDynamicStylePropertyDescriptor; export type IconDynamicOptions = { - iconPaletteId?: string; + iconPaletteId: string | null; customIconStops?: IconStop[]; useCustomIconMap?: boolean; field?: StylePropertyField; diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.test.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.test.ts index 075d19dccdb686..e6349fbe9ab9dc 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.test.ts @@ -5,14 +5,9 @@ */ jest.mock('../../../../kibana_services', () => { - const mockUiSettings = { - get: () => { - return undefined; - }, - }; return { - getUiSettings: () => { - return mockUiSettings; + getIsDarkMode() { + return false; }, }; }); diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts index 49a86f45a681b8..d02f07923c6820 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts @@ -5,14 +5,9 @@ */ jest.mock('../../../../kibana_services', () => { - const mockUiSettings = { - get: () => { - return undefined; - }, - }; return { - getUiSettings: () => { - return mockUiSettings; + getIsDarkMode() { + return false; }, }; }); diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx index ecd625db344119..faae26cac08e71 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx @@ -8,12 +8,8 @@ import sinon from 'sinon'; jest.mock('../../../kibana_services', () => { return { - getUiSettings() { - return { - get() { - return false; - }, - }; + getIsDarkMode() { + return false; }, }; }); diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js index 83c87eb53d4fe7..b364dd32860f36 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js @@ -12,7 +12,7 @@ import { UpdateSourceEditor } from './update_source_editor'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { SOURCE_TYPES } from '../../../../common/constants'; -import { getEmsTileLayerId, getUiSettings } from '../../../kibana_services'; +import { getEmsTileLayerId, getIsDarkMode } from '../../../kibana_services'; import { registerSource } from '../source_registry'; export const sourceTitle = i18n.translate('xpack.maps.source.emsTileTitle', { @@ -122,9 +122,8 @@ export class EMSTMSSource extends AbstractTMSSource { return this._descriptor.id; } - const isDarkMode = getUiSettings().get('theme:darkMode', false); const emsTileLayerId = getEmsTileLayerId(); - return isDarkMode ? emsTileLayerId.dark : emsTileLayerId.bright; + return getIsDarkMode() ? emsTileLayerId.dark : emsTileLayerId.bright; } } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/style_map_select.js b/x-pack/plugins/maps/public/classes/styles/vector/components/style_map_select.js deleted file mode 100644 index e4dc9d1b4d8f6a..00000000000000 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/style_map_select.js +++ /dev/null @@ -1,100 +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 React, { Component, Fragment } from 'react'; - -import { EuiSuperSelect, EuiSpacer } from '@elastic/eui'; - -const CUSTOM_MAP = 'CUSTOM_MAP'; - -export class StyleMapSelect extends Component { - state = {}; - - static getDerivedStateFromProps(nextProps, prevState) { - if (nextProps.customMapStops === prevState.prevPropsCustomMapStops) { - return null; - } - - return { - prevPropsCustomMapStops: nextProps.customMapStops, // reset tracker to latest value - customMapStops: nextProps.customMapStops, // reset customMapStops to latest value - }; - } - - _onMapSelect = (selectedValue) => { - const useCustomMap = selectedValue === CUSTOM_MAP; - this.props.onChange({ - selectedMapId: useCustomMap ? null : selectedValue, - useCustomMap, - }); - }; - - _onCustomMapChange = ({ customMapStops, isInvalid }) => { - // Manage invalid custom map in local state - if (isInvalid) { - this.setState({ customMapStops }); - return; - } - - this.props.onChange({ - useCustomMap: true, - customMapStops, - }); - }; - - _renderCustomStopsInput() { - return !this.props.isCustomOnly && !this.props.useCustomMap - ? null - : this.props.renderCustomStopsInput(this._onCustomMapChange); - } - - _renderMapSelect() { - if (this.props.isCustomOnly) { - return null; - } - - const mapOptionsWithCustom = [ - { - value: CUSTOM_MAP, - inputDisplay: this.props.customOptionLabel, - }, - ...this.props.options, - ]; - - let valueOfSelected; - if (this.props.useCustomMap) { - valueOfSelected = CUSTOM_MAP; - } else { - valueOfSelected = this.props.options.find( - (option) => option.value === this.props.selectedMapId - ) - ? this.props.selectedMapId - : ''; - } - - return ( - - - - - ); - } - - render() { - return ( - - {this._renderMapSelect()} - {this._renderCustomStopsInput()} - - ); - } -} diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/__snapshots__/icon_map_select.test.tsx.snap b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/__snapshots__/icon_map_select.test.tsx.snap new file mode 100644 index 00000000000000..b0b85268aa1c88 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/__snapshots__/icon_map_select.test.tsx.snap @@ -0,0 +1,124 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should not render icon map select when isCustomOnly 1`] = ` + + + +`; + +exports[`Should render custom stops input when useCustomIconMap 1`] = ` + + + mock filledShapes option + , + "value": "filledShapes", + }, + Object { + "inputDisplay":
+ mock hollowShapes option +
, + "value": "hollowShapes", + }, + ] + } + valueOfSelected="CUSTOM_MAP_ID" + /> + + +
+`; + +exports[`Should render default props 1`] = ` + + + mock filledShapes option + , + "value": "filledShapes", + }, + Object { + "inputDisplay":
+ mock hollowShapes option +
, + "value": "hollowShapes", + }, + ] + } + valueOfSelected="filledShapes" + /> + +
+`; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/dynamic_icon_form.js b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/dynamic_icon_form.js index e3724d42a783b7..0601922077b4ad 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/dynamic_icon_form.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/dynamic_icon_form.js @@ -12,11 +12,9 @@ import { IconMapSelect } from './icon_map_select'; export function DynamicIconForm({ fields, - isDarkMode, onDynamicStyleChange, staticDynamicSelect, styleProperty, - symbolOptions, }) { const styleOptions = styleProperty.getOptions(); @@ -44,11 +42,8 @@ export function DynamicIconForm({ return ( ); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.js b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.js deleted file mode 100644 index 6cfe656d65a1e3..00000000000000 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.js +++ /dev/null @@ -1,59 +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 React from 'react'; - -import { StyleMapSelect } from '../style_map_select'; -import { i18n } from '@kbn/i18n'; -import { IconStops } from './icon_stops'; -import { getIconPaletteOptions } from '../../symbol_utils'; - -export function IconMapSelect({ - customIconStops, - iconPaletteId, - isDarkMode, - onChange, - styleProperty, - symbolOptions, - useCustomIconMap, - isCustomOnly, -}) { - function onMapSelectChange({ customMapStops, selectedMapId, useCustomMap }) { - onChange({ - customIconStops: customMapStops, - iconPaletteId: selectedMapId, - useCustomIconMap: useCustomMap, - }); - } - - function renderCustomIconStopsInput(onCustomMapChange) { - return ( - - ); - } - - return ( - - ); -} diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.test.tsx new file mode 100644 index 00000000000000..4e68baf0bd7b7f --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.test.tsx @@ -0,0 +1,78 @@ +/* + * 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. + */ +/* eslint-disable max-classes-per-file */ + +jest.mock('./icon_stops', () => ({ + IconStops: () => { + return
mockIconStops
; + }, +})); + +jest.mock('../../symbol_utils', () => { + return { + getIconPaletteOptions: () => { + return [ + { value: 'filledShapes', inputDisplay:
mock filledShapes option
}, + { value: 'hollowShapes', inputDisplay:
mock hollowShapes option
}, + ]; + }, + PREFERRED_ICONS: ['circle'], + }; +}); + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { FIELD_ORIGIN } from '../../../../../../common/constants'; +import { AbstractField } from '../../../../fields/field'; +import { IDynamicStyleProperty } from '../../properties/dynamic_style_property'; +import { IconMapSelect } from './icon_map_select'; + +class MockField extends AbstractField {} + +class MockDynamicStyleProperty { + getField() { + return new MockField({ fieldName: 'myField', origin: FIELD_ORIGIN.SOURCE }); + } + + getValueSuggestions() { + return []; + } +} + +const defaultProps = { + iconPaletteId: 'filledShapes', + onChange: () => {}, + styleProperty: (new MockDynamicStyleProperty() as unknown) as IDynamicStyleProperty, + isCustomOnly: false, +}; + +test('Should render default props', () => { + const component = shallow(); + + expect(component).toMatchSnapshot(); +}); + +test('Should render custom stops input when useCustomIconMap', () => { + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); +}); + +test('Should not render icon map select when isCustomOnly', () => { + const component = shallow(); + + expect(component).toMatchSnapshot(); +}); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.tsx new file mode 100644 index 00000000000000..1dd55bbb47f78a --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.tsx @@ -0,0 +1,136 @@ +/* + * 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, Fragment } from 'react'; +import { EuiSuperSelect, EuiSpacer } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +// @ts-expect-error +import { IconStops } from './icon_stops'; +// @ts-expect-error +import { getIconPaletteOptions, PREFERRED_ICONS } from '../../symbol_utils'; +import { IconStop } from '../../../../../../common/descriptor_types'; +import { IDynamicStyleProperty } from '../../properties/dynamic_style_property'; + +const CUSTOM_MAP_ID = 'CUSTOM_MAP_ID'; + +const DEFAULT_ICON_STOPS = [ + { stop: null, icon: PREFERRED_ICONS[0] }, // first stop is the "other" category + { stop: '', icon: PREFERRED_ICONS[1] }, +]; + +interface StyleOptionChanges { + customIconStops?: IconStop[]; + iconPaletteId?: string | null; + useCustomIconMap: boolean; +} + +interface Props { + customIconStops?: IconStop[]; + iconPaletteId: string | null; + onChange: ({ customIconStops, iconPaletteId, useCustomIconMap }: StyleOptionChanges) => void; + styleProperty: IDynamicStyleProperty; + useCustomIconMap?: boolean; + isCustomOnly: boolean; +} + +interface State { + customIconStops: IconStop[]; +} + +export class IconMapSelect extends Component { + state = { + customIconStops: this.props.customIconStops ? this.props.customIconStops : DEFAULT_ICON_STOPS, + }; + + _onMapSelect = (selectedValue: string) => { + const useCustomIconMap = selectedValue === CUSTOM_MAP_ID; + const changes: StyleOptionChanges = { + iconPaletteId: useCustomIconMap ? null : selectedValue, + useCustomIconMap, + }; + // edge case when custom palette is first enabled + // customIconStops is undefined so need to update custom stops with default so icons are rendered. + if (!this.props.customIconStops) { + changes.customIconStops = DEFAULT_ICON_STOPS; + } + this.props.onChange(changes); + }; + + _onCustomMapChange = ({ + customStops, + isInvalid, + }: { + customStops: IconStop[]; + isInvalid: boolean; + }) => { + // Manage invalid custom map in local state + this.setState({ customIconStops: customStops }); + + if (!isInvalid) { + this.props.onChange({ + useCustomIconMap: true, + customIconStops: customStops, + }); + } + }; + + _renderCustomStopsInput() { + return !this.props.isCustomOnly && !this.props.useCustomIconMap ? null : ( + + ); + } + + _renderMapSelect() { + if (this.props.isCustomOnly) { + return null; + } + + const mapOptionsWithCustom = [ + { + value: CUSTOM_MAP_ID, + inputDisplay: i18n.translate('xpack.maps.styles.icon.customMapLabel', { + defaultMessage: 'Custom icon palette', + }), + }, + ...getIconPaletteOptions(), + ]; + + let valueOfSelected = ''; + if (this.props.useCustomIconMap) { + valueOfSelected = CUSTOM_MAP_ID; + } else if (this.props.iconPaletteId) { + valueOfSelected = this.props.iconPaletteId; + } + + return ( + + + + + ); + } + + render() { + return ( + + {this._renderMapSelect()} + {this._renderCustomStopsInput()} + + ); + } +} diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_select.js b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_select.js index 1ceff3e3ba8019..c8ad869d33d33f 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_select.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_select.js @@ -15,6 +15,8 @@ import { EuiSelectable, } from '@elastic/eui'; import { SymbolIcon } from '../legend/symbol_icon'; +import { SYMBOL_OPTIONS } from '../../symbol_utils'; +import { getIsDarkMode } from '../../../../../kibana_services'; function isKeyboardEvent(event) { return typeof event === 'object' && 'keyCode' in event; @@ -62,7 +64,6 @@ export class IconSelect extends Component { }; _renderPopoverButton() { - const { isDarkMode, value } = this.props; return ( } /> @@ -93,8 +94,7 @@ export class IconSelect extends Component { } _renderIconSelectable() { - const { isDarkMode } = this.props; - const options = this.props.symbolOptions.map(({ value, label }) => { + const options = SYMBOL_OPTIONS.map(({ value, label }) => { return { value, label, @@ -102,7 +102,7 @@ export class IconSelect extends Component { ), }; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_select.test.js b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_select.test.js index 56dce6fad8386c..8dc2057054e626 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_select.test.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_select.test.js @@ -4,25 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ +jest.mock('../../../../../kibana_services', () => { + return { + getIsDarkMode() { + return false; + }, + }; +}); + +jest.mock('../../symbol_utils', () => { + return { + SYMBOL_OPTIONS: [ + { value: 'symbol1', label: 'symbol1' }, + { value: 'symbol2', label: 'symbol2' }, + ], + }; +}); + import React from 'react'; import { shallow } from 'enzyme'; import { IconSelect } from './icon_select'; -const symbolOptions = [ - { value: 'symbol1', label: 'symbol1' }, - { value: 'symbol2', label: 'symbol2' }, -]; - test('Should render icon select', () => { - const component = shallow( - {}} - symbolOptions={symbolOptions} - isDarkMode={false} - /> - ); + const component = shallow( {}} />); expect(component).toMatchSnapshot(); }); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_stops.js b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_stops.js index 81a44fcaadbd36..78fa6c10b899d2 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_stops.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_stops.js @@ -11,7 +11,7 @@ import { getOtherCategoryLabel } from '../../style_util'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldText } from '@elastic/eui'; import { IconSelect } from './icon_select'; import { StopInput } from '../stop_input'; -import { PREFERRED_ICONS } from '../../symbol_utils'; +import { PREFERRED_ICONS, SYMBOL_OPTIONS } from '../../symbol_utils'; function isDuplicateStop(targetStop, iconStops) { const stops = iconStops.filter(({ stop }) => { @@ -20,7 +20,7 @@ function isDuplicateStop(targetStop, iconStops) { return stops.length > 1; } -export function getFirstUnusedSymbol(symbolOptions, iconStops) { +export function getFirstUnusedSymbol(iconStops) { const firstUnusedPreferredIconId = PREFERRED_ICONS.find((iconId) => { const isSymbolBeingUsed = iconStops.some(({ icon }) => { return icon === iconId; @@ -32,7 +32,7 @@ export function getFirstUnusedSymbol(symbolOptions, iconStops) { return firstUnusedPreferredIconId; } - const firstUnusedSymbol = symbolOptions.find(({ value }) => { + const firstUnusedSymbol = SYMBOL_OPTIONS.find(({ value }) => { const isSymbolBeingUsed = iconStops.some(({ icon }) => { return icon === value; }); @@ -42,19 +42,7 @@ export function getFirstUnusedSymbol(symbolOptions, iconStops) { return firstUnusedSymbol ? firstUnusedSymbol.value : DEFAULT_ICON; } -const DEFAULT_ICON_STOPS = [ - { stop: null, icon: PREFERRED_ICONS[0] }, //first stop is the "other" color - { stop: '', icon: PREFERRED_ICONS[1] }, -]; - -export function IconStops({ - field, - getValueSuggestions, - iconStops = DEFAULT_ICON_STOPS, - isDarkMode, - onChange, - symbolOptions, -}) { +export function IconStops({ field, getValueSuggestions, iconStops, onChange }) { return iconStops.map(({ stop, icon }, index) => { const onIconSelect = (selectedIconId) => { const newIconStops = [...iconStops]; @@ -62,7 +50,7 @@ export function IconStops({ ...iconStops[index], icon: selectedIconId, }; - onChange({ customMapStops: newIconStops }); + onChange({ customStops: newIconStops }); }; const onStopChange = (newStopValue) => { const newIconStops = [...iconStops]; @@ -71,17 +59,17 @@ export function IconStops({ stop: newStopValue, }; onChange({ - customMapStops: newIconStops, + customStops: newIconStops, isInvalid: isDuplicateStop(newStopValue, iconStops), }); }; const onAdd = () => { onChange({ - customMapStops: [ + customStops: [ ...iconStops.slice(0, index + 1), { stop: '', - icon: getFirstUnusedSymbol(symbolOptions, iconStops), + icon: getFirstUnusedSymbol(iconStops), }, ...iconStops.slice(index + 1), ], @@ -89,7 +77,7 @@ export function IconStops({ }; const onRemove = () => { onChange({ - customMapStops: [...iconStops.slice(0, index), ...iconStops.slice(index + 1)], + customStops: [...iconStops.slice(0, index), ...iconStops.slice(index + 1)], }); }; @@ -157,13 +145,7 @@ export function IconStops({ {stopInput} - + diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_stops.test.js b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_stops.test.js index ffe9b6feef4624..fe73659b0fe58d 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_stops.test.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_stops.test.js @@ -4,17 +4,41 @@ * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; import { getFirstUnusedSymbol } from './icon_stops'; -describe('getFirstUnusedSymbol', () => { - const symbolOptions = [{ value: 'icon1' }, { value: 'icon2' }]; +jest.mock('./icon_select', () => ({ + IconSelect: () => { + return
mockIconSelect
; + }, +})); + +jest.mock('../../symbol_utils', () => { + return { + SYMBOL_OPTIONS: [{ value: 'icon1' }, { value: 'icon2' }], + PREFERRED_ICONS: [ + 'circle', + 'marker', + 'square', + 'star', + 'triangle', + 'hospital', + 'circle-stroked', + 'marker-stroked', + 'square-stroked', + 'star-stroked', + 'triangle-stroked', + ], + }; +}); +describe('getFirstUnusedSymbol', () => { test('Should return first unused icon from PREFERRED_ICONS', () => { const iconStops = [ { stop: 'category1', icon: 'circle' }, { stop: 'category2', icon: 'marker' }, ]; - const nextIcon = getFirstUnusedSymbol(symbolOptions, iconStops); + const nextIcon = getFirstUnusedSymbol(iconStops); expect(nextIcon).toBe('square'); }); @@ -33,7 +57,7 @@ describe('getFirstUnusedSymbol', () => { { stop: 'category11', icon: 'triangle-stroked' }, { stop: 'category12', icon: 'icon1' }, ]; - const nextIcon = getFirstUnusedSymbol(symbolOptions, iconStops); + const nextIcon = getFirstUnusedSymbol(iconStops); expect(nextIcon).toBe('icon2'); }); @@ -53,7 +77,7 @@ describe('getFirstUnusedSymbol', () => { { stop: 'category12', icon: 'icon1' }, { stop: 'category13', icon: 'icon2' }, ]; - const nextIcon = getFirstUnusedSymbol(symbolOptions, iconStops); + const nextIcon = getFirstUnusedSymbol(iconStops); expect(nextIcon).toBe('marker'); }); }); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/static_icon_form.js b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/static_icon_form.js index 56e5737f724498..986f279dddc1a2 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/static_icon_form.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/static_icon_form.js @@ -8,13 +8,7 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { IconSelect } from './icon_select'; -export function StaticIconForm({ - isDarkMode, - onStaticStyleChange, - staticDynamicSelect, - styleProperty, - symbolOptions, -}) { +export function StaticIconForm({ onStaticStyleChange, staticDynamicSelect, styleProperty }) { const onChange = (selectedIconId) => { onStaticStyleChange(styleProperty.getStyleName(), { value: selectedIconId }); }; @@ -25,12 +19,7 @@ export function StaticIconForm({ {staticDynamicSelect} - + ); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.js b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.js index 36b6c1a76470ca..2a983a32f0d825 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.js @@ -6,25 +6,15 @@ import React from 'react'; -import { getUiSettings } from '../../../../../kibana_services'; import { StylePropEditor } from '../style_prop_editor'; import { DynamicIconForm } from './dynamic_icon_form'; import { StaticIconForm } from './static_icon_form'; -import { SYMBOL_OPTIONS } from '../../symbol_utils'; export function VectorStyleIconEditor(props) { const iconForm = props.styleProperty.isDynamic() ? ( - + ) : ( - + ); return {iconForm}; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.d.ts b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.d.ts index b53623ab52edb2..e153b6e4850f79 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.d.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.d.ts @@ -33,4 +33,5 @@ export interface IDynamicStyleProperty extends IStyleProperty { pluckCategoricalStyleMetaFromFeatures(features: unknown[]): CategoryFieldMeta; pluckOrdinalStyleMetaFromFieldMetaData(fieldMetaData: unknown): RangeFieldMeta; pluckCategoricalStyleMetaFromFieldMetaData(fieldMetaData: unknown): CategoryFieldMeta; + getValueSuggestions(query: string): string[]; } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.js b/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.js index 04df9d73d75cd8..3a5f9b8f6690ec 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/symbol_utils.js @@ -9,6 +9,7 @@ import maki from '@elastic/maki'; import xml2js from 'xml2js'; import { parseXmlString } from '../../../../common/parse_xml_string'; import { SymbolIcon } from './components/legend/symbol_icon'; +import { getIsDarkMode } from '../../../kibana_services'; export const LARGE_MAKI_ICON_SIZE = 15; const LARGE_MAKI_ICON_SIZE_AS_STRING = LARGE_MAKI_ICON_SIZE.toString(); @@ -111,7 +112,8 @@ ICON_PALETTES.forEach((iconPalette) => { }); }); -export function getIconPaletteOptions(isDarkMode) { +export function getIconPaletteOptions() { + const isDarkMode = getIsDarkMode(); return ICON_PALETTES.map(({ id, icons }) => { const iconsDisplay = icons.map((iconId) => { const style = { diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.test.ts b/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.test.ts index bc032639dd07d4..d630d2909b3d8b 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.test.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.test.ts @@ -5,14 +5,9 @@ */ jest.mock('../../../kibana_services', () => { - const mockUiSettings = { - get: () => { - return undefined; - }, - }; return { - getUiSettings: () => { - return mockUiSettings; + getIsDarkMode() { + return false; }, }; }); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.ts b/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.ts index a3ae80e0a59359..50321510c2ba82 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.ts @@ -18,8 +18,7 @@ import { CATEGORICAL_COLOR_PALETTES, } from '../color_palettes'; import { VectorStylePropertiesDescriptor } from '../../../../common/descriptor_types'; -// @ts-ignore -import { getUiSettings } from '../../../kibana_services'; +import { getIsDarkMode } from '../../../kibana_services'; export const MIN_SIZE = 1; export const MAX_SIZE = 64; @@ -67,7 +66,7 @@ export function getDefaultStaticProperties( const nextFillColor = DEFAULT_FILL_COLORS[nextColorIndex]; const nextLineColor = DEFAULT_LINE_COLORS[nextColorIndex]; - const isDarkMode = getUiSettings().get('theme:darkMode', false); + const isDarkMode = getIsDarkMode(); return { [VECTOR_STYLES.ICON]: { diff --git a/x-pack/plugins/maps/public/kibana_services.d.ts b/x-pack/plugins/maps/public/kibana_services.d.ts index 8fa52500fb16e1..d4a7fa5d50af8a 100644 --- a/x-pack/plugins/maps/public/kibana_services.d.ts +++ b/x-pack/plugins/maps/public/kibana_services.d.ts @@ -24,6 +24,7 @@ export function getVisualizations(): any; export function getDocLinks(): any; export function getCoreChrome(): any; export function getUiSettings(): any; +export function getIsDarkMode(): boolean; export function getCoreOverlays(): any; export function getData(): any; export function getUiActions(): any; diff --git a/x-pack/plugins/maps/public/kibana_services.js b/x-pack/plugins/maps/public/kibana_services.js index 1684acfb0f463b..97d7f0c66c629a 100644 --- a/x-pack/plugins/maps/public/kibana_services.js +++ b/x-pack/plugins/maps/public/kibana_services.js @@ -40,6 +40,9 @@ export const getFileUploadComponent = () => { let uiSettings; export const setUiSettings = (coreUiSettings) => (uiSettings = coreUiSettings); export const getUiSettings = () => uiSettings; +export const getIsDarkMode = () => { + return getUiSettings().get('theme:darkMode', false); +}; let indexPatternSelectComponent; export const setIndexPatternSelect = (indexPatternSelect) =>