[Maps] Provide ability to create MapEmbeddable directly from a map configuration (#43878)
* [Maps] Provide ability to create MapEmbeddable directly from a map configuration * call updateInput instead of passing modified input to MapEmbeddable constructor * cleanup i18n translations * update map embeddable documenation to reflect createFromState examples
This commit is contained in:
parent
98e9708d7e
commit
48e563f554
|
@ -9,10 +9,10 @@ import { EMSTMSSource } from '../layers/sources/ems_tms_source';
|
|||
import chrome from 'ui/chrome';
|
||||
import { getKibanaTileMap } from '../meta';
|
||||
|
||||
export function getInitialLayers(savedMapLayerListJSON) {
|
||||
export function getInitialLayers(layerListJSON) {
|
||||
|
||||
if (savedMapLayerListJSON) {
|
||||
return JSON.parse(savedMapLayerListJSON);
|
||||
if (layerListJSON) {
|
||||
return JSON.parse(layerListJSON);
|
||||
}
|
||||
|
||||
const tilemapSourceFromKibana = getKibanaTileMap();
|
||||
|
|
38
x-pack/legacy/plugins/maps/public/embeddable/README.md
Normal file
38
x-pack/legacy/plugins/maps/public/embeddable/README.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
|
||||
### Map specific `input` parameters
|
||||
- **hideFilterActions:** (Boolean) Set to true to hide all filtering controls.
|
||||
- **isLayerTOCOpen:** (Boolean) Set to false to render map with legend in collapsed state.
|
||||
- **openTOCDetails:** (Array of Strings) Array of layer ids. Add layer id to show layer details on initial render.
|
||||
- **mapCenter:** ({lat, lon, zoom }) Provide mapCenter to customize initial map location.
|
||||
|
||||
### Creating a Map embeddable from saved object
|
||||
```
|
||||
const factory = new MapEmbeddableFactory();
|
||||
const input = {
|
||||
hideFilterActions: true,
|
||||
isLayerTOCOpen: false,
|
||||
openTOCDetails: ['tfi3f', 'edh66'],
|
||||
mapCenter: { lat: 0.0, lon: 0.0, zoom: 7 }
|
||||
}
|
||||
const mapEmbeddable = await factory.createFromSavedObject(
|
||||
'de71f4f0-1902-11e9-919b-ffe5949a18d2',
|
||||
input,
|
||||
parent
|
||||
);
|
||||
```
|
||||
|
||||
### Creating a Map embeddable from state
|
||||
```
|
||||
const factory = new MapEmbeddableFactory();
|
||||
const state = {
|
||||
layerList: [], // where layerList is same as saved object layerListJSON property (unstringified)
|
||||
title: 'my map',
|
||||
}
|
||||
const input = {
|
||||
hideFilterActions: true,
|
||||
isLayerTOCOpen: false,
|
||||
openTOCDetails: ['tfi3f', 'edh66'],
|
||||
mapCenter: { lat: 0.0, lon: 0.0, zoom: 7 }
|
||||
}
|
||||
const mapEmbeddable = await factory.createFromState(state, input, parent);
|
||||
```
|
|
@ -18,7 +18,6 @@ import { I18nContext } from 'ui/i18n';
|
|||
|
||||
import { GisMap } from '../connected_components/gis_map';
|
||||
import { createMapStore } from '../reducers/store';
|
||||
import { getInitialLayers } from '../angular/get_initial_layers';
|
||||
import {
|
||||
setGotoWithCenter,
|
||||
replaceLayerList,
|
||||
|
@ -26,7 +25,6 @@ import {
|
|||
setRefreshConfig,
|
||||
disableScrollZoom,
|
||||
} from '../actions/map_actions';
|
||||
import { DEFAULT_IS_LAYER_TOC_OPEN } from '../reducers/ui';
|
||||
import {
|
||||
setReadOnly,
|
||||
setIsLayerTOCOpen,
|
||||
|
@ -47,11 +45,11 @@ export class MapEmbeddable extends Embeddable {
|
|||
editUrl: config.editUrl,
|
||||
indexPatterns: config.indexPatterns,
|
||||
editable: config.editable,
|
||||
defaultTitle: config.savedMap.title
|
||||
defaultTitle: config.title
|
||||
},
|
||||
parent);
|
||||
|
||||
this._savedMap = config.savedMap;
|
||||
this._layerList = config.layerList;
|
||||
this._store = createMapStore();
|
||||
|
||||
this._subscription = this.getInput$().subscribe((input) => this.onContainerStateChanged(input));
|
||||
|
@ -103,16 +101,10 @@ export class MapEmbeddable extends Embeddable {
|
|||
|
||||
if (_.has(this.input, 'isLayerTOCOpen')) {
|
||||
this._store.dispatch(setIsLayerTOCOpen(this.input.isLayerTOCOpen));
|
||||
} else if (this._savedMap.uiStateJSON) {
|
||||
const uiState = JSON.parse(this._savedMap.uiStateJSON);
|
||||
this._store.dispatch(setIsLayerTOCOpen(_.get(uiState, 'isLayerTOCOpen', DEFAULT_IS_LAYER_TOC_OPEN)));
|
||||
}
|
||||
|
||||
if (_.has(this.input, 'openTOCDetails')) {
|
||||
this._store.dispatch(setOpenTOCDetails(this.input.openTOCDetails));
|
||||
} else if (this._savedMap.uiStateJSON) {
|
||||
const uiState = JSON.parse(this._savedMap.uiStateJSON);
|
||||
this._store.dispatch(setOpenTOCDetails(_.get(uiState, 'openTOCDetails', [])));
|
||||
}
|
||||
|
||||
if (this.input.mapCenter) {
|
||||
|
@ -121,16 +113,9 @@ export class MapEmbeddable extends Embeddable {
|
|||
lon: this.input.mapCenter.lon,
|
||||
zoom: this.input.mapCenter.zoom,
|
||||
}));
|
||||
} else if (this._savedMap.mapStateJSON) {
|
||||
const mapState = JSON.parse(this._savedMap.mapStateJSON);
|
||||
this._store.dispatch(setGotoWithCenter({
|
||||
lat: mapState.center.lat,
|
||||
lon: mapState.center.lon,
|
||||
zoom: mapState.zoom,
|
||||
}));
|
||||
}
|
||||
const layerList = getInitialLayers(this._savedMap.layerListJSON);
|
||||
this._store.dispatch(replaceLayerList(layerList));
|
||||
|
||||
this._store.dispatch(replaceLayerList(this._layerList));
|
||||
this._dispatchSetQuery(this.input);
|
||||
this._dispatchSetRefreshConfig(this.input);
|
||||
|
||||
|
@ -162,7 +147,7 @@ export class MapEmbeddable extends Embeddable {
|
|||
if (this._unsubscribeFromStore) {
|
||||
this._unsubscribeFromStore();
|
||||
}
|
||||
this._savedMap.destroy();
|
||||
|
||||
if (this._domNode) {
|
||||
unmountComponentAtNode(this._domNode);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/con
|
|||
import { createMapStore } from '../reducers/store';
|
||||
import { addLayerWithoutDataSync } from '../actions/map_actions';
|
||||
import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors';
|
||||
import { getInitialLayers } from '../angular/get_initial_layers';
|
||||
import { mergeInputWithSavedMap } from './merge_input_with_saved_map';
|
||||
import '../angular/services/gis_map_saved_object_loader';
|
||||
import 'ui/vis/map/service_settings';
|
||||
|
||||
|
@ -50,19 +52,20 @@ export class MapEmbeddableFactory extends EmbeddableFactory {
|
|||
});
|
||||
}
|
||||
|
||||
async _getIndexPatterns(layerListJSON) {
|
||||
async _getIndexPatterns(layerList) {
|
||||
// Need to extract layerList from store to get queryable index pattern ids
|
||||
const store = createMapStore();
|
||||
let queryableIndexPatternIds;
|
||||
try {
|
||||
JSON.parse(layerListJSON).forEach(layerDescriptor => {
|
||||
layerList.forEach(layerDescriptor => {
|
||||
store.dispatch(addLayerWithoutDataSync(layerDescriptor));
|
||||
});
|
||||
queryableIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState());
|
||||
} catch (error) {
|
||||
throw new Error(i18n.translate('xpack.maps.mapEmbeddableFactory', {
|
||||
defaultMessage: 'Unable to load map, malformed saved object',
|
||||
throw new Error(i18n.translate('xpack.maps.mapEmbeddableFactory.invalidLayerList', {
|
||||
defaultMessage: 'Unable to load map, malformed layer list',
|
||||
}));
|
||||
}
|
||||
const queryableIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState());
|
||||
|
||||
const promises = queryableIndexPatternIds.map(async (indexPatternId) => {
|
||||
try {
|
||||
|
@ -77,20 +80,25 @@ export class MapEmbeddableFactory extends EmbeddableFactory {
|
|||
return _.compact(indexPatterns);
|
||||
}
|
||||
|
||||
async _fetchSavedMap(savedObjectId) {
|
||||
const $injector = await chrome.dangerouslyGetActiveInjector();
|
||||
const savedObjectLoader = $injector.get('gisMapSavedObjectLoader');
|
||||
return await savedObjectLoader.get(savedObjectId);
|
||||
}
|
||||
|
||||
async createFromSavedObject(
|
||||
savedObjectId,
|
||||
input,
|
||||
parent
|
||||
) {
|
||||
const $injector = await chrome.dangerouslyGetActiveInjector();
|
||||
const savedObjectLoader = $injector.get('gisMapSavedObjectLoader');
|
||||
const savedMap = await this._fetchSavedMap(savedObjectId);
|
||||
const layerList = getInitialLayers(savedMap.layerListJSON);
|
||||
const indexPatterns = await this._getIndexPatterns(layerList);
|
||||
|
||||
const savedMap = await savedObjectLoader.get(savedObjectId);
|
||||
const indexPatterns = await this._getIndexPatterns(savedMap.layerListJSON);
|
||||
|
||||
return new MapEmbeddable(
|
||||
const embeddable = new MapEmbeddable(
|
||||
{
|
||||
savedMap,
|
||||
layerList,
|
||||
title: savedMap.title,
|
||||
editUrl: chrome.addBasePath(createMapPath(savedObjectId)),
|
||||
indexPatterns,
|
||||
editable: this.isEditable(),
|
||||
|
@ -98,11 +106,42 @@ export class MapEmbeddableFactory extends EmbeddableFactory {
|
|||
input,
|
||||
parent
|
||||
);
|
||||
|
||||
try {
|
||||
embeddable.updateInput(mergeInputWithSavedMap(input, savedMap));
|
||||
} catch (error) {
|
||||
throw new Error(i18n.translate('xpack.maps.mapEmbeddableFactory.invalidSavedObject', {
|
||||
defaultMessage: 'Unable to load map, malformed saved object',
|
||||
}));
|
||||
}
|
||||
|
||||
return embeddable;
|
||||
}
|
||||
|
||||
async createFromState(
|
||||
state,
|
||||
input,
|
||||
parent
|
||||
) {
|
||||
const layerList = state && state.layerList ? state.layerList : getInitialLayers();
|
||||
const indexPatterns = await this._getIndexPatterns(layerList);
|
||||
|
||||
return new MapEmbeddable(
|
||||
{
|
||||
layerList,
|
||||
title: state && state.title ? state.title : '',
|
||||
editUrl: null,
|
||||
indexPatterns,
|
||||
editable: false,
|
||||
},
|
||||
input,
|
||||
parent
|
||||
);
|
||||
}
|
||||
|
||||
async create(input) {
|
||||
window.location.href = chrome.addBasePath(createMapPath(''));
|
||||
return new ErrorEmbeddable('Maps can only be created from a saved object', input);
|
||||
return new ErrorEmbeddable('Maps can only be created with createFromSavedObject or createFromState', input);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
### MapEmbeddableFactory
|
||||
Use to load map saved object and create MapEmbeddable instance.
|
||||
|
||||
### MapEmbeddable
|
||||
Renders map saved object. State comes from saved object.
|
||||
|
||||
#### Customizable parameters
|
||||
- **hideFilterActions:** (Boolean) Set to true to hide all filtering controls.
|
||||
- **isLayerTOCOpen:** (Boolean) Set to false to render map with legend in collapsed state.
|
||||
- **openTOCDetails:** (Array of Strings) Array of layer ids. Add layer id to show layer details on initial render.
|
||||
- **mapCenter:** ({lat, lon, zoom }) Provide mapCenter to customize initial map location.
|
||||
|
||||
|
||||
Customize embeddable state by providing properties for `MapEmbeddableFactory.createFromSavedObject` `input` parameter.
|
||||
|
||||
```
|
||||
const factory = new MapEmbeddableFactory();
|
||||
const mapEmbeddable = await factory.createFromSavedObject(
|
||||
'de71f4f0-1902-11e9-919b-ffe5949a18d2',
|
||||
{
|
||||
hideFilterActions: true,
|
||||
isLayerTOCOpen: false,
|
||||
openTOCDetails: ['tfi3f', 'edh66'],
|
||||
mapCenter: { lat: 0.0, lon: 0.0, zoom: 7 }
|
||||
}
|
||||
);
|
||||
```
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { DEFAULT_IS_LAYER_TOC_OPEN } from '../reducers/ui';
|
||||
|
||||
const MAP_EMBEDDABLE_INPUT_KEYS = [
|
||||
'hideFilterActions',
|
||||
'isLayerTOCOpen',
|
||||
'openTOCDetails',
|
||||
'mapCenter'
|
||||
];
|
||||
|
||||
export function mergeInputWithSavedMap(input, savedMap) {
|
||||
const mergedInput = _.pick(input, MAP_EMBEDDABLE_INPUT_KEYS);
|
||||
|
||||
if (!_.has(input, 'isLayerTOCOpen') && savedMap.uiStateJSON) {
|
||||
const uiState = JSON.parse(savedMap.uiStateJSON);
|
||||
mergedInput.isLayerTOCOpen = _.get(uiState, 'isLayerTOCOpen', DEFAULT_IS_LAYER_TOC_OPEN);
|
||||
}
|
||||
|
||||
if (!_.has(input, 'openTOCDetails') && savedMap.uiStateJSON) {
|
||||
const uiState = JSON.parse(savedMap.uiStateJSON);
|
||||
if (_.has(uiState, 'openTOCDetails')) {
|
||||
mergedInput.openTOCDetails = _.get(uiState, 'openTOCDetails', []);
|
||||
}
|
||||
}
|
||||
|
||||
if (!input.mapCenter && savedMap.mapStateJSON) {
|
||||
const mapState = JSON.parse(savedMap.mapStateJSON);
|
||||
mergedInput.mapCenter = {
|
||||
lat: mapState.center.lat,
|
||||
lon: mapState.center.lon,
|
||||
zoom: mapState.zoom,
|
||||
};
|
||||
}
|
||||
|
||||
return mergedInput;
|
||||
}
|
|
@ -5501,7 +5501,6 @@
|
|||
"xpack.maps.mapController.saveMapButtonLabel": "保存",
|
||||
"xpack.maps.mapController.saveMapDescription": "マップを保存",
|
||||
"xpack.maps.mapController.saveSuccessMessage": "「{title}」が保存されました",
|
||||
"xpack.maps.mapEmbeddableFactory": "マップを読み込めません。奇形の保存されたオブジェクト",
|
||||
"xpack.maps.mapListing.advancedSettingsLinkText": "高度な設定",
|
||||
"xpack.maps.mapListing.cancelTitle": "キャンセル",
|
||||
"xpack.maps.mapListing.createMapButtonLabel": "マップを作成",
|
||||
|
|
|
@ -5644,7 +5644,6 @@
|
|||
"xpack.maps.mapController.saveMapButtonLabel": "保存",
|
||||
"xpack.maps.mapController.saveMapDescription": "保存地图",
|
||||
"xpack.maps.mapController.saveSuccessMessage": "已保存“{title}”",
|
||||
"xpack.maps.mapEmbeddableFactory": "无法加载地图,已保存对象格式错误",
|
||||
"xpack.maps.mapListing.advancedSettingsLinkText": "高级设置",
|
||||
"xpack.maps.mapListing.cancelTitle": "取消",
|
||||
"xpack.maps.mapListing.createMapButtonLabel": "创建地图",
|
||||
|
|
Loading…
Reference in a new issue