diff --git a/x-pack/plugins/maps/public/actions/layer_actions.test.ts b/x-pack/plugins/maps/public/actions/layer_actions.test.ts index cbec68e5108f8..06adbed92c0cf 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.test.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.test.ts @@ -39,11 +39,6 @@ describe('layer_actions', () => { return true; }; - // eslint-disable-next-line @typescript-eslint/no-var-requires - require('../selectors/map_selectors').getCustomIcons = () => { - return []; - }; - // eslint-disable-next-line @typescript-eslint/no-var-requires require('../selectors/map_selectors').createLayerInstance = () => { return { diff --git a/x-pack/plugins/maps/public/actions/layer_actions.ts b/x-pack/plugins/maps/public/actions/layer_actions.ts index a89172f8ce340..d66c2d97f55ef 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.ts @@ -11,7 +11,6 @@ import { Query } from 'src/plugins/data/public'; import { MapStoreState } from '../reducers/store'; import { createLayerInstance, - getCustomIcons, getEditState, getLayerById, getLayerList, @@ -175,7 +174,7 @@ export function addLayer(layerDescriptor: LayerDescriptor) { layer: layerDescriptor, }); dispatch(syncDataForLayerId(layerDescriptor.id, false)); - const layer = createLayerInstance(layerDescriptor, getCustomIcons(getState())); + const layer = createLayerInstance(layerDescriptor, []); // custom icons not needed, layer instance only used to get licensed features const features = await layer.getLicensedFeatures(); features.forEach(notifyLicensedFeatureUsage); }; diff --git a/x-pack/plugins/maps/public/actions/map_actions.ts b/x-pack/plugins/maps/public/actions/map_actions.ts index 24a378335bc56..1e55bd6bbb1e5 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.ts +++ b/x-pack/plugins/maps/public/actions/map_actions.ts @@ -119,9 +119,7 @@ export function updateCustomIcons(customIcons: CustomIcon[]) { return { type: UPDATE_MAP_SETTING, settingKey: 'customIcons', - settingValue: customIcons.map((icon) => { - return { ...icon, svg: Buffer.from(icon.svg).toString('base64') }; - }), + settingValue: customIcons, }; } diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx index 8cbfcd3a41e80..52228988e67c5 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_control.tsx @@ -129,7 +129,10 @@ export class DrawControl extends Component { } else if (this.props.drawShape === DRAW_SHAPE.WAIT) { this.props.mbMap.getCanvas().style.cursor = 'wait'; this._mbDrawControl.changeMode(SIMPLE_SELECT); - } else { + } else if ( + (drawMode !== SIMPLE_SELECT && !this.props.drawShape) || + this.props.drawShape === DRAW_SHAPE.SIMPLE_SELECT + ) { this._mbDrawControl.changeMode(SIMPLE_SELECT); } } diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts index 781a72aabf78b..7db8606110d01 100644 --- a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts +++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts @@ -11,6 +11,7 @@ import { EmbeddableStateTransfer } from 'src/plugins/embeddable/public'; import { MapSavedObjectAttributes } from '../../../../common/map_saved_object_type'; import { APP_ID, MAP_PATH, MAP_SAVED_OBJECT_TYPE } from '../../../../common/constants'; import { createMapStore, MapStore, MapStoreState } from '../../../reducers/store'; +import { MapSettings } from '../../../reducers/map'; import { getTimeFilters, getMapZoom, @@ -50,6 +51,19 @@ import { whenLicenseInitialized } from '../../../licensed_features'; import { SerializedMapState, SerializedUiState } from './types'; import { setAutoOpenLayerWizardId } from '../../../actions/ui_actions'; +function setMapSettingsFromEncodedState(settings: Partial) { + const decodedCustomIcons = settings.customIcons + ? // base64 decode svg string + settings.customIcons.map((icon) => { + return { ...icon, svg: Buffer.from(icon.svg, 'base64').toString('utf-8') }; + }) + : []; + return setMapSettings({ + ...settings, + customIcons: decodedCustomIcons, + }); +} + export class SavedMap { private _attributes: MapSavedObjectAttributes | null = null; private _sharingSavedObjectProps: SharingSavedObjectProps | null = null; @@ -123,12 +137,12 @@ export class SavedMap { } if (this._mapEmbeddableInput && this._mapEmbeddableInput.mapSettings !== undefined) { - this._store.dispatch(setMapSettings(this._mapEmbeddableInput.mapSettings)); + this._store.dispatch(setMapSettingsFromEncodedState(this._mapEmbeddableInput.mapSettings)); } else if (this._attributes?.mapStateJSON) { try { const mapState = JSON.parse(this._attributes.mapStateJSON) as SerializedMapState; if (mapState.settings) { - this._store.dispatch(setMapSettings(mapState.settings)); + this._store.dispatch(setMapSettingsFromEncodedState(mapState.settings)); } } catch (e) { // ignore malformed mapStateJSON, not a critical error for viewing map - map will just use defaults @@ -439,6 +453,8 @@ export class SavedMap { const layerListConfigOnly = copyPersistentState(layerList); this._attributes!.layerListJSON = JSON.stringify(layerListConfigOnly); + const mapSettings = getMapSettings(state); + this._attributes!.mapStateJSON = JSON.stringify({ zoom: getMapZoom(state), center: getMapCenter(state), @@ -449,7 +465,13 @@ export class SavedMap { }, query: getQuery(state), filters: getFilters(state), - settings: getMapSettings(state), + settings: { + ...mapSettings, + // base64 encode custom icons to avoid svg strings breaking saved object stringification/parsing. + customIcons: mapSettings.customIcons.map((icon) => { + return { ...icon, svg: Buffer.from(icon.svg).toString('base64') }; + }), + }, } as SerializedMapState); this._attributes!.uiStateJSON = JSON.stringify({ diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts index 3c08a0e6f19ce..baca2d79b833d 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts @@ -35,7 +35,7 @@ import { getQueryableUniqueIndexPatternIds, } from './map_selectors'; -import { CustomIcon, LayerDescriptor, VectorLayerDescriptor } from '../../common/descriptor_types'; +import { LayerDescriptor, VectorLayerDescriptor } from '../../common/descriptor_types'; import { ILayer } from '../classes/layers/layer'; import { Filter } from '@kbn/es-query'; import { ESSearchSource } from '../classes/sources/es_search_source'; @@ -255,13 +255,8 @@ describe('getQueryableUniqueIndexPatternIds', () => { ]; const waitingForMapReadyLayerList: VectorLayerDescriptor[] = [] as unknown as VectorLayerDescriptor[]; - const customIcons: CustomIcon[] = []; expect( - getQueryableUniqueIndexPatternIds.resultFunc( - layerList, - waitingForMapReadyLayerList, - customIcons - ) + getQueryableUniqueIndexPatternIds.resultFunc(layerList, waitingForMapReadyLayerList) ).toEqual(['foo', 'bar']); }); @@ -279,13 +274,8 @@ describe('getQueryableUniqueIndexPatternIds', () => { createWaitLayerDescriptorMock({ indexPatternId: 'fbr' }), createWaitLayerDescriptorMock({ indexPatternId: 'foo' }), ] as unknown as VectorLayerDescriptor[]; - const customIcons: CustomIcon[] = []; expect( - getQueryableUniqueIndexPatternIds.resultFunc( - layerList, - waitingForMapReadyLayerList, - customIcons - ) + getQueryableUniqueIndexPatternIds.resultFunc(layerList, waitingForMapReadyLayerList) ).toEqual(['foo', 'fbr']); }); }); diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index f86f3dd927c69..8e99a625cbaa2 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -190,11 +190,7 @@ export const getTimeFilters = ({ map }: MapStoreState): TimeRange => export const getTimeslice = ({ map }: MapStoreState) => map.mapState.timeslice; export const getCustomIcons = ({ map }: MapStoreState): CustomIcon[] => { - return ( - map.settings.customIcons.map((icon) => { - return { ...icon, svg: Buffer.from(icon.svg, 'base64').toString('utf-8') }; - }) ?? [] - ); + return map.settings.customIcons; }; export const getQuery = ({ map }: MapStoreState): Query | undefined => map.mapState.query; @@ -274,8 +270,7 @@ export const getDataFilters = createSelector( export const getSpatialFiltersLayer = createSelector( getFilters, getMapSettings, - getCustomIcons, - (filters, settings, customIcons) => { + (filters, settings) => { const featureCollection: FeatureCollection = { type: 'FeatureCollection', features: extractFeaturesFromFilters(filters), @@ -312,7 +307,7 @@ export const getSpatialFiltersLayer = createSelector( }), }), source: new GeoJsonFileSource(geoJsonSourceDescriptor), - customIcons, + customIcons: [], // spatial filters layer does not support custom icons }); } ); @@ -396,13 +391,12 @@ export const getSelectedLayerJoinDescriptors = createSelector(getSelectedLayer, export const getQueryableUniqueIndexPatternIds = createSelector( getLayerList, getWaitingForMapReadyLayerListRaw, - getCustomIcons, - (layerList, waitingForMapReadyLayerList, customIcons) => { + (layerList, waitingForMapReadyLayerList) => { const indexPatternIds: string[] = []; if (waitingForMapReadyLayerList.length) { waitingForMapReadyLayerList.forEach((layerDescriptor) => { - const layer = createLayerInstance(layerDescriptor, customIcons); + const layer = createLayerInstance(layerDescriptor, []); // custom icons not needed, layer instance only used to get index pattern ids if (layer.isVisible()) { indexPatternIds.push(...layer.getQueryableIndexPatternIds()); } @@ -421,13 +415,12 @@ export const getQueryableUniqueIndexPatternIds = createSelector( export const getGeoFieldNames = createSelector( getLayerList, getWaitingForMapReadyLayerListRaw, - getCustomIcons, - (layerList, waitingForMapReadyLayerList, customIcons) => { + (layerList, waitingForMapReadyLayerList) => { const geoFieldNames: string[] = []; if (waitingForMapReadyLayerList.length) { waitingForMapReadyLayerList.forEach((layerDescriptor) => { - const layer = createLayerInstance(layerDescriptor, customIcons); + const layer = createLayerInstance(layerDescriptor, []); // custom icons not needed, layer instance only used to get geo field names geoFieldNames.push(...layer.getGeoFieldNames()); }); } else {