[Maps] Migrate maps client router to react (#65079)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Aaron Caldwell 2020-06-18 12:56:51 -06:00 committed by GitHub
parent b41ade3588
commit 55a73616ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 1459 additions and 1128 deletions

View file

@ -104,7 +104,7 @@ export function getMapsAppUrl(
return {
app: 'maps',
path: `#/map?${mapAppParams.toString()}`,
path: `/map#?${mapAppParams.toString()}`,
};
}

View file

@ -162,6 +162,27 @@ export async function FindProvider({ getService }: FtrProviderContext) {
return wrapAll(elements);
}
public async allByButtonText(
buttonText: string,
element: WebDriver | WebElement | WebElementWrapper = driver,
timeout: number = defaultFindTimeout
): Promise<string[]> {
log.debug(`Find.byButtonText('${buttonText}') with timeout=${timeout}`);
return await retry.tryForTime(timeout, async () => {
// tslint:disable-next-line:variable-name
const _element = element instanceof WebElementWrapper ? element._webElement : element;
await this._withTimeout(0);
const allButtons = wrapAll(await _element.findElements(By.tagName('button')));
await this._withTimeout(defaultFindTimeout);
const buttonTexts = await Promise.all(
allButtons.map(async (el) => {
return el.getVisibleText();
})
);
return buttonTexts.filter((text) => text.trim() === buttonText.trim());
});
}
public async allByCssSelector(
selector: string,
timeout: number = defaultFindTimeout

View file

@ -9,7 +9,6 @@ import { monitoring } from './legacy/plugins/monitoring';
import { security } from './legacy/plugins/security';
import { dashboardMode } from './legacy/plugins/dashboard_mode';
import { beats } from './legacy/plugins/beats_management';
import { maps } from './legacy/plugins/maps';
import { spaces } from './legacy/plugins/spaces';
import { ingestManager } from './legacy/plugins/ingest_manager';
@ -21,7 +20,6 @@ module.exports = function (kibana) {
security(kibana),
dashboardMode(kibana),
beats(kibana),
maps(kibana),
ingestManager(kibana),
];
};

View file

@ -1,41 +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 { i18n } from '@kbn/i18n';
import { resolve } from 'path';
import { getAppTitle } from '../../../plugins/maps/common/i18n_getters';
import { APP_ID, APP_ICON } from '../../../plugins/maps/common/constants';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
export function maps(kibana) {
return new kibana.Plugin({
require: ['kibana', 'elasticsearch'],
id: APP_ID,
configPrefix: 'xpack.maps',
publicDir: resolve(__dirname, 'public'),
config(Joi) {
return Joi.object({
enabled: Joi.boolean().default(true),
})
.unknown()
.default();
},
uiExports: {
app: {
title: getAppTitle(),
description: i18n.translate('xpack.maps.appDescription', {
defaultMessage: 'Map application',
}),
main: 'plugins/maps/legacy',
icon: 'plugins/maps/icon.svg',
euiIconType: APP_ICON,
category: DEFAULT_APP_CATEGORIES.kibana,
order: 4000,
},
styleSheetPaths: `${__dirname}/public/index.scss`,
},
});
}

View file

@ -1,686 +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 _ from 'lodash';
import rison from 'rison-node';
import 'ui/directives/listen';
import 'ui/directives/storage';
import React from 'react';
import { I18nProvider } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { render, unmountComponentAtNode } from 'react-dom';
import { uiModules } from 'ui/modules';
import {
getTimeFilter,
getIndexPatternService,
getInspector,
getNavigation,
getData,
getCoreI18n,
getCoreChrome,
getMapsCapabilities,
getToasts,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../plugins/maps/public/kibana_services';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { createMapStore } from '../../../../../plugins/maps/public/reducers/store';
import { Provider } from 'react-redux';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { GisMap } from '../../../../../plugins/maps/public/connected_components/gis_map';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { addHelpMenuToAppChrome } from '../../../../../plugins/maps/public/help_menu_util';
import {
setSelectedLayer,
setRefreshConfig,
setGotoWithCenter,
replaceLayerList,
setQuery,
setMapSettings,
enableFullScreen,
updateFlyout,
setReadOnly,
setIsLayerTOCOpen,
setOpenTOCDetails,
openMapSettings,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../plugins/maps/public/actions';
import {
DEFAULT_IS_LAYER_TOC_OPEN,
FLYOUT_STATE,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../plugins/maps/public/reducers/ui';
import {
getIsFullScreen,
getFlyoutDisplay,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../plugins/maps/public/selectors/ui_selectors';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { copyPersistentState } from '../../../../../plugins/maps/public/reducers/util';
import {
getQueryableUniqueIndexPatternIds,
hasDirtyState,
getLayerListRaw,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../plugins/maps/public/selectors/map_selectors';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getInitialLayers } from '../../../../../plugins/maps/public/angular/get_initial_layers';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getInitialQuery } from '../../../../../plugins/maps/public/angular/get_initial_query';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getInitialTimeFilters } from '../../../../../plugins/maps/public/angular/get_initial_time_filters';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getInitialRefreshConfig } from '../../../../../plugins/maps/public/angular/get_initial_refresh_config';
import { MAP_SAVED_OBJECT_TYPE, MAP_APP_PATH } from '../../../../../plugins/maps/common/constants';
import { npSetup, npStart } from 'ui/new_platform';
import { esFilters } from '../../../../../../src/plugins/data/public';
import {
SavedObjectSaveModal,
showSaveModal,
} from '../../../../../../src/plugins/saved_objects/public';
import { loadKbnTopNavDirectives } from '../../../../../../src/plugins/kibana_legacy/public';
import {
bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins,
bindStartCoreAndPlugins as bindNpStartCoreAndPlugins,
} from '../../../../../plugins/maps/public/plugin'; // eslint-disable-line @kbn/eslint/no-restricted-paths
const REACT_ANCHOR_DOM_ELEMENT_ID = 'react-maps-root';
const app = uiModules.get(MAP_APP_PATH, []);
// Init required services. Necessary while in legacy
const config = _.get(npSetup, 'plugins.maps.config', {});
const kibanaVersion = npSetup.core.injectedMetadata.getKibanaVersion();
bindNpSetupCoreAndPlugins(npSetup.core, npSetup.plugins, config, kibanaVersion);
bindNpStartCoreAndPlugins(npStart.core, npStart.plugins);
loadKbnTopNavDirectives(getNavigation().ui);
function getInitialLayersFromUrlParam() {
const locationSplit = window.location.href.split('?');
if (locationSplit.length <= 1) {
return [];
}
const mapAppParams = new URLSearchParams(locationSplit[1]);
if (!mapAppParams.has('initialLayers')) {
return [];
}
try {
return rison.decode_array(mapAppParams.get('initialLayers'));
} catch (e) {
getToasts().addWarning({
title: i18n.translate('xpack.maps.initialLayers.unableToParseTitle', {
defaultMessage: `Inital layers not added to map`,
}),
text: i18n.translate('xpack.maps.initialLayers.unableToParseMessage', {
defaultMessage: `Unable to parse contents of 'initialLayers' parameter. Error: {errorMsg}`,
values: { errorMsg: e.message },
}),
});
return [];
}
}
app.controller(
'GisMapController',
($scope, $route, kbnUrl, localStorage, AppState, globalState) => {
const savedQueryService = getData().query.savedQueries;
const { filterManager } = getData().query;
const savedMap = $route.current.locals.map;
$scope.screenTitle = savedMap.title;
let unsubscribe;
let initialLayerListConfig;
const $state = new AppState();
const store = createMapStore();
function getAppStateFilters() {
return _.get($state, 'filters', []);
}
const visibleSubscription = getCoreChrome()
.getIsVisible$()
.subscribe((isVisible) => {
$scope.$evalAsync(() => {
$scope.isVisible = isVisible;
});
});
$scope.$listen(globalState, 'fetch_with_changes', (diff) => {
if (diff.includes('time') || diff.includes('filters')) {
onQueryChange({
filters: [...globalState.filters, ...getAppStateFilters()],
time: globalState.time,
});
}
if (diff.includes('refreshInterval')) {
$scope.onRefreshChange({ isPaused: globalState.pause, refreshInterval: globalState.value });
}
});
$scope.$listen($state, 'fetch_with_changes', function (diff) {
if ((diff.includes('query') || diff.includes('filters')) && $state.query) {
onQueryChange({
filters: [...globalState.filters, ...getAppStateFilters()],
query: $state.query,
});
}
});
function syncAppAndGlobalState() {
$scope.$evalAsync(() => {
// appState
$state.query = $scope.query;
$state.filters = filterManager.getAppFilters();
$state.save();
// globalState
globalState.time = $scope.time;
globalState.refreshInterval = {
pause: $scope.refreshConfig.isPaused,
value: $scope.refreshConfig.interval,
};
globalState.filters = filterManager.getGlobalFilters();
globalState.save();
});
}
$scope.query = getInitialQuery({
mapStateJSON: savedMap.mapStateJSON,
appState: $state,
userQueryLanguage: localStorage.get('kibana.userQueryLanguage'),
});
$scope.time = getInitialTimeFilters({
mapStateJSON: savedMap.mapStateJSON,
globalState: globalState,
});
$scope.refreshConfig = getInitialRefreshConfig({
mapStateJSON: savedMap.mapStateJSON,
globalState: globalState,
});
/* Saved Queries */
$scope.showSaveQuery = getMapsCapabilities().saveQuery;
$scope.$watch(
() => getMapsCapabilities().saveQuery,
(newCapability) => {
$scope.showSaveQuery = newCapability;
}
);
$scope.onQuerySaved = (savedQuery) => {
$scope.savedQuery = savedQuery;
};
$scope.onSavedQueryUpdated = (savedQuery) => {
$scope.savedQuery = { ...savedQuery };
};
$scope.onClearSavedQuery = () => {
delete $scope.savedQuery;
delete $state.savedQuery;
onQueryChange({
filters: filterManager.getGlobalFilters(),
query: {
query: '',
language: localStorage.get('kibana.userQueryLanguage'),
},
});
};
function updateStateFromSavedQuery(savedQuery) {
const savedQueryFilters = savedQuery.attributes.filters || [];
const globalFilters = filterManager.getGlobalFilters();
const allFilters = [...savedQueryFilters, ...globalFilters];
if (savedQuery.attributes.timefilter) {
if (savedQuery.attributes.timefilter.refreshInterval) {
$scope.onRefreshChange({
isPaused: savedQuery.attributes.timefilter.refreshInterval.pause,
refreshInterval: savedQuery.attributes.timefilter.refreshInterval.value,
});
}
onQueryChange({
filters: allFilters,
query: savedQuery.attributes.query,
time: savedQuery.attributes.timefilter,
});
} else {
onQueryChange({
filters: allFilters,
query: savedQuery.attributes.query,
});
}
}
$scope.$watch('savedQuery', (newSavedQuery) => {
if (!newSavedQuery) return;
$state.savedQuery = newSavedQuery.id;
updateStateFromSavedQuery(newSavedQuery);
});
$scope.$watch(
() => $state.savedQuery,
(newSavedQueryId) => {
if (!newSavedQueryId) {
$scope.savedQuery = undefined;
return;
}
if ($scope.savedQuery && newSavedQueryId !== $scope.savedQuery.id) {
savedQueryService.getSavedQuery(newSavedQueryId).then((savedQuery) => {
$scope.$evalAsync(() => {
$scope.savedQuery = savedQuery;
updateStateFromSavedQuery(savedQuery);
});
});
}
}
);
/* End of Saved Queries */
async function onQueryChange({ filters, query, time, refresh }) {
if (filters) {
filterManager.setFilters(filters); // Maps and merges filters
$scope.filters = filterManager.getFilters();
}
if (query) {
$scope.query = query;
}
if (time) {
$scope.time = time;
}
syncAppAndGlobalState();
dispatchSetQuery(refresh);
}
function dispatchSetQuery(refresh) {
store.dispatch(
setQuery({
filters: $scope.filters,
query: $scope.query,
timeFilters: $scope.time,
refresh,
})
);
}
$scope.indexPatterns = [];
$scope.onQuerySubmit = function ({ dateRange, query }) {
onQueryChange({
query,
time: dateRange,
refresh: true,
});
};
$scope.updateFiltersAndDispatch = function (filters) {
onQueryChange({
filters,
});
};
$scope.onRefreshChange = function ({ isPaused, refreshInterval }) {
$scope.refreshConfig = {
isPaused,
interval: refreshInterval ? refreshInterval : $scope.refreshConfig.interval,
};
syncAppAndGlobalState();
store.dispatch(setRefreshConfig($scope.refreshConfig));
};
function addFilters(newFilters) {
newFilters.forEach((filter) => {
filter.$state = { store: esFilters.FilterStateStore.APP_STATE };
});
$scope.updateFiltersAndDispatch([...$scope.filters, ...newFilters]);
}
function hasUnsavedChanges() {
const state = store.getState();
const layerList = getLayerListRaw(state);
const layerListConfigOnly = copyPersistentState(layerList);
const savedLayerList = savedMap.getLayerList();
return !savedLayerList
? !_.isEqual(layerListConfigOnly, initialLayerListConfig)
: // savedMap stores layerList as a JSON string using JSON.stringify.
// JSON.stringify removes undefined properties from objects.
// savedMap.getLayerList converts the JSON string back into Javascript array of objects.
// Need to perform the same process for layerListConfigOnly to compare apples to apples
// and avoid undefined properties in layerListConfigOnly triggering unsaved changes.
!_.isEqual(JSON.parse(JSON.stringify(layerListConfigOnly)), savedLayerList);
}
function isOnMapNow() {
return window.location.hash.startsWith(`#/${MAP_SAVED_OBJECT_TYPE}`);
}
function beforeUnload(event) {
if (!isOnMapNow()) {
return;
}
const hasChanged = hasUnsavedChanges();
if (hasChanged) {
event.preventDefault();
event.returnValue = 'foobar'; //this is required for Chrome
}
}
window.addEventListener('beforeunload', beforeUnload);
async function renderMap() {
// clear old UI state
store.dispatch(setSelectedLayer(null));
store.dispatch(updateFlyout(FLYOUT_STATE.NONE));
store.dispatch(setReadOnly(!getMapsCapabilities().save));
handleStoreChanges(store);
unsubscribe = store.subscribe(() => {
handleStoreChanges(store);
});
// sync store with savedMap mapState
let savedObjectFilters = [];
if (savedMap.mapStateJSON) {
const mapState = JSON.parse(savedMap.mapStateJSON);
store.dispatch(
setGotoWithCenter({
lat: mapState.center.lat,
lon: mapState.center.lon,
zoom: mapState.zoom,
})
);
if (mapState.filters) {
savedObjectFilters = mapState.filters;
}
if (mapState.settings) {
store.dispatch(setMapSettings(mapState.settings));
}
}
if (savedMap.uiStateJSON) {
const uiState = JSON.parse(savedMap.uiStateJSON);
store.dispatch(
setIsLayerTOCOpen(_.get(uiState, 'isLayerTOCOpen', DEFAULT_IS_LAYER_TOC_OPEN))
);
store.dispatch(setOpenTOCDetails(_.get(uiState, 'openTOCDetails', [])));
}
const layerList = getInitialLayers(savedMap.layerListJSON, getInitialLayersFromUrlParam());
initialLayerListConfig = copyPersistentState(layerList);
store.dispatch(replaceLayerList(layerList));
store.dispatch(setRefreshConfig($scope.refreshConfig));
const initialFilters = [
..._.get(globalState, 'filters', []),
...getAppStateFilters(),
...savedObjectFilters,
];
await onQueryChange({ filters: initialFilters });
const root = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID);
render(
<Provider store={store}>
<I18nProvider>
<GisMap addFilters={addFilters} />
</I18nProvider>
</Provider>,
root
);
}
renderMap();
let prevIndexPatternIds;
async function updateIndexPatterns(nextIndexPatternIds) {
const indexPatterns = [];
const getIndexPatternPromises = nextIndexPatternIds.map(async (indexPatternId) => {
try {
const indexPattern = await getIndexPatternService().get(indexPatternId);
indexPatterns.push(indexPattern);
} catch (err) {
// unable to fetch index pattern
}
});
await Promise.all(getIndexPatternPromises);
// ignore outdated results
if (prevIndexPatternIds !== nextIndexPatternIds) {
return;
}
$scope.$evalAsync(() => {
$scope.indexPatterns = indexPatterns;
});
}
$scope.isFullScreen = false;
$scope.isSaveDisabled = false;
$scope.isOpenSettingsDisabled = false;
function handleStoreChanges(store) {
const nextIsFullScreen = getIsFullScreen(store.getState());
if (nextIsFullScreen !== $scope.isFullScreen) {
// Must trigger digest cycle for angular top nav to redraw itself when isFullScreen changes
$scope.$evalAsync(() => {
$scope.isFullScreen = nextIsFullScreen;
});
}
const nextIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState());
if (nextIndexPatternIds !== prevIndexPatternIds) {
prevIndexPatternIds = nextIndexPatternIds;
updateIndexPatterns(nextIndexPatternIds);
}
const nextIsSaveDisabled = hasDirtyState(store.getState());
if (nextIsSaveDisabled !== $scope.isSaveDisabled) {
$scope.$evalAsync(() => {
$scope.isSaveDisabled = nextIsSaveDisabled;
});
}
const flyoutDisplay = getFlyoutDisplay(store.getState());
const nextIsOpenSettingsDisabled = flyoutDisplay !== FLYOUT_STATE.NONE;
if (nextIsOpenSettingsDisabled !== $scope.isOpenSettingsDisabled) {
$scope.$evalAsync(() => {
$scope.isOpenSettingsDisabled = nextIsOpenSettingsDisabled;
});
}
}
$scope.$on('$destroy', () => {
window.removeEventListener('beforeunload', beforeUnload);
visibleSubscription.unsubscribe();
getCoreChrome().setIsVisible(true);
if (unsubscribe) {
unsubscribe();
}
const node = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID);
if (node) {
unmountComponentAtNode(node);
}
});
const updateBreadcrumbs = () => {
getCoreChrome().setBreadcrumbs([
{
text: i18n.translate('xpack.maps.mapController.mapsBreadcrumbLabel', {
defaultMessage: 'Maps',
}),
onClick: () => {
if (isOnMapNow() && hasUnsavedChanges()) {
const navigateAway = window.confirm(
i18n.translate('xpack.maps.mapController.unsavedChangesWarning', {
defaultMessage: `Your unsaved changes might not be saved`,
})
);
if (navigateAway) {
window.location.hash = '#';
}
} else {
window.location.hash = '#';
}
},
},
{ text: savedMap.title },
]);
};
updateBreadcrumbs();
addHelpMenuToAppChrome();
async function doSave(saveOptions) {
savedMap.syncWithStore(store.getState());
let id;
try {
id = await savedMap.save(saveOptions);
getCoreChrome().docTitle.change(savedMap.title);
} catch (err) {
getToasts().addDanger({
title: i18n.translate('xpack.maps.mapController.saveErrorMessage', {
defaultMessage: `Error on saving '{title}'`,
values: { title: savedMap.title },
}),
text: err.message,
'data-test-subj': 'saveMapError',
});
return { error: err };
}
if (id) {
getToasts().addSuccess({
title: i18n.translate('xpack.maps.mapController.saveSuccessMessage', {
defaultMessage: `Saved '{title}'`,
values: { title: savedMap.title },
}),
'data-test-subj': 'saveMapSuccess',
});
updateBreadcrumbs();
if (savedMap.id !== $route.current.params.id) {
$scope.$evalAsync(() => {
kbnUrl.change(`map/{{id}}`, { id: savedMap.id });
});
}
}
return { id };
}
// Hide angular timepicer/refresh UI from top nav
getTimeFilter().disableTimeRangeSelector();
getTimeFilter().disableAutoRefreshSelector();
$scope.showDatePicker = true; // used by query-bar directive to enable timepikcer in query bar
$scope.topNavMenu = [
{
id: 'full-screen',
label: i18n.translate('xpack.maps.mapController.fullScreenButtonLabel', {
defaultMessage: `full screen`,
}),
description: i18n.translate('xpack.maps.mapController.fullScreenDescription', {
defaultMessage: `full screen`,
}),
testId: 'mapsFullScreenMode',
run() {
getCoreChrome().setIsVisible(false);
store.dispatch(enableFullScreen());
},
},
{
id: 'inspect',
label: i18n.translate('xpack.maps.mapController.openInspectorButtonLabel', {
defaultMessage: `inspect`,
}),
description: i18n.translate('xpack.maps.mapController.openInspectorDescription', {
defaultMessage: `Open Inspector`,
}),
testId: 'openInspectorButton',
run() {
const inspectorAdapters = getInspectorAdapters(store.getState());
getInspector().open(inspectorAdapters, {});
},
},
{
id: 'mapSettings',
label: i18n.translate('xpack.maps.mapController.openSettingsButtonLabel', {
defaultMessage: `Map settings`,
}),
description: i18n.translate('xpack.maps.mapController.openSettingsDescription', {
defaultMessage: `Open map settings`,
}),
testId: 'openSettingsButton',
disableButton() {
return $scope.isOpenSettingsDisabled;
},
run() {
store.dispatch(openMapSettings());
},
},
...(getMapsCapabilities().save
? [
{
id: 'save',
label: i18n.translate('xpack.maps.mapController.saveMapButtonLabel', {
defaultMessage: `save`,
}),
description: i18n.translate('xpack.maps.mapController.saveMapDescription', {
defaultMessage: `Save map`,
}),
testId: 'mapSaveButton',
disableButton() {
return $scope.isSaveDisabled;
},
tooltip() {
if ($scope.isSaveDisabled) {
return i18n.translate('xpack.maps.mapController.saveMapDisabledButtonTooltip', {
defaultMessage: 'Save or Cancel your layer changes before saving',
});
}
},
run: async () => {
const onSave = ({
newTitle,
newCopyOnSave,
isTitleDuplicateConfirmed,
onTitleDuplicate,
}) => {
const currentTitle = savedMap.title;
savedMap.title = newTitle;
savedMap.copyOnSave = newCopyOnSave;
const saveOptions = {
confirmOverwrite: false,
isTitleDuplicateConfirmed,
onTitleDuplicate,
};
return doSave(saveOptions).then((response) => {
// If the save wasn't successful, put the original values back.
if (!response.id || response.error) {
savedMap.title = currentTitle;
}
return response;
});
};
const saveModal = (
<SavedObjectSaveModal
onSave={onSave}
onClose={() => {}}
title={savedMap.title}
showCopyOnSave={savedMap.id ? true : false}
objectType={MAP_SAVED_OBJECT_TYPE}
showDescription={false}
/>
);
showSaveModal(saveModal, getCoreI18n().Context);
},
},
]
: []),
];
}
);

View file

@ -1,3 +0,0 @@
/* GIS plugin styles */
@import '../../../../plugins/maps/public/index';

View file

@ -1,34 +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.
*/
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import '../../../../plugins/maps/public/kibana_services';
// import the uiExports that we want to "use"
import 'uiExports/inspectorViews';
import 'uiExports/search';
import 'uiExports/embeddableFactories';
import 'uiExports/embeddableActions';
import 'ui/autoload/all';
import 'react-vis/dist/style.css';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import '../../../../plugins/maps/public/angular/services/gis_map_saved_object_loader';
import './angular/map_controller';
import './routes';
// @ts-ignore
import { MapsPlugin } from './plugin';
export const plugin = () => {
return new MapsPlugin();
};
export {
RenderTooltipContentParams,
ITooltipProperty,
} from '../../../../plugins/maps/public/classes/tooltips/tooltip_property'; // eslint-disable-line @kbn/eslint/no-restricted-paths
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
export { MapEmbeddable, MapEmbeddableInput } from '../../../../plugins/maps/public/embeddable';

View file

@ -1,22 +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 { npSetup, npStart } from 'ui/new_platform';
// @ts-ignore Untyped Module
import { uiModules } from 'ui/modules';
import { plugin } from '.';
const pluginInstance = plugin();
const setupPlugins = {
__LEGACY: {
uiModules,
},
np: npSetup.plugins,
};
export const setup = pluginInstance.setup(npSetup.core, setupPlugins);
export const start = pluginInstance.start(npStart.core, npStart.plugins);

View file

@ -1,70 +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 _ from 'lodash';
import { Plugin, CoreStart, CoreSetup } from 'src/core/public';
// @ts-ignore
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
// @ts-ignore
import { wrapInI18nContext } from 'ui/i18n';
// @ts-ignore
import { MapListing } from '../../../../plugins/maps/public/components/map_listing'; // eslint-disable-line @kbn/eslint/no-restricted-paths
// @ts-ignore
import {
bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins,
bindStartCoreAndPlugins as bindNpStartCoreAndPlugins,
} from '../../../../plugins/maps/public/plugin'; // eslint-disable-line @kbn/eslint/no-restricted-paths
import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public';
import { LicensingPluginSetup } from '../../../../plugins/licensing/public';
import {
DataPublicPluginSetup,
DataPublicPluginStart,
} from '../../../../../src/plugins/data/public';
/**
* These are the interfaces with your public contracts. You should export these
* for other plugins to use in _their_ `SetupDeps`/`StartDeps` interfaces.
* @public
*/
export type MapsPluginSetup = ReturnType<MapsPlugin['setup']>;
export type MapsPluginStart = ReturnType<MapsPlugin['start']>;
interface MapsPluginSetupDependencies {
__LEGACY: any;
np: {
licensing?: LicensingPluginSetup;
home: HomePublicPluginSetup;
data: DataPublicPluginSetup;
};
}
interface MapsPluginStartDependencies {
data: DataPublicPluginStart;
inspector: InspectorStartContract;
// file_upload TODO: Export type from file upload and use here
}
/** @internal */
export class MapsPlugin implements Plugin<MapsPluginSetup, MapsPluginStart> {
public setup(core: CoreSetup, { __LEGACY: { uiModules }, np }: MapsPluginSetupDependencies) {
uiModules
.get('app/maps', ['ngRoute', 'react'])
.directive('mapListing', function (reactDirective: any) {
return reactDirective(wrapInI18nContext(MapListing));
});
// @ts-ignore
const config = _.get(np, 'maps.config', {});
// @ts-ignore
const kibanaVersion = core.injectedMetadata.getKibanaVersion();
// @ts-ignore
bindNpSetupCoreAndPlugins(core, np, config, kibanaVersion);
}
public start(core: CoreStart, plugins: MapsPluginStartDependencies) {
bindNpStartCoreAndPlugins(core, plugins);
}
}

View file

@ -1,108 +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 { i18n } from '@kbn/i18n';
import routes from 'ui/routes';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import listingTemplate from '../../../../plugins/maps/public/angular/listing_ng_wrapper.html';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import mapTemplate from '../../../../plugins/maps/public/angular/map.html';
import {
getSavedObjectsClient,
getCoreChrome,
getMapsCapabilities,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../plugins/maps/public/kibana_services';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getMapsSavedObjectLoader } from '../../../../plugins/maps/public/angular/services/gis_map_saved_object_loader';
import { LISTING_LIMIT_SETTING } from '../../../../../src/plugins/saved_objects/common';
routes.enable();
routes
.defaults(/.*/, {
badge: () => {
if (getMapsCapabilities().save) {
return undefined;
}
return {
text: i18n.translate('xpack.maps.badge.readOnly.text', {
defaultMessage: 'Read only',
}),
tooltip: i18n.translate('xpack.maps.badge.readOnly.tooltip', {
defaultMessage: 'Unable to save maps',
}),
iconType: 'glasses',
};
},
})
.when('/', {
template: listingTemplate,
controller($scope, config) {
const gisMapSavedObjectLoader = getMapsSavedObjectLoader();
$scope.listingLimit = config.get(LISTING_LIMIT_SETTING);
$scope.find = (search) => {
return gisMapSavedObjectLoader.find(search, $scope.listingLimit);
};
$scope.delete = (ids) => {
return gisMapSavedObjectLoader.delete(ids);
};
$scope.readOnly = !getMapsCapabilities().save;
},
resolve: {
hasMaps: function (kbnUrl) {
getSavedObjectsClient()
.find({ type: 'map', perPage: 1 })
.then((resp) => {
// Do not show empty listing page, just redirect to a new map
if (resp.savedObjects.length === 0) {
kbnUrl.redirect('/map');
}
return true;
});
},
},
})
.when('/map', {
template: mapTemplate,
controller: 'GisMapController',
resolve: {
map: function (redirectWhenMissing) {
const gisMapSavedObjectLoader = getMapsSavedObjectLoader();
return gisMapSavedObjectLoader.get().catch(
redirectWhenMissing({
map: '/',
})
);
},
},
})
.when('/map/:id', {
template: mapTemplate,
controller: 'GisMapController',
resolve: {
map: function (redirectWhenMissing, $route) {
const gisMapSavedObjectLoader = getMapsSavedObjectLoader();
const id = $route.current.params.id;
return gisMapSavedObjectLoader
.get(id)
.then((savedMap) => {
getCoreChrome().recentlyAccessed.add(savedMap.getFullPath(), savedMap.title, id);
getCoreChrome().docTitle.change(savedMap.title);
return savedMap;
})
.catch(
redirectWhenMissing({
map: '/',
})
);
},
},
})
.otherwise({
redirectTo: '/',
});

View file

@ -13,7 +13,8 @@ import {
EmbeddableExpression,
} from '../../expression_types';
import { getFunctionHelp } from '../../../i18n';
import { MapEmbeddableInput } from '../../../../../legacy/plugins/maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { MapEmbeddableInput } from '../../../../../plugins/maps/public/embeddable';
interface Arguments {
id: string;

View file

@ -5,7 +5,7 @@
*/
import { toExpression } from './map';
import { MapEmbeddableInput } from '../../../../../../legacy/plugins/maps/public';
import { MapEmbeddableInput } from '../../../../../../plugins/maps/public/embeddable';
import { fromExpression, Ast } from '@kbn/interpreter/common';
const baseSavedMapInput = {

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { MapEmbeddableInput } from '../../../../../../legacy/plugins/maps/public';
import { MapEmbeddableInput } from '../../../../../../plugins/maps/public/embeddable';
export function toExpression(input: MapEmbeddableInput): string {
const expressionParts = [] as string[];

View file

@ -32,7 +32,7 @@ export const GIS_API_PATH = `api/${APP_ID}`;
export const INDEX_SETTINGS_API_PATH = `${GIS_API_PATH}/indexSettings`;
export const FONTS_API_PATH = `${GIS_API_PATH}/fonts`;
export const MAP_BASE_URL = `/${MAP_APP_PATH}#/${MAP_SAVED_OBJECT_TYPE}`;
export const MAP_BASE_URL = `/${MAP_APP_PATH}/${MAP_SAVED_OBJECT_TYPE}`;
export function createMapPath(id: string) {
return `${MAP_BASE_URL}/${id}`;

View file

@ -1,6 +0,0 @@
<map-listing
find="find"
delete="delete"
listing-limit="listingLimit"
read-only="readOnly"
></map-listing>

View file

@ -1,35 +0,0 @@
<div id="maps-plugin" ng-class="{mapFullScreen: isFullScreen}">
<div id="maps-top-nav">
<div>
<kbn-top-nav
ng-show="isVisible"
app-name="'maps'"
config="topNavMenu"
show-search-bar="isVisible"
show-filter-bar="isVisible"
show-date-picker="showDatePicker"
show-save-query="showSaveQuery"
query="query"
saved-query="savedQuery"
on-query-submit="onQuerySubmit"
filters="filters"
on-filters-updated="updateFiltersAndDispatch"
index-patterns="indexPatterns"
date-range-from="time.from"
date-range-to="time.to"
is-refresh-paused="refreshConfig.isPaused"
refresh-interval="refreshConfig.interval"
on-refresh-change="onRefreshChange"
on-saved="onQuerySaved"
on-saved-query-updated="onSavedQueryUpdated"
on-clear-saved-query="onClearSavedQuery"
>
</kbn-top-nav>
</div>
</div>
<h1 class="euiScreenReaderOnly">{{screenTitle}}</h1>
<div id="react-maps-root"></div>
</div>

View file

@ -29,14 +29,13 @@ async function fetchIndexSettings(indexPatternTitle) {
const http = getHttp();
const toasts = getToasts();
try {
const indexSettings = await http.fetch(`../${INDEX_SETTINGS_API_PATH}`, {
return await http.fetch(`/${INDEX_SETTINGS_API_PATH}`, {
method: 'GET',
credentials: 'same-origin',
query: {
indexPatternTitle,
},
});
return indexSettings;
} catch (err) {
const warningMsg = i18n.translate('xpack.maps.indexSettings.fetchErrorMsg', {
defaultMessage: `Unable to fetch index settings for index pattern '{indexPatternTitle}'.

View file

@ -14,7 +14,6 @@ import { LayerPanel } from '../layer_panel';
import { AddLayerPanel } from '../add_layer_panel';
import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui';
import { ExitFullScreenButton } from '../../../../../../src/plugins/kibana_react/public';
import { getIndexPatternsFromIds } from '../../index_pattern_util';
import { ES_GEO_FIELD_TYPE } from '../../../common/constants';
import { indexPatterns as indexPatternsUtils } from '../../../../../../src/plugins/data/public';
@ -23,6 +22,7 @@ import uuid from 'uuid/v4';
import { FLYOUT_STATE } from '../../reducers/ui';
import { MapSettingsPanel } from '../map_settings_panel';
import { registerLayerWizards } from '../../classes/layers/load_layer_wizards';
import 'mapbox-gl/dist/mapbox-gl.css';
const RENDER_COMPLETE_EVENT = 'renderComplete';

View file

@ -8,7 +8,6 @@ import _ from 'lodash';
import React, { Suspense, lazy } from 'react';
import { Provider } from 'react-redux';
import { render, unmountComponentAtNode } from 'react-dom';
import 'mapbox-gl/dist/mapbox-gl.css';
import { Subscription } from 'rxjs';
import { Unsubscribe } from 'redux';
import { EuiLoadingSpinner } from '@elastic/eui';

View file

@ -17,5 +17,3 @@ export const plugin: PluginInitializer<MapsPluginSetup, MapsPluginStart> = (
};
export { MAP_SAVED_OBJECT_TYPE } from '../common/constants';
export { ITooltipProperty } from './classes/tooltips/tooltip_property';
export { MapsPluginStart } from './plugin';

View file

@ -7,6 +7,7 @@
import { AnyAction } from 'redux';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { IndexPatternsService } from 'src/plugins/data/public/index_patterns';
import { ReactElement } from 'react';
import { Embeddable, IContainer } from '../../../../../src/plugins/embeddable/public';
import { LayerDescriptor } from '../../common/descriptor_types';
import { MapStore, MapStoreState } from '../reducers/store';
@ -36,6 +37,7 @@ interface LazyLoadedMapModules {
initialLayers?: LayerDescriptor[]
) => LayerDescriptor[];
mergeInputWithSavedMap: any;
renderApp: (context: unknown, params: unknown) => ReactElement<any>;
createSecurityLayerDescriptors: (
indexPatternId: string,
indexPatternTitle: string
@ -49,7 +51,7 @@ export async function lazyLoadMapModules(): Promise<LazyLoadedMapModules> {
loadModulesPromise = new Promise(async (resolve) => {
const {
// @ts-ignore
// @ts-expect-error
getMapsSavedObjectLoader,
getQueryableUniqueIndexPatternIds,
MapEmbeddable,
@ -60,6 +62,8 @@ export async function lazyLoadMapModules(): Promise<LazyLoadedMapModules> {
addLayerWithoutDataSync,
getInitialLayers,
mergeInputWithSavedMap,
// @ts-expect-error
renderApp,
createSecurityLayerDescriptors,
} = await import('./lazy');
@ -74,6 +78,7 @@ export async function lazyLoadMapModules(): Promise<LazyLoadedMapModules> {
addLayerWithoutDataSync,
getInitialLayers,
mergeInputWithSavedMap,
renderApp,
createSecurityLayerDescriptors,
});
});

View file

@ -7,13 +7,15 @@
// These are map-dependencies of the embeddable.
// By lazy-loading these, the Maps-app can register the embeddable when the plugin mounts, without actually pulling all the code.
// @ts-ignore
export * from '../../angular/services/gis_map_saved_object_loader';
// @ts-expect-error
export * from '../../routing/bootstrap/services/gis_map_saved_object_loader';
export * from '../../embeddable/map_embeddable';
export * from '../../kibana_services';
export * from '../../reducers/store';
export * from '../../actions';
export * from '../../selectors/map_selectors';
export * from '../../angular/get_initial_layers';
export * from '../../routing/bootstrap/get_initial_layers';
export * from '../../embeddable/merge_input_with_saved_map';
// @ts-expect-error
export * from '../../routing/maps_router';
export * from '../../classes/layers/solution_layers/security';

View file

@ -28,7 +28,7 @@ The Maps app offers more functionality and is easier to use.`,
return {
aliasApp: APP_ID,
aliasPath: `#/${MAP_SAVED_OBJECT_TYPE}`,
aliasPath: `/${MAP_SAVED_OBJECT_TYPE}`,
name: APP_ID,
title: i18n.translate('xpack.maps.visTypeAlias.title', {
defaultMessage: 'Maps',

View file

@ -4,8 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public';
import { Setup as InspectorSetupContract } from 'src/plugins/inspector/public';
import {
CoreSetup,
CoreStart,
Plugin,
PluginInitializerContext,
DEFAULT_APP_CATEGORIES,
} from '../../../../src/core/public';
// @ts-ignore
import { MapView } from './inspector/views/map_view';
import {
@ -41,11 +47,13 @@ import { featureCatalogueEntry } from './feature_catalogue_entry';
import { getMapsVisTypeAlias } from './maps_vis_type_alias';
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public';
import { APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { APP_ICON, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory';
import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public';
import { MapsConfigType, MapsXPackConfig } from '../config';
import { MapsXPackConfig, MapsConfigType } from '../config';
import { getAppTitle } from '../common/i18n_getters';
import { ILicense } from '../../licensing/common/types';
import { lazyLoadMapModules } from './lazy_load_bundle';
import { MapsStartApi } from './api';
import { createSecurityLayerDescriptors } from './api/create_security_layer_descriptors';
@ -140,9 +148,22 @@ export class MapsPlugin
home.featureCatalogue.register(featureCatalogueEntry);
visualizations.registerAlias(getMapsVisTypeAlias());
embeddable.registerEmbeddableFactory(MAP_SAVED_OBJECT_TYPE, new MapEmbeddableFactory());
return {
config,
};
core.application.register({
id: APP_ID,
title: getAppTitle(),
order: 4000,
icon: `plugins/${APP_ID}/icon.svg`,
euiIconType: APP_ICON,
category: DEFAULT_APP_CATEGORIES.kibana,
// @ts-expect-error
async mount(context, params) {
const [coreStart, startPlugins] = await core.getStartServices();
bindStartCoreAndPlugins(coreStart, startPlugins);
const { renderApp } = await lazyLoadMapModules();
return renderApp(context, params);
},
});
}
public start(core: CoreStart, plugins: any): MapsStartApi {

View file

@ -9,6 +9,7 @@ import thunk from 'redux-thunk';
import { ui, DEFAULT_MAP_UI_STATE } from './ui';
import { map, DEFAULT_MAP_STATE } from './map'; // eslint-disable-line import/named
import { nonSerializableInstances } from './non_serializable_instances';
import { MAP_DESTROYED } from '../actions';
export const DEFAULT_MAP_STORE_STATE = {
ui: { ...DEFAULT_MAP_UI_STATE },
@ -17,11 +18,21 @@ export const DEFAULT_MAP_STORE_STATE = {
export function createMapStore() {
const enhancers = [applyMiddleware(thunk)];
const rootReducer = combineReducers({
const combinedReducers = combineReducers({
map,
ui,
nonSerializableInstances,
});
const rootReducer = (state, action) => {
// Reset store on map destroyed
if (action.type === MAP_DESTROYED) {
state = undefined;
}
return combinedReducers(state, action);
};
const storeConfig = {};
return createStore(rootReducer, storeConfig, compose(...enhancers));
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { LayerDescriptor } from '../../common/descriptor_types';
import { LayerDescriptor } from '../../../common/descriptor_types';
export function getInitialLayers(
layerListJSON?: string,

View file

@ -5,25 +5,24 @@
*/
import _ from 'lodash';
// Import each layer type, even those not used, to init in registry
import '../classes/sources/wms_source';
import '../classes/sources/ems_file_source';
import '../classes/sources/es_search_source';
import '../classes/sources/es_pew_pew_source';
import '../classes/sources/kibana_regionmap_source';
import '../classes/sources/es_geo_grid_source';
import '../classes/sources/xyz_tms_source';
import { KibanaTilemapSource } from '../classes/sources/kibana_tilemap_source';
import { TileLayer } from '../classes/layers/tile_layer/tile_layer';
import { EMSTMSSource } from '../classes/sources/ems_tms_source';
import { VectorTileLayer } from '../classes/layers/vector_tile_layer/vector_tile_layer';
import { getIsEmsEnabled } from '../kibana_services';
import { getKibanaTileMap } from '../meta';
import '../../classes/sources/wms_source';
import '../../classes/sources/ems_file_source';
import '../../classes/sources/es_search_source';
import '../../classes/sources/es_pew_pew_source';
import '../../classes/sources/kibana_regionmap_source';
import '../../classes/sources/es_geo_grid_source';
import '../../classes/sources/xyz_tms_source';
import { KibanaTilemapSource } from '../../classes/sources/kibana_tilemap_source';
import { TileLayer } from '../../classes/layers/tile_layer/tile_layer';
import { EMSTMSSource } from '../../classes/sources/ems_tms_source';
import { VectorTileLayer } from '../../classes/layers/vector_tile_layer/vector_tile_layer';
import { getIsEmsEnabled } from '../../kibana_services';
import { getKibanaTileMap } from '../../meta';
export function getInitialLayers(layerListJSON, initialLayers = []) {
if (layerListJSON) {
return JSON.parse(layerListJSON);
}
const tilemapSourceFromKibana = getKibanaTileMap();
if (_.get(tilemapSourceFromKibana, 'url')) {
const layerDescriptor = TileLayer.createDescriptor({

View file

@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
jest.mock('../meta', () => {
jest.mock('../../meta', () => {
return {};
});
jest.mock('../kibana_services');
jest.mock('../../kibana_services');
import { getInitialLayers } from './get_initial_layers';
@ -15,7 +15,7 @@ const layerListNotProvided = undefined;
describe('Saved object has layer list', () => {
beforeEach(() => {
require('../kibana_services').getIsEmsEnabled = () => true;
require('../../kibana_services').getIsEmsEnabled = () => true;
});
it('Should get initial layers from saved object', () => {
@ -32,7 +32,7 @@ describe('Saved object has layer list', () => {
describe('kibana.yml configured with map.tilemap.url', () => {
beforeAll(() => {
require('../meta').getKibanaTileMap = () => {
require('../../meta').getKibanaTileMap = () => {
return {
url: 'myTileUrl',
};
@ -62,11 +62,11 @@ describe('kibana.yml configured with map.tilemap.url', () => {
describe('EMS is enabled', () => {
beforeAll(() => {
require('../meta').getKibanaTileMap = () => {
require('../../meta').getKibanaTileMap = () => {
return null;
};
require('../kibana_services').getIsEmsEnabled = () => true;
require('../kibana_services').getEmsTileLayerId = () => ({
require('../../kibana_services').getIsEmsEnabled = () => true;
require('../../kibana_services').getEmsTileLayerId = () => ({
bright: 'road_map',
desaturated: 'road_map_desaturated',
dark: 'dark_map',
@ -98,10 +98,10 @@ describe('EMS is enabled', () => {
describe('EMS is not enabled', () => {
beforeAll(() => {
require('../meta').getKibanaTileMap = () => {
require('../../meta').getKibanaTileMap = () => {
return null;
};
require('../kibana_services').getIsEmsEnabled = () => false;
require('../../kibana_services').getIsEmsEnabled = () => false;
});
it('Should return empty layer list since there are no configured tile layers', () => {

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getUiSettings } from '../kibana_services';
import { UI_SETTINGS } from '../../../../../src/plugins/data/public';
import { getUiSettings } from '../../kibana_services';
import { UI_SETTINGS } from '../../../../../../src/plugins/data/public';
export function getInitialQuery({ mapStateJSON, appState = {}, userQueryLanguage }) {
const settings = getUiSettings();

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getUiSettings } from '../kibana_services';
import { UI_SETTINGS } from '../../../../../src/plugins/data/public';
import { getUiSettings } from '../../kibana_services';
import { UI_SETTINGS } from '../../../../../../src/plugins/data/public';
export function getInitialRefreshConfig({ mapStateJSON, globalState = {} }) {
const uiSettings = getUiSettings();

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getUiSettings } from '../kibana_services';
import { getUiSettings } from '../../kibana_services';
export function getInitialTimeFilters({ mapStateJSON, globalState = {} }) {
export function getInitialTimeFilters({ mapStateJSON, globalState }) {
if (mapStateJSON) {
const mapState = JSON.parse(mapStateJSON);
if (mapState.timeFilters) {

View file

@ -6,14 +6,14 @@
import _ from 'lodash';
import { createSavedGisMapClass } from './saved_gis_map';
import { SavedObjectLoader } from '../../../../../../src/plugins/saved_objects/public';
import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public';
import {
getCoreChrome,
getSavedObjectsClient,
getIndexPatternService,
getCoreOverlays,
getData,
} from '../../kibana_services';
} from '../../../kibana_services';
export const getMapsSavedObjectLoader = _.once(function () {
const services = {

View file

@ -5,7 +5,7 @@
*/
import _ from 'lodash';
import { createSavedObjectClass } from '../../../../../../src/plugins/saved_objects/public';
import { createSavedObjectClass } from '../../../../../../../src/plugins/saved_objects/public';
import {
getTimeFilters,
getMapZoom,
@ -15,11 +15,12 @@ import {
getQuery,
getFilters,
getMapSettings,
} from '../../selectors/map_selectors';
import { getIsLayerTOCOpen, getOpenTOCDetails } from '../../selectors/ui_selectors';
import { copyPersistentState } from '../../reducers/util';
import { extractReferences, injectReferences } from '../../../common/migrations/references';
import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants';
} from '../../../selectors/map_selectors';
import { getIsLayerTOCOpen, getOpenTOCDetails } from '../../../selectors/ui_selectors';
import { copyPersistentState } from '../../../reducers/util';
import { extractReferences, injectReferences } from '../../../../common/migrations/references';
import { MAP_BASE_URL, MAP_SAVED_OBJECT_TYPE } from '../../../../common/constants';
import { getStore } from '../../store_operations';
export function createSavedGisMapClass(services) {
const SavedObjectClass = createSavedObjectClass(services);
@ -73,14 +74,17 @@ export function createSavedGisMapClass(services) {
});
this.showInRecentlyAccessed = true;
}
getFullPath() {
return `/app/maps#map/${this.id}`;
return `${MAP_BASE_URL}/${this.id}`;
}
getLayerList() {
return this.layerListJSON ? JSON.parse(this.layerListJSON) : null;
}
syncWithStore(state) {
syncWithStore() {
const state = getStore().getState();
const layerList = getLayerListRaw(state);
const layerListConfigOnly = copyPersistentState(layerList);
this.layerListJSON = JSON.stringify(layerListConfigOnly);

View file

@ -0,0 +1,62 @@
/*
* 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 { render, unmountComponentAtNode } from 'react-dom';
import { Router, Switch, Route, Redirect } from 'react-router-dom';
import { getCoreI18n } from '../kibana_services';
import { createKbnUrlStateStorage } from '../../../../../src/plugins/kibana_utils/public';
import { getStore } from './store_operations';
import { Provider } from 'react-redux';
import { LoadListAndRender } from './routes/list/load_list_and_render';
import { LoadMapAndRender } from './routes/maps_app/load_map_and_render';
export let goToSpecifiedPath;
export let kbnUrlStateStorage;
export async function renderApp(context, { appBasePath, element, history }) {
goToSpecifiedPath = (path) => history.push(path);
kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: false, history });
render(<App history={history} appBasePath={appBasePath} />, element);
return () => {
unmountComponentAtNode(element);
};
}
const App = ({ history, appBasePath }) => {
const store = getStore();
const I18nContext = getCoreI18n().Context;
return (
<I18nContext>
<Provider store={store}>
<Router basename={appBasePath} history={history}>
<Switch>
<Route path={`/map/:savedMapId`} component={LoadMapAndRender} />
<Route exact path={`/map`} component={LoadMapAndRender} />
// Redirect other routes to list, or if hash-containing, their non-hash equivalents
<Route
path={``}
render={({ location: { pathname, hash } }) => {
if (hash) {
// Remove leading hash
const newPath = hash.substr(1);
return <Redirect to={newPath} />;
} else if (pathname === '/' || pathname === '') {
return <LoadListAndRender />;
} else {
return <Redirect to="/" />;
}
}}
/>
</Switch>
</Router>
</Provider>
</I18nContext>
);
};

View file

@ -0,0 +1,60 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { getCoreChrome } from '../../kibana_services';
import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants';
import _ from 'lodash';
import { getLayerListRaw } from '../../selectors/map_selectors';
import { copyPersistentState } from '../../reducers/util';
import { getStore } from '../store_operations';
import { goToSpecifiedPath } from '../maps_router';
function hasUnsavedChanges(savedMap, initialLayerListConfig) {
const state = getStore().getState();
const layerList = getLayerListRaw(state);
const layerListConfigOnly = copyPersistentState(layerList);
const savedLayerList = savedMap.getLayerList();
return !savedLayerList
? !_.isEqual(layerListConfigOnly, initialLayerListConfig)
: // savedMap stores layerList as a JSON string using JSON.stringify.
// JSON.stringify removes undefined properties from objects.
// savedMap.getLayerList converts the JSON string back into Javascript array of objects.
// Need to perform the same process for layerListConfigOnly to compare apples to apples
// and avoid undefined properties in layerListConfigOnly triggering unsaved changes.
!_.isEqual(JSON.parse(JSON.stringify(layerListConfigOnly)), savedLayerList);
}
export const updateBreadcrumbs = (savedMap, initialLayerListConfig, currentPath = '') => {
const isOnMapNow = currentPath.startsWith(`/${MAP_SAVED_OBJECT_TYPE}`);
const breadCrumbs = isOnMapNow
? [
{
text: i18n.translate('xpack.maps.mapController.mapsBreadcrumbLabel', {
defaultMessage: 'Maps',
}),
onClick: () => {
if (hasUnsavedChanges(savedMap, initialLayerListConfig)) {
const navigateAway = window.confirm(
i18n.translate('xpack.maps.breadCrumbs.unsavedChangesWarning', {
defaultMessage: `Your unsaved changes might not be saved`,
})
);
if (navigateAway) {
goToSpecifiedPath('/');
}
} else {
goToSpecifiedPath('/');
}
},
},
{ text: savedMap.title },
]
: [];
getCoreChrome().setBreadcrumbs(breadCrumbs);
};

View file

@ -0,0 +1,44 @@
/*
* 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 { connect } from 'react-redux';
import { MapsTopNavMenu } from './top_nav_menu';
import {
enableFullScreen,
openMapSettings,
removePreviewLayers,
setRefreshConfig,
setSelectedLayer,
updateFlyout,
} from '../../../actions';
import { FLYOUT_STATE } from '../../../reducers/ui';
import { getInspectorAdapters } from '../../../reducers/non_serializable_instances';
import { getFlyoutDisplay } from '../../../selectors/ui_selectors';
import { hasDirtyState } from '../../../selectors/map_selectors';
function mapStateToProps(state = {}) {
return {
isOpenSettingsDisabled: getFlyoutDisplay(state) !== FLYOUT_STATE.NONE,
inspectorAdapters: getInspectorAdapters(state),
isSaveDisabled: hasDirtyState(state),
};
}
function mapDispatchToProps(dispatch) {
return {
closeFlyout: () => {
dispatch(setSelectedLayer(null));
dispatch(updateFlyout(FLYOUT_STATE.NONE));
dispatch(removePreviewLayers());
},
setRefreshStoreConfig: (refreshConfig) => dispatch(setRefreshConfig(refreshConfig)),
enableFullScreen: () => dispatch(enableFullScreen()),
openMapSettings: () => dispatch(openMapSettings()),
};
}
const connectedMapsTopNavMenu = connect(mapStateToProps, mapDispatchToProps)(MapsTopNavMenu);
export { connectedMapsTopNavMenu as MapsTopNavMenu };

View file

@ -0,0 +1,279 @@
/*
* 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 { i18n } from '@kbn/i18n';
import {
getNavigation,
getCoreChrome,
getMapsCapabilities,
getInspector,
getToasts,
getCoreI18n,
getData,
getUiSettings,
} from '../../../kibana_services';
import {
SavedObjectSaveModal,
showSaveModal,
} from '../../../../../../../src/plugins/saved_objects/public';
import { MAP_SAVED_OBJECT_TYPE } from '../../../../common/constants';
import { updateBreadcrumbs } from '../breadcrumbs';
import { goToSpecifiedPath } from '../../maps_router';
export function MapsTopNavMenu({
savedMap,
query,
onQueryChange,
onQuerySaved,
onSavedQueryUpdated,
savedQuery,
time,
refreshConfig,
setRefreshConfig,
setRefreshStoreConfig,
initialLayerListConfig,
indexPatterns,
updateFiltersAndDispatch,
isSaveDisabled,
closeFlyout,
enableFullScreen,
openMapSettings,
inspectorAdapters,
syncAppAndGlobalState,
currentPath,
isOpenSettingsDisabled,
}) {
const { TopNavMenu } = getNavigation().ui;
const { filterManager } = getData().query;
const showSaveQuery = getMapsCapabilities().saveQuery;
const onClearSavedQuery = () => {
onQuerySaved(undefined);
onQueryChange({
filters: filterManager.getGlobalFilters(),
query: {
query: '',
language: getUiSettings().get('search:queryLanguage'),
},
});
};
// Nav settings
const config = getTopNavConfig(
savedMap,
initialLayerListConfig,
isOpenSettingsDisabled,
isSaveDisabled,
closeFlyout,
enableFullScreen,
openMapSettings,
inspectorAdapters,
currentPath
);
const submitQuery = function ({ dateRange, query }) {
onQueryChange({
query,
time: dateRange,
refresh: true,
});
};
const onRefreshChange = function ({ isPaused, refreshInterval }) {
const newRefreshConfig = {
isPaused,
interval: isNaN(refreshInterval) ? refreshConfig.interval : refreshInterval,
};
setRefreshConfig(newRefreshConfig, () => {
setRefreshStoreConfig(newRefreshConfig);
syncAppAndGlobalState();
});
};
return (
<TopNavMenu
appName="maps"
config={config}
indexPatterns={indexPatterns || []}
filters={filterManager.getFilters()}
query={query}
onQuerySubmit={submitQuery}
onFiltersUpdated={updateFiltersAndDispatch}
dateRangeFrom={time.from}
dateRangeTo={time.to}
isRefreshPaused={refreshConfig.isPaused}
refreshInterval={refreshConfig.interval}
onRefreshChange={onRefreshChange}
showSearchBar={true}
showFilterBar={true}
showDatePicker={true}
showSaveQuery={showSaveQuery}
savedQuery={savedQuery}
onSaved={onQuerySaved}
onSavedQueryUpdated={onSavedQueryUpdated}
onClearSavedQuery={onClearSavedQuery}
/>
);
}
function getTopNavConfig(
savedMap,
initialLayerListConfig,
isOpenSettingsDisabled,
isSaveDisabled,
closeFlyout,
enableFullScreen,
openMapSettings,
inspectorAdapters,
currentPath
) {
return [
{
id: 'full-screen',
label: i18n.translate('xpack.maps.mapController.fullScreenButtonLabel', {
defaultMessage: `full screen`,
}),
description: i18n.translate('xpack.maps.mapController.fullScreenDescription', {
defaultMessage: `full screen`,
}),
testId: 'mapsFullScreenMode',
run() {
getCoreChrome().setIsVisible(false);
enableFullScreen();
},
},
{
id: 'inspect',
label: i18n.translate('xpack.maps.mapController.openInspectorButtonLabel', {
defaultMessage: `inspect`,
}),
description: i18n.translate('xpack.maps.mapController.openInspectorDescription', {
defaultMessage: `Open Inspector`,
}),
testId: 'openInspectorButton',
run() {
getInspector().open(inspectorAdapters, {});
},
},
{
id: 'mapSettings',
label: i18n.translate('xpack.maps.mapController.openSettingsButtonLabel', {
defaultMessage: `Map settings`,
}),
description: i18n.translate('xpack.maps.mapController.openSettingsDescription', {
defaultMessage: `Open map settings`,
}),
testId: 'openSettingsButton',
disableButton() {
return isOpenSettingsDisabled;
},
run() {
openMapSettings();
},
},
...(getMapsCapabilities().save
? [
{
id: 'save',
label: i18n.translate('xpack.maps.mapController.saveMapButtonLabel', {
defaultMessage: `save`,
}),
description: i18n.translate('xpack.maps.mapController.saveMapDescription', {
defaultMessage: `Save map`,
}),
testId: 'mapSaveButton',
disableButton() {
return isSaveDisabled;
},
tooltip() {
if (isSaveDisabled) {
return i18n.translate('xpack.maps.mapController.saveMapDisabledButtonTooltip', {
defaultMessage: 'Save or Cancel your layer changes before saving',
});
}
},
run: async () => {
const onSave = ({
newTitle,
newCopyOnSave,
isTitleDuplicateConfirmed,
onTitleDuplicate,
}) => {
const currentTitle = savedMap.title;
savedMap.title = newTitle;
savedMap.copyOnSave = newCopyOnSave;
const saveOptions = {
confirmOverwrite: false,
isTitleDuplicateConfirmed,
onTitleDuplicate,
};
return doSave(
savedMap,
saveOptions,
initialLayerListConfig,
closeFlyout,
currentPath
).then((response) => {
// If the save wasn't successful, put the original values back.
if (!response.id || response.error) {
savedMap.title = currentTitle;
}
return response;
});
};
const saveModal = (
<SavedObjectSaveModal
onSave={onSave}
onClose={() => {}}
title={savedMap.title}
showCopyOnSave={!!savedMap.id}
objectType={MAP_SAVED_OBJECT_TYPE}
showDescription={false}
/>
);
showSaveModal(saveModal, getCoreI18n().Context);
},
},
]
: []),
];
}
async function doSave(savedMap, saveOptions, initialLayerListConfig, closeFlyout, currentPath) {
closeFlyout();
savedMap.syncWithStore();
let id;
try {
id = await savedMap.save(saveOptions);
getCoreChrome().docTitle.change(savedMap.title);
} catch (err) {
getToasts().addDanger({
title: i18n.translate('xpack.maps.mapController.saveErrorMessage', {
defaultMessage: `Error on saving '{title}'`,
values: { title: savedMap.title },
}),
text: err.message,
'data-test-subj': 'saveMapError',
});
return { error: err };
}
if (id) {
goToSpecifiedPath(`/map/${id}${window.location.hash}`);
updateBreadcrumbs(savedMap, initialLayerListConfig, currentPath);
getToasts().addSuccess({
title: i18n.translate('xpack.maps.mapController.saveSuccessMessage', {
defaultMessage: `Saved '{title}'`,
values: { title: savedMap.title },
}),
'data-test-subj': 'saveMapSuccess',
});
}
return { id };
}

View file

@ -0,0 +1,57 @@
/*
* 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 { getMapsSavedObjectLoader } from '../../bootstrap/services/gis_map_saved_object_loader';
import { getToasts } from '../../../kibana_services';
import { i18n } from '@kbn/i18n';
import { MapsListView } from './maps_list_view';
import { Redirect } from 'react-router-dom';
export class LoadListAndRender extends React.Component {
state = {
mapsLoaded: false,
hasSavedMaps: null,
};
componentDidMount() {
this._isMounted = true;
this._loadMapsList();
}
componentWillUnmount() {
this._isMounted = false;
}
async _loadMapsList() {
try {
const { hits = [] } = await getMapsSavedObjectLoader().find('', 1);
if (this._isMounted) {
this.setState({ mapsLoaded: true, hasSavedMaps: !!hits.length });
}
} catch (err) {
if (this._isMounted) {
this.setState({ mapsLoaded: true, hasSavedMaps: false });
getToasts().addDanger({
title: i18n.translate('xpack.maps.mapListing.errorAttemptingToLoadSavedMaps', {
defaultMessage: `Unable to load maps`,
}),
text: `${err}`,
});
}
}
}
render() {
const { mapsLoaded, hasSavedMaps } = this.state;
if (mapsLoaded) {
return hasSavedMaps ? <MapsListView /> : <Redirect to="/map" />;
} else {
return null;
}
}
}

View file

@ -5,10 +5,14 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { getToasts } from '../kibana_services';
import { getMapsSavedObjectLoader } from '../../bootstrap/services/gis_map_saved_object_loader';
import {
getMapsCapabilities,
getUiSettings,
getToasts,
getCoreChrome,
} from '../../../kibana_services';
import {
EuiTitle,
EuiFieldSearch,
@ -27,11 +31,14 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { addHelpMenuToAppChrome } from '../help_menu_util';
import { addHelpMenuToAppChrome } from '../../../help_menu_util';
import { Link } from 'react-router-dom';
import { updateBreadcrumbs } from '../../page_elements/breadcrumbs';
import { goToSpecifiedPath } from '../../maps_router';
export const EMPTY_FILTER = '';
export class MapListing extends React.Component {
export class MapsListView extends React.Component {
state = {
hasInitialFetchReturned: false,
isFetchingItems: false,
@ -42,10 +49,13 @@ export class MapListing extends React.Component {
selectedIds: [],
page: 0,
perPage: 20,
readOnly: !getMapsCapabilities().save,
listingLimit: getUiSettings().get('savedObjects:listingLimit'),
};
UNSAFE_componentWillMount() {
this._isMounted = true;
updateBreadcrumbs();
}
componentWillUnmount() {
@ -54,12 +64,21 @@ export class MapListing extends React.Component {
}
componentDidMount() {
this.fetchItems();
addHelpMenuToAppChrome();
this.initMapList();
}
async initMapList() {
this.fetchItems();
addHelpMenuToAppChrome();
getCoreChrome().docTitle.change('Maps');
}
_find = (search) => getMapsSavedObjectLoader().find(search, this.state.listingLimit);
_delete = (ids) => getMapsSavedObjectLoader().delete(ids);
debouncedFetch = _.debounce(async (filter) => {
const response = await this.props.find(filter);
const response = await this._find(filter);
if (!this._isMounted) {
return;
@ -73,7 +92,7 @@ export class MapListing extends React.Component {
isFetchingItems: false,
items: response.hits,
totalItems: response.total,
showLimitError: response.total > this.props.listingLimit,
showLimitError: response.total > this.state.listingLimit,
});
}
}, 300);
@ -89,7 +108,7 @@ export class MapListing extends React.Component {
deleteSelectedItems = async () => {
try {
await this.props.delete(this.state.selectedIds);
await this._delete(this.state.selectedIds);
} catch (error) {
getToasts().addDanger({
title: i18n.translate('xpack.maps.mapListing.unableToDeleteToastTitle', {
@ -211,11 +230,11 @@ export class MapListing extends React.Component {
<FormattedMessage
id="xpack.maps.mapListing.limitHelpDescription"
defaultMessage="You have {totalItems} items,
but your <strong>listingLimit</strong> setting prevents the table below from displaying more than {listingLimit}.
You can change this setting under "
but your <strong>listingLimit</strong> setting prevents the table below from displaying more than {listingLimit}.
You can change this setting under "
values={{
totalItems: this.state.totalItems,
listingLimit: this.props.listingLimit,
listingLimit: this.state.listingLimit,
}}
/>
<EuiLink href="#/management/kibana/settings">
@ -307,7 +326,10 @@ export class MapListing extends React.Component {
sortable: true,
render: (field, record) => (
<EuiLink
href={`#/map/${record.id}`}
onClick={(e) => {
e.preventDefault();
goToSpecifiedPath(`/map/${record.id}`);
}}
data-test-subj={`mapListingTitleLink-${record.title.split(' ').join('-')}`}
>
{field}
@ -331,7 +353,7 @@ export class MapListing extends React.Component {
};
let selection = false;
if (!this.props.readOnly) {
if (!this.state.readOnly) {
selection = {
onSelectionChange: (selection) => {
this.setState({
@ -369,14 +391,16 @@ export class MapListing extends React.Component {
renderListing() {
let createButton;
if (!this.props.readOnly) {
if (!this.state.readOnly) {
createButton = (
<EuiButton href={`#/map`} data-test-subj="newMapLink" fill>
<FormattedMessage
id="xpack.maps.mapListing.createMapButtonLabel"
defaultMessage="Create map"
/>
</EuiButton>
<Link to={'/map'}>
<EuiButton data-test-subj="newMapLink" fill>
<FormattedMessage
id="xpack.maps.mapListing.createMapButtonLabel"
defaultMessage="Create map"
/>
</EuiButton>
</Link>
);
}
return (
@ -427,10 +451,3 @@ export class MapListing extends React.Component {
);
}
}
MapListing.propTypes = {
readOnly: PropTypes.bool.isRequired,
find: PropTypes.func.isRequired,
delete: PropTypes.func.isRequired,
listingLimit: PropTypes.number.isRequired,
};

View file

@ -0,0 +1,67 @@
/*
* 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 { connect } from 'react-redux';
import { MapsAppView } from './maps_app_view';
import { getFlyoutDisplay, getIsFullScreen } from '../../../selectors/ui_selectors';
import {
getFilters,
getQueryableUniqueIndexPatternIds,
getRefreshConfig,
} from '../../../selectors/map_selectors';
import {
replaceLayerList,
setGotoWithCenter,
setIsLayerTOCOpen,
setMapSettings,
setOpenTOCDetails,
setQuery,
setReadOnly,
setRefreshConfig,
setSelectedLayer,
updateFlyout,
} from '../../../actions';
import { FLYOUT_STATE } from '../../../reducers/ui';
import { getMapsCapabilities } from '../../../kibana_services';
function mapStateToProps(state = {}) {
return {
isFullScreen: getIsFullScreen(state),
nextIndexPatternIds: getQueryableUniqueIndexPatternIds(state),
flyoutDisplay: getFlyoutDisplay(state),
refreshConfig: getRefreshConfig(state),
filters: getFilters(state),
};
}
function mapDispatchToProps(dispatch) {
return {
dispatchSetQuery: (refresh, filters, query, time) => {
dispatch(
setQuery({
filters,
query,
timeFilters: time,
refresh,
})
);
},
setRefreshConfig: (refreshConfig) => dispatch(setRefreshConfig(refreshConfig)),
replaceLayerList: (layerList) => dispatch(replaceLayerList(layerList)),
setGotoWithCenter: (latLonZoom) => dispatch(setGotoWithCenter(latLonZoom)),
setMapSettings: (mapSettings) => dispatch(setMapSettings(mapSettings)),
setIsLayerTOCOpen: (isLayerTOCOpen) => dispatch(setIsLayerTOCOpen(isLayerTOCOpen)),
setOpenTOCDetails: (openTOCDetails) => dispatch(setOpenTOCDetails(openTOCDetails)),
clearUi: () => {
dispatch(setSelectedLayer(null));
dispatch(updateFlyout(FLYOUT_STATE.NONE));
dispatch(setReadOnly(!getMapsCapabilities().save));
},
};
}
const connectedMapsAppView = connect(mapStateToProps, mapDispatchToProps)(MapsAppView);
export { connectedMapsAppView as MapsAppView };

View file

@ -0,0 +1,58 @@
/*
* 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 { MapsAppView } from '.';
import { getMapsSavedObjectLoader } from '../../bootstrap/services/gis_map_saved_object_loader';
import { getToasts } from '../../../kibana_services';
import { i18n } from '@kbn/i18n';
import { Redirect } from 'react-router-dom';
export const LoadMapAndRender = class extends React.Component {
state = {
savedMap: null,
failedToLoad: false,
};
componentDidMount() {
this._isMounted = true;
this._loadSavedMap();
}
componentWillUnmount() {
this._isMounted = false;
}
async _loadSavedMap() {
const { savedMapId } = this.props.match.params;
try {
const savedMap = await getMapsSavedObjectLoader().get(savedMapId);
if (this._isMounted) {
this.setState({ savedMap });
}
} catch (err) {
if (this._isMounted) {
this.setState({ failedToLoad: true });
getToasts().addWarning({
title: i18n.translate('xpack.maps.loadMap.errorAttemptingToLoadSavedMap', {
defaultMessage: `Unable to load map`,
}),
text: `${err.message}`,
});
}
}
}
render() {
const { savedMap, failedToLoad } = this.state;
if (failedToLoad) {
return <Redirect to="/" />;
}
const currentPath = this.props.match.url;
return savedMap ? <MapsAppView savedMap={savedMap} currentPath={currentPath} /> : null;
}
};

View file

@ -0,0 +1,476 @@
/*
* 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 { i18n } from '@kbn/i18n';
import 'mapbox-gl/dist/mapbox-gl.css';
import _ from 'lodash';
import { DEFAULT_IS_LAYER_TOC_OPEN } from '../../../reducers/ui';
import {
getIndexPatternService,
getToasts,
getData,
getUiSettings,
getCoreChrome,
} from '../../../kibana_services';
import { copyPersistentState } from '../../../reducers/util';
import { getInitialLayers } from '../../bootstrap/get_initial_layers';
import rison from 'rison-node';
import { getInitialTimeFilters } from '../../bootstrap/get_initial_time_filters';
import { getInitialRefreshConfig } from '../../bootstrap/get_initial_refresh_config';
import { getInitialQuery } from '../../bootstrap/get_initial_query';
import { MapsTopNavMenu } from '../../page_elements/top_nav_menu';
import {
getGlobalState,
updateGlobalState,
useGlobalStateSyncing,
} from '../../state_syncing/global_sync';
import { AppStateManager } from '../../state_syncing/app_state_manager';
import { useAppStateSyncing } from '../../state_syncing/app_sync';
import { updateBreadcrumbs } from '../../page_elements/breadcrumbs';
import { esFilters } from '../../../../../../../src/plugins/data/public';
import { GisMap } from '../../../connected_components/gis_map';
export class MapsAppView extends React.Component {
_visibleSubscription = null;
_globalSyncUnsubscribe = null;
_globalSyncChangeMonitorSubscription = null;
_appSyncUnsubscribe = null;
_appStateManager = new AppStateManager();
constructor(props) {
super(props);
this.state = {
indexPatterns: [],
prevIndexPatternIds: [],
initialized: false,
isVisible: true,
savedQuery: '',
currentPath: '',
initialLayerListConfig: null,
};
}
componentDidMount() {
const { savedMap, currentPath } = this.props;
this.setState({ currentPath });
getCoreChrome().docTitle.change(savedMap.title);
getCoreChrome().recentlyAccessed.add(savedMap.getFullPath(), savedMap.title, savedMap.id);
// Init sync utils
// eslint-disable-next-line react-hooks/rules-of-hooks
this._globalSyncUnsubscribe = useGlobalStateSyncing();
// eslint-disable-next-line react-hooks/rules-of-hooks
this._appSyncUnsubscribe = useAppStateSyncing(this._appStateManager);
this._globalSyncChangeMonitorSubscription = getData().query.state$.subscribe(
this._updateFromGlobalState
);
// Check app state in case of refresh
const initAppState = this._appStateManager.getAppState();
this._onQueryChange(initAppState);
if (initAppState.savedQuery) {
this._updateStateFromSavedQuery(initAppState.savedQuery);
}
// Monitor visibility
this._visibleSubscription = getCoreChrome()
.getIsVisible$()
.subscribe((isVisible) => this.setState({ isVisible }));
this._initMap();
}
_initBreadcrumbUpdater = () => {
const { initialLayerListConfig, currentPath } = this.state;
updateBreadcrumbs(this.props.savedMap, initialLayerListConfig, currentPath);
};
componentDidUpdate(prevProps, prevState) {
const { currentPath: prevCurrentPath } = prevState;
const { currentPath, initialLayerListConfig } = this.state;
const { savedMap } = this.props;
if (savedMap && initialLayerListConfig && currentPath !== prevCurrentPath) {
updateBreadcrumbs(savedMap, initialLayerListConfig, currentPath);
}
// TODO: Handle null when converting to TS
this._handleStoreChanges();
}
_updateFromGlobalState = ({ changes, state: globalState }) => {
if (!changes || !globalState) {
return;
}
const newState = {};
Object.keys(changes).forEach((key) => {
if (changes[key]) {
newState[key] = globalState[key];
}
});
this.setState(newState, () => {
this._appStateManager.setQueryAndFilters({
filters: getData().query.filterManager.getAppFilters(),
});
const { time, filters, refreshInterval } = globalState;
this.props.dispatchSetQuery(refreshInterval, filters, this.state.query, time);
});
};
componentWillUnmount() {
if (this._globalSyncUnsubscribe) {
this._globalSyncUnsubscribe();
}
if (this._appSyncUnsubscribe) {
this._appSyncUnsubscribe();
}
if (this._visibleSubscription) {
this._visibleSubscription.unsubscribe();
}
if (this._globalSyncChangeMonitorSubscription) {
this._globalSyncChangeMonitorSubscription.unsubscribe();
}
// Clean up app state filters
const { filterManager } = getData().query;
filterManager.filters.forEach((filter) => {
if (filter.$state.store === esFilters.FilterStateStore.APP_STATE) {
filterManager.removeFilter(filter);
}
});
}
_getInitialLayersFromUrlParam() {
const locationSplit = window.location.href.split('?');
if (locationSplit.length <= 1) {
return [];
}
const mapAppParams = new URLSearchParams(locationSplit[1]);
if (!mapAppParams.has('initialLayers')) {
return [];
}
try {
let mapInitLayers = mapAppParams.get('initialLayers');
if (mapInitLayers[mapInitLayers.length - 1] === '#') {
mapInitLayers = mapInitLayers.substr(0, mapInitLayers.length - 1);
}
return rison.decode_array(mapInitLayers);
} catch (e) {
getToasts().addWarning({
title: i18n.translate('xpack.maps.initialLayers.unableToParseTitle', {
defaultMessage: `Initial layers not added to map`,
}),
text: i18n.translate('xpack.maps.initialLayers.unableToParseMessage', {
defaultMessage: `Unable to parse contents of 'initialLayers' parameter. Error: {errorMsg}`,
values: { errorMsg: e.message },
}),
});
return [];
}
}
async _updateIndexPatterns(nextIndexPatternIds) {
const indexPatterns = [];
const getIndexPatternPromises = nextIndexPatternIds.map(async (indexPatternId) => {
try {
const indexPattern = await getIndexPatternService().get(indexPatternId);
indexPatterns.push(indexPattern);
} catch (err) {
// unable to fetch index pattern
}
});
await Promise.all(getIndexPatternPromises);
this.setState({
indexPatterns,
});
}
_handleStoreChanges = () => {
const { prevIndexPatternIds } = this.state;
const { nextIndexPatternIds } = this.props;
if (nextIndexPatternIds !== prevIndexPatternIds) {
this.setState({ prevIndexPatternIds: nextIndexPatternIds });
this._updateIndexPatterns(nextIndexPatternIds);
}
};
_getAppStateFilters = () => {
return this._appStateManager.getFilters() || [];
};
_syncAppAndGlobalState = () => {
const { query, time, initialized } = this.state;
const { refreshConfig } = this.props;
const { filterManager } = getData().query;
// appState
this._appStateManager.setQueryAndFilters({
query: query,
filters: filterManager.getAppFilters(),
});
// globalState
const refreshInterval = {
pause: refreshConfig.isPaused,
value: refreshConfig.interval,
};
updateGlobalState(
{
time: time,
refreshInterval,
filters: filterManager.getGlobalFilters(),
},
!initialized
);
this.setState({ refreshInterval });
};
_onQueryChange = async ({ filters, query, time, refresh }) => {
const { filterManager } = getData().query;
const { dispatchSetQuery } = this.props;
const newState = {};
let newFilters;
if (filters) {
filterManager.setFilters(filters); // Maps and merges filters
newFilters = filterManager.getFilters();
}
if (query) {
newState.query = query;
}
if (time) {
newState.time = time;
}
this.setState(newState, () => {
this._syncAppAndGlobalState();
dispatchSetQuery(
refresh,
newFilters || this.props.filters,
query || this.state.query,
time || this.state.time
);
});
};
_initQueryTimeRefresh() {
const { setRefreshConfig, savedMap } = this.props;
// TODO: Handle null when converting to TS
const globalState = getGlobalState();
const mapStateJSON = savedMap ? savedMap.mapStateJSON : undefined;
const newState = {
query: getInitialQuery({
mapStateJSON,
appState: this._appStateManager.getAppState(),
userQueryLanguage: getUiSettings().get('search:queryLanguage'),
}),
time: getInitialTimeFilters({
mapStateJSON,
globalState,
}),
refreshConfig: getInitialRefreshConfig({
mapStateJSON,
globalState,
}),
};
this.setState({ query: newState.query, time: newState.time });
updateGlobalState(
{
time: newState.time,
refreshInterval: {
value: newState.refreshConfig.interval,
pause: newState.refreshConfig.isPaused,
},
},
!this.state.initialized
);
setRefreshConfig(newState.refreshConfig);
}
_initMapAndLayerSettings() {
const { savedMap } = this.props;
// Get saved map & layer settings
this._initQueryTimeRefresh();
const layerList = getInitialLayers(
savedMap.layerListJSON,
this._getInitialLayersFromUrlParam()
);
this.props.replaceLayerList(layerList);
this.setState(
{
initialLayerListConfig: copyPersistentState(layerList),
savedMap,
},
this._initBreadcrumbUpdater
);
}
_updateFiltersAndDispatch = (filters) => {
this._onQueryChange({
filters,
});
};
_onRefreshChange = ({ isPaused, refreshInterval }) => {
const { refreshConfig } = this.props;
const newRefreshConfig = {
isPaused,
interval: isNaN(refreshInterval) ? refreshConfig.interval : refreshInterval,
};
this.setState({ refreshConfig: newRefreshConfig }, this._syncAppAndGlobalState);
this.props.setRefreshConfig(newRefreshConfig);
};
_updateStateFromSavedQuery(savedQuery) {
if (!savedQuery) {
this.setState({ savedQuery: '' });
return;
}
const { filterManager } = getData().query;
const savedQueryFilters = savedQuery.attributes.filters || [];
const globalFilters = filterManager.getGlobalFilters();
const allFilters = [...savedQueryFilters, ...globalFilters];
if (savedQuery.attributes.timefilter) {
if (savedQuery.attributes.timefilter.refreshInterval) {
this._onRefreshChange({
isPaused: savedQuery.attributes.timefilter.refreshInterval.pause,
refreshInterval: savedQuery.attributes.timefilter.refreshInterval.value,
});
}
this._onQueryChange({
filters: allFilters,
query: savedQuery.attributes.query,
time: savedQuery.attributes.timefilter,
});
} else {
this._onQueryChange({
filters: allFilters,
query: savedQuery.attributes.query,
});
}
}
_syncStoreAndGetFilters() {
const {
savedMap,
setGotoWithCenter,
setMapSettings,
setIsLayerTOCOpen,
setOpenTOCDetails,
} = this.props;
let savedObjectFilters = [];
if (savedMap.mapStateJSON) {
const mapState = JSON.parse(savedMap.mapStateJSON);
setGotoWithCenter({
lat: mapState.center.lat,
lon: mapState.center.lon,
zoom: mapState.zoom,
});
if (mapState.filters) {
savedObjectFilters = mapState.filters;
}
if (mapState.settings) {
setMapSettings(mapState.settings);
}
}
if (savedMap.uiStateJSON) {
const uiState = JSON.parse(savedMap.uiStateJSON);
setIsLayerTOCOpen(_.get(uiState, 'isLayerTOCOpen', DEFAULT_IS_LAYER_TOC_OPEN));
setOpenTOCDetails(_.get(uiState, 'openTOCDetails', []));
}
return savedObjectFilters;
}
async _initMap() {
const { clearUi, savedMap } = this.props;
// TODO: Handle null when converting to TS
const globalState = getGlobalState();
this._initMapAndLayerSettings();
clearUi();
const savedObjectFilters = this._syncStoreAndGetFilters(savedMap);
await this._onQueryChange({
filters: [
..._.get(globalState, 'filters', []),
...this._getAppStateFilters(),
...savedObjectFilters,
],
});
this.setState({ initialized: true });
}
_renderTopNav() {
const {
query,
time,
savedQuery,
initialLayerListConfig,
isVisible,
indexPatterns,
currentPath,
} = this.state;
const { savedMap, refreshConfig } = this.props;
return isVisible ? (
<MapsTopNavMenu
savedMap={savedMap}
query={query}
savedQuery={savedQuery}
onQueryChange={this._onQueryChange}
time={time}
refreshConfig={refreshConfig}
setRefreshConfig={(newConfig, callback) => {
this.setState(
{
refreshConfig: newConfig,
},
callback
);
}}
initialLayerListConfig={initialLayerListConfig}
indexPatterns={indexPatterns}
updateFiltersAndDispatch={this._updateFiltersAndDispatch}
onQuerySaved={(query) => {
this.setState({ savedQuery: query });
this._appStateManager.setQueryAndFilters({ savedQuery: query });
this._updateStateFromSavedQuery(query);
}}
onSavedQueryUpdated={(query) => {
this.setState({ savedQuery: { ...query } });
this._appStateManager.setQueryAndFilters({ savedQuery: query });
this._updateStateFromSavedQuery(query);
}}
syncAppAndGlobalState={this._syncAppAndGlobalState}
currentPath={currentPath}
/>
) : null;
}
render() {
const { filters, isFullScreen } = this.props;
return this.state.initialized ? (
<div id="maps-plugin" className={isFullScreen ? 'mapFullScreen' : ''}>
{this._renderTopNav()}
<h1 className="euiScreenReaderOnly">{`screenTitle placeholder`}</h1>
<div id="react-maps-root">
<GisMap
addFilters={(newFilters) => {
newFilters.forEach((filter) => {
filter.$state = { store: esFilters.FilterStateStore.APP_STATE };
});
this._updateFiltersAndDispatch([...filters, ...newFilters]);
}}
/>
</div>
</div>
) : null;
}
}

View file

@ -0,0 +1,44 @@
/*
* 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 { Subject } from 'rxjs';
export class AppStateManager {
_query = '';
_savedQuery = '';
_filters = [];
_updated$ = new Subject();
setQueryAndFilters({ query, savedQuery, filters }) {
if (this._query !== query) {
this._query = query;
}
if (this._savedQuery !== savedQuery) {
this._savedQuery = savedQuery;
}
if (this._filters !== filters) {
this._filters = filters;
}
this._updated$.next();
}
getQuery() {
return this._query;
}
getFilters() {
return this._filters;
}
getAppState() {
return {
query: this._query,
savedQuery: this._savedQuery,
filters: this._filters,
};
}
}

View file

@ -0,0 +1,61 @@
/*
* 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 { connectToQueryState, esFilters } from '../../../../../../src/plugins/data/public';
import { syncState } from '../../../../../../src/plugins/kibana_utils/public';
import { map } from 'rxjs/operators';
import { getData } from '../../kibana_services';
import { kbnUrlStateStorage } from '../maps_router';
export function useAppStateSyncing(appStateManager) {
// get appStateContainer
// sync app filters with app state container from data.query to state container
const { query } = getData();
const stateContainer = {
get: () => ({
query: appStateManager.getQuery(),
filters: appStateManager.getFilters(),
}),
set: (state) =>
state && appStateManager.setQueryAndFilters({ query: state.query, filters: state.filters }),
state$: appStateManager._updated$.pipe(
map(() => ({
query: appStateManager.getQuery(),
filters: appStateManager.getFilters(),
}))
),
};
const stopSyncingQueryAppStateWithStateContainer = connectToQueryState(query, stateContainer, {
filters: esFilters.FilterStateStore.APP_STATE,
});
// sets up syncing app state container with url
const { start: startSyncingAppStateWithUrl, stop: stopSyncingAppStateWithUrl } = syncState({
storageKey: '_a',
stateStorage: kbnUrlStateStorage,
stateContainer,
});
// merge initial state from app state container and current state in url
const initialAppState = {
...stateContainer.get(),
...kbnUrlStateStorage.get('_a'),
};
// trigger state update. actually needed in case some data was in url
stateContainer.set(initialAppState);
// set current url to whatever is in app state container
kbnUrlStateStorage.set('_a', initialAppState);
// finally start syncing state containers with url
startSyncingAppStateWithUrl();
return () => {
stopSyncingQueryAppStateWithStateContainer();
stopSyncingAppStateWithUrl();
};
}

View file

@ -0,0 +1,32 @@
/*
* 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 { syncQueryStateWithUrl } from '../../../../../../src/plugins/data/public';
import { getData } from '../../kibana_services';
// @ts-ignore
import { kbnUrlStateStorage } from '../maps_router';
export function useGlobalStateSyncing() {
const { stop } = syncQueryStateWithUrl(getData().query, kbnUrlStateStorage);
return stop;
}
export function getGlobalState() {
return kbnUrlStateStorage.get('_g');
}
export function updateGlobalState(newState: unknown, flushUrlState = false) {
const globalState = getGlobalState();
kbnUrlStateStorage.set('_g', {
// @ts-ignore
...globalState,
// @ts-ignore
...newState,
});
if (flushUrlState) {
kbnUrlStateStorage.flush({ replace: true });
}
}

View file

@ -0,0 +1,11 @@
/*
* 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 { createMapStore } from '../reducers/store';
const store = createMapStore();
export const getStore = () => store;

View file

@ -6,6 +6,7 @@
import { i18n } from '@kbn/i18n';
import { TutorialsCategory } from '../../../../../../src/plugins/home/server';
import { MAP_BASE_URL } from '../../../common/constants';
export function emsBoundariesSpecProvider({
emsLandingPageUrl,
@ -63,7 +64,7 @@ Indexing EMS administrative boundaries in Elasticsearch allows for search on bou
2. Click `Add layer`, then select `Upload GeoJSON`.\n\
3. Upload the GeoJSON file and click `Import file`.',
values: {
newMapUrl: prependBasePath('/app/maps#/map'),
newMapUrl: prependBasePath(MAP_BASE_URL),
},
}),
},

View file

@ -22,7 +22,8 @@ import { IndexPatternsMissingPrompt } from './index_patterns_missing_prompt';
import { MapToolTip } from './map_tool_tip/map_tool_tip';
import * as i18n from './translations';
import { SetQuery } from './types';
import { MapEmbeddable } from '../../../../../../legacy/plugins/maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { MapEmbeddable } from '../../../../../../plugins/maps/public/embeddable';
import { Query, Filter } from '../../../../../../../src/plugins/data/public';
import { useKibana, useUiSetting$ } from '../../../common/lib/kibana';

View file

@ -13,9 +13,13 @@ import { getLayerList } from './map_config';
import { MAP_SAVED_OBJECT_TYPE } from '../../../../../maps/public';
import {
MapEmbeddable,
RenderTooltipContentParams,
MapEmbeddableInput,
} from '../../../../../../legacy/plugins/maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../../plugins/maps/public/embeddable';
import {
RenderTooltipContentParams,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../../plugins/maps/public/classes/tooltips/tooltip_property';
import * as i18n from './translations';
import { Query, Filter } from '../../../../../../../src/plugins/data/public';
import {

View file

@ -13,8 +13,10 @@ import {
SUM_OF_SERVER_BYTES,
SUM_OF_SOURCE_BYTES,
} from '../map_config';
import { ITooltipProperty } from '../../../../../../maps/public';
import { TooltipProperty } from '../../../../../../maps/public/classes/tooltips/tooltip_property';
import {
ITooltipProperty,
TooltipProperty,
} from '../../../../../../maps/public/classes/tooltips/tooltip_property';
describe('LineToolTipContent', () => {
const mockFeatureProps: ITooltipProperty[] = [

View file

@ -16,7 +16,8 @@ import {
} from '../map_config';
import * as i18n from '../translations';
import { ITooltipProperty } from '../../../../../../maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { ITooltipProperty } from '../../../../../../maps/public/classes/tooltips/tooltip_property';
const FlowBadge = (styled(EuiBadge)`
height: 45px;

View file

@ -17,7 +17,8 @@ import { LineToolTipContent } from './line_tool_tip_content';
import { PointToolTipContent } from './point_tool_tip_content';
import { Loader } from '../../../../common/components/loader';
import * as i18n from '../translations';
import { ITooltipProperty } from '../../../../../../maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { ITooltipProperty } from '../../../../../../maps/public/classes/tooltips/tooltip_property';
export const MapToolTipComponent = ({
closeTooltip,

View file

@ -11,8 +11,10 @@ import { TestProviders } from '../../../../common/mock';
import { getEmptyStringTag } from '../../../../common/components/empty_value';
import { HostDetailsLink, IPDetailsLink } from '../../../../common/components/links';
import { FlowTarget } from '../../../../graphql/types';
import { ITooltipProperty } from '../../../../../../maps/public';
import { TooltipProperty } from '../../../../../../maps/public/classes/tooltips/tooltip_property';
import {
TooltipProperty,
ITooltipProperty,
} from '../../../../../../maps/public/classes/tooltips/tooltip_property';
describe('PointToolTipContent', () => {
const mockFeatureProps: ITooltipProperty[] = [

View file

@ -14,7 +14,8 @@ import { DescriptionListStyled } from '../../../../common/components/page';
import { HostDetailsLink, IPDetailsLink } from '../../../../common/components/links';
import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/field_renderers';
import { FlowTarget } from '../../../../graphql/types';
import { ITooltipProperty } from '../../../../../../maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { ITooltipProperty } from '../../../../../../maps/public/classes/tooltips/tooltip_property';
interface PointToolTipContentProps {
contextId: string;

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RenderTooltipContentParams } from '../../../../../../legacy/plugins/maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { RenderTooltipContentParams } from '../../../../../maps/public/classes/tooltips/tooltip_property';
import { inputsModel } from '../../../common/store/inputs';
export interface IndexPatternMapping {

View file

@ -8971,10 +8971,7 @@
"xpack.maps.addLayerPanel.footer.cancelButtonLabel": "キャンセル",
"xpack.maps.addLayerPanel.importFile": "ファイルのインポート",
"xpack.maps.aggs.defaultCountLabel": "カウント",
"xpack.maps.appDescription": "マップアプリケーション",
"xpack.maps.appTitle": "マップ",
"xpack.maps.badge.readOnly.text": "読み込み専用",
"xpack.maps.badge.readOnly.tooltip": "マップを保存できませんで",
"xpack.maps.blendedVectorLayer.clusteredLayerName": "クラスター化 {displayName}",
"xpack.maps.common.esSpatialRelation.containsLabel": "contains",
"xpack.maps.common.esSpatialRelation.disjointLabel": "disjoint",
@ -9105,7 +9102,6 @@
"xpack.maps.mapController.saveMapDescription": "マップを保存",
"xpack.maps.mapController.saveMapDisabledButtonTooltip": "保存する前に、レイヤーの変更を保存するか、キャンセルしてください",
"xpack.maps.mapController.saveSuccessMessage": "「{title}」が保存されました",
"xpack.maps.mapController.unsavedChangesWarning": "保存されていない変更は保存されない可能性があります",
"xpack.maps.mapEmbeddableFactory.invalidLayerList": "不正な形式のレイヤーリストによりマップを読み込めません",
"xpack.maps.mapEmbeddableFactory.invalidSavedObject": "不正な形式の保存済みオブジェクトによりマップを読み込めません",
"xpack.maps.mapListing.advancedSettingsLinkText": "高度な設定",

View file

@ -8975,10 +8975,7 @@
"xpack.maps.addLayerPanel.footer.cancelButtonLabel": "鍙栨秷",
"xpack.maps.addLayerPanel.importFile": "导入文件",
"xpack.maps.aggs.defaultCountLabel": "计数",
"xpack.maps.appDescription": "地图应用程序",
"xpack.maps.appTitle": "Maps",
"xpack.maps.badge.readOnly.text": "只读",
"xpack.maps.badge.readOnly.tooltip": "无法保存地图",
"xpack.maps.blendedVectorLayer.clusteredLayerName": "集群 {displayName}",
"xpack.maps.common.esSpatialRelation.containsLabel": "contains",
"xpack.maps.common.esSpatialRelation.disjointLabel": "disjoint",
@ -9109,7 +9106,6 @@
"xpack.maps.mapController.saveMapDescription": "保存地图",
"xpack.maps.mapController.saveMapDisabledButtonTooltip": "保存或在保存之前取消您的图层更改",
"xpack.maps.mapController.saveSuccessMessage": "已保存“{title}”",
"xpack.maps.mapController.unsavedChangesWarning": "可能不会保存您未保存的更改",
"xpack.maps.mapEmbeddableFactory.invalidLayerList": "无法加载地图,图层列表格式不正确",
"xpack.maps.mapEmbeddableFactory.invalidSavedObject": "无法加载地图,已保存对象格式错误",
"xpack.maps.mapListing.advancedSettingsLinkText": "高级设置",

View file

@ -11,7 +11,8 @@ import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal';
import {
MapEmbeddable,
MapEmbeddableInput,
} from '../../../../../../../../legacy/plugins/maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../../../maps/public/embeddable';
import * as i18n from './translations';
import { GeoPoint } from '../../../../../../common/runtime_types';
import { getLayerList } from './map_config';
@ -23,7 +24,8 @@ import {
} from '../../../../../../../../../src/plugins/embeddable/public';
import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../../maps/public';
import { MapToolTipComponent } from './map_tool_tip';
import { RenderTooltipContentParams } from '../../../../../../../../legacy/plugins/maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { RenderTooltipContentParams } from '../../../../../../../maps/public/classes/tooltips/tooltip_property';
export interface EmbeddedMapProps {
upPoints: LocationPoint[];

View file

@ -21,7 +21,8 @@ import { AppState } from '../../../../../state';
import { monitorLocationsSelector } from '../../../../../state/selectors';
import { useMonitorId } from '../../../../../hooks';
import { MonitorLocation } from '../../../../../../common/runtime_types/monitor';
import { RenderTooltipContentParams } from '../../../../../../../../legacy/plugins/maps/public';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { RenderTooltipContentParams } from '../../../../../../../maps/public/classes/tooltips/tooltip_property';
import { formatAvailabilityValue } from '../../availability_reporting/availability_reporting';
import { LastCheckLabel } from '../../translations';

View file

@ -77,9 +77,9 @@ export default function ({ getPageObjects, getService }) {
it('should override query stored with map when query is provided in app state', async () => {
const currentUrl = await browser.getCurrentUrl();
const kibanaBaseUrl = currentUrl.substring(0, currentUrl.indexOf('#'));
const kibanaBaseUrl = currentUrl.substring(0, currentUrl.indexOf('/maps/'));
const appState = `_a=(query:(language:kuery,query:'machine.os.raw%20:%20"win%208"'))`;
const urlWithQueryInAppState = `${kibanaBaseUrl}#/map/8eabdab0-144f-11e9-809f-ad25bb78262c?${appState}`;
const urlWithQueryInAppState = `${kibanaBaseUrl}/maps/map/8eabdab0-144f-11e9-809f-ad25bb78262c#?${appState}`;
await browser.get(urlWithQueryInAppState, true);
await PageObjects.maps.waitForLayersToLoad();

View file

@ -5,6 +5,7 @@
*/
import _ from 'lodash';
import { APP_ID } from '../../../plugins/maps/common/constants';
export function GisPageProvider({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['common', 'header', 'timePicker']);
@ -159,7 +160,7 @@ export function GisPageProvider({ getService, getPageObjects }) {
async onMapListingPage() {
log.debug(`onMapListingPage`);
const exists = await testSubjects.exists('mapsListingPage');
const exists = await testSubjects.exists('mapsListingPage', { timeout: 3500 });
return exists;
}
@ -197,7 +198,7 @@ export function GisPageProvider({ getService, getPageObjects }) {
const onPage = await this.onMapListingPage();
if (!onPage) {
await retry.try(async () => {
await PageObjects.common.navigateToUrl('maps', '/', { basePath: this.basePath });
await PageObjects.common.navigateToUrlWithBrowserHistory(APP_ID, '/');
const onMapListingPage = await this.onMapListingPage();
if (!onMapListingPage) throw new Error('Not on map listing page.');
});
@ -209,8 +210,8 @@ export function GisPageProvider({ getService, getPageObjects }) {
log.debug(`getMapCountWithName: ${name}`);
await this.searchForMapWithName(name);
const links = await find.allByLinkText(name);
return links.length;
const buttons = await find.allByButtonText(name);
return buttons.length;
}
async isSetViewPopoverOpen() {