mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[Visualize] Shim with local application service (#49891)
* Add dashboard updates * Use I18nProvider instead of I18nContext * remove unused dependencies * Centralizing and cleaning up legacy imports * Fix merge conflict * fix merge bugs and rename main dynamic entrypoint * Rename app to legacy_app * Clear deps * fix jest tests * fix saved object finder bug * Fix unit tests * Ignore TS * revert using stateless component for this PR * fix types * Fix merge conflicts * Update deps * Revert filter bar export * Revert ts-ignore * Clean up * Refactoring * Fix test * Remove global_state_sync * Refactoring * Remove uiExports/embeddableFactories * Trigger digest cycle in local angular when vis is changed. * Fix TS * Revert back syncOnMount * Add missed import * Revert import 'uiExports/embeddableFactories' * Update app navigation func test * Update app navigation func test * Update app navigation func test * Remove 'kibana-install-dir' arg in pluginFunctionalTestsRelease * Fix review comments * Fix code review comments * Rename alias * Fix indexPatterns * Use IndexPatternsContract interface
This commit is contained in:
parent
23edb41739
commit
33989b0805
38 changed files with 1113 additions and 474 deletions
|
@ -62,6 +62,7 @@ export default function (kibana) {
|
||||||
hacks: [
|
hacks: [
|
||||||
'plugins/kibana/discover',
|
'plugins/kibana/discover',
|
||||||
'plugins/kibana/dev_tools',
|
'plugins/kibana/dev_tools',
|
||||||
|
'plugins/kibana/visualize',
|
||||||
],
|
],
|
||||||
savedObjectTypes: [
|
savedObjectTypes: [
|
||||||
'plugins/kibana/visualize/saved_visualizations/saved_visualization_register',
|
'plugins/kibana/visualize/saved_visualizations/saved_visualization_register',
|
||||||
|
|
|
@ -29,7 +29,6 @@ import { npSetup } from 'ui/new_platform';
|
||||||
import 'uiExports/home';
|
import 'uiExports/home';
|
||||||
import 'uiExports/visTypes';
|
import 'uiExports/visTypes';
|
||||||
|
|
||||||
import 'uiExports/visEditorTypes';
|
|
||||||
import 'uiExports/visualize';
|
import 'uiExports/visualize';
|
||||||
import 'uiExports/savedObjectTypes';
|
import 'uiExports/savedObjectTypes';
|
||||||
import 'uiExports/fieldFormatEditors';
|
import 'uiExports/fieldFormatEditors';
|
||||||
|
|
193
src/legacy/core_plugins/kibana/public/visualize/application.ts
Normal file
193
src/legacy/core_plugins/kibana/public/visualize/application.ts
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import angular, { IModule } from 'angular';
|
||||||
|
import { EuiConfirmModal } from '@elastic/eui';
|
||||||
|
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
|
||||||
|
|
||||||
|
import { AppMountContext, LegacyCoreStart } from 'kibana/public';
|
||||||
|
import {
|
||||||
|
AppStateProvider,
|
||||||
|
AppState,
|
||||||
|
configureAppAngularModule,
|
||||||
|
confirmModalFactory,
|
||||||
|
createTopNavDirective,
|
||||||
|
createTopNavHelper,
|
||||||
|
EventsProvider,
|
||||||
|
GlobalStateProvider,
|
||||||
|
KbnUrlProvider,
|
||||||
|
RedirectWhenMissingProvider,
|
||||||
|
IPrivate,
|
||||||
|
PersistedState,
|
||||||
|
PrivateProvider,
|
||||||
|
PromiseServiceCreator,
|
||||||
|
StateManagementConfigProvider,
|
||||||
|
} from './legacy_imports';
|
||||||
|
import { NavigationStart } from '../../../navigation/public';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import { initVisualizeApp } from './legacy_app';
|
||||||
|
import { VisualizeKibanaServices } from './kibana_services';
|
||||||
|
|
||||||
|
let angularModuleInstance: IModule | null = null;
|
||||||
|
|
||||||
|
export const renderApp = async (
|
||||||
|
element: HTMLElement,
|
||||||
|
appBasePath: string,
|
||||||
|
deps: VisualizeKibanaServices
|
||||||
|
) => {
|
||||||
|
if (!angularModuleInstance) {
|
||||||
|
angularModuleInstance = createLocalAngularModule(deps.core, deps.navigation);
|
||||||
|
// global routing stuff
|
||||||
|
configureAppAngularModule(angularModuleInstance, deps.core as LegacyCoreStart, true);
|
||||||
|
// custom routing stuff
|
||||||
|
initVisualizeApp(angularModuleInstance, deps);
|
||||||
|
}
|
||||||
|
const $injector = mountVisualizeApp(appBasePath, element);
|
||||||
|
return () => $injector.get('$rootScope').$destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
const mainTemplate = (basePath: string) => `<div style="height: 100%">
|
||||||
|
<base href="${basePath}" />
|
||||||
|
<div ng-view style="height: 100%;"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const moduleName = 'app/visualize';
|
||||||
|
|
||||||
|
const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react'];
|
||||||
|
|
||||||
|
function mountVisualizeApp(appBasePath: string, element: HTMLElement) {
|
||||||
|
const mountpoint = document.createElement('div');
|
||||||
|
mountpoint.setAttribute('style', 'height: 100%');
|
||||||
|
mountpoint.innerHTML = mainTemplate(appBasePath);
|
||||||
|
// bootstrap angular into detached element and attach it later to
|
||||||
|
// make angular-within-angular possible
|
||||||
|
const $injector = angular.bootstrap(mountpoint, [moduleName]);
|
||||||
|
// initialize global state handler
|
||||||
|
element.appendChild(mountpoint);
|
||||||
|
return $injector;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalAngularModule(core: AppMountContext['core'], navigation: NavigationStart) {
|
||||||
|
createLocalI18nModule();
|
||||||
|
createLocalPrivateModule();
|
||||||
|
createLocalPromiseModule();
|
||||||
|
createLocalConfigModule(core);
|
||||||
|
createLocalKbnUrlModule();
|
||||||
|
createLocalStateModule();
|
||||||
|
createLocalPersistedStateModule();
|
||||||
|
createLocalTopNavModule(navigation);
|
||||||
|
createLocalConfirmModalModule();
|
||||||
|
|
||||||
|
const visualizeAngularModule: IModule = angular.module(moduleName, [
|
||||||
|
...thirdPartyAngularDependencies,
|
||||||
|
'app/visualize/Config',
|
||||||
|
'app/visualize/I18n',
|
||||||
|
'app/visualize/Private',
|
||||||
|
'app/visualize/PersistedState',
|
||||||
|
'app/visualize/TopNav',
|
||||||
|
'app/visualize/State',
|
||||||
|
'app/visualize/ConfirmModal',
|
||||||
|
]);
|
||||||
|
return visualizeAngularModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalConfirmModalModule() {
|
||||||
|
angular
|
||||||
|
.module('app/visualize/ConfirmModal', ['react'])
|
||||||
|
.factory('confirmModal', confirmModalFactory)
|
||||||
|
.directive('confirmModal', reactDirective => reactDirective(EuiConfirmModal));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalStateModule() {
|
||||||
|
angular
|
||||||
|
.module('app/visualize/State', [
|
||||||
|
'app/visualize/Private',
|
||||||
|
'app/visualize/Config',
|
||||||
|
'app/visualize/KbnUrl',
|
||||||
|
'app/visualize/Promise',
|
||||||
|
'app/visualize/PersistedState',
|
||||||
|
])
|
||||||
|
.factory('AppState', function(Private: IPrivate) {
|
||||||
|
return Private(AppStateProvider);
|
||||||
|
})
|
||||||
|
.service('getAppState', function(Private: IPrivate) {
|
||||||
|
return Private<AppState>(AppStateProvider).getAppState;
|
||||||
|
})
|
||||||
|
.service('globalState', function(Private: IPrivate) {
|
||||||
|
return Private(GlobalStateProvider);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalPersistedStateModule() {
|
||||||
|
angular
|
||||||
|
.module('app/visualize/PersistedState', ['app/visualize/Private', 'app/visualize/Promise'])
|
||||||
|
.factory('PersistedState', (Private: IPrivate) => {
|
||||||
|
const Events = Private(EventsProvider);
|
||||||
|
return class AngularPersistedState extends PersistedState {
|
||||||
|
constructor(value: any, path: any) {
|
||||||
|
super(value, path, Events);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalKbnUrlModule() {
|
||||||
|
angular
|
||||||
|
.module('app/visualize/KbnUrl', ['app/visualize/Private', 'ngRoute'])
|
||||||
|
.service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider))
|
||||||
|
.service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalConfigModule(core: AppMountContext['core']) {
|
||||||
|
angular
|
||||||
|
.module('app/visualize/Config', ['app/visualize/Private'])
|
||||||
|
.provider('stateManagementConfig', StateManagementConfigProvider)
|
||||||
|
.provider('config', () => {
|
||||||
|
return {
|
||||||
|
$get: () => ({
|
||||||
|
get: core.uiSettings.get.bind(core.uiSettings),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalPromiseModule() {
|
||||||
|
angular.module('app/visualize/Promise', []).service('Promise', PromiseServiceCreator);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalPrivateModule() {
|
||||||
|
angular.module('app/visualize/Private', []).provider('Private', PrivateProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalTopNavModule(navigation: NavigationStart) {
|
||||||
|
angular
|
||||||
|
.module('app/visualize/TopNav', ['react'])
|
||||||
|
.directive('kbnTopNav', createTopNavDirective)
|
||||||
|
.directive('kbnTopNavHelper', createTopNavHelper(navigation.ui));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalI18nModule() {
|
||||||
|
angular
|
||||||
|
.module('app/visualize/I18n', [])
|
||||||
|
.provider('i18n', I18nProvider)
|
||||||
|
.filter('i18n', i18nFilter)
|
||||||
|
.directive('i18nId', i18nDirective);
|
||||||
|
}
|
|
@ -39,7 +39,7 @@
|
||||||
show-search-bar="true"
|
show-search-bar="true"
|
||||||
show-query-bar="true"
|
show-query-bar="true"
|
||||||
show-query-input="showQueryInput()"
|
show-query-input="showQueryInput()"
|
||||||
show-filter-bar="showFilterBar() && chrome.getVisible()"
|
show-filter-bar="showFilterBar() && isVisible"
|
||||||
show-date-picker="showQueryBarTimePicker()"
|
show-date-picker="showQueryBarTimePicker()"
|
||||||
show-auto-refresh-only="!showQueryBarTimePicker()"
|
show-auto-refresh-only="!showQueryBarTimePicker()"
|
||||||
query="state.query"
|
query="state.query"
|
||||||
|
@ -91,7 +91,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<visualization-embedded
|
<visualization-embedded
|
||||||
ng-if="!chrome.getVisible()"
|
ng-if="!isVisible"
|
||||||
class="visualize"
|
class="visualize"
|
||||||
saved-obj="savedVis"
|
saved-obj="savedVis"
|
||||||
ui-state="uiState"
|
ui-state="uiState"
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
>
|
>
|
||||||
</h1>
|
</h1>
|
||||||
<visualization-editor
|
<visualization-editor
|
||||||
ng-if="chrome.getVisible()"
|
ng-if="isVisible"
|
||||||
saved-obj="savedVis"
|
saved-obj="savedVis"
|
||||||
ui-state="uiState"
|
ui-state="uiState"
|
||||||
time-range="timeRange"
|
time-range="timeRange"
|
||||||
|
|
|
@ -17,27 +17,27 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import angular from 'angular';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import '../saved_visualizations/saved_visualizations';
|
import '../saved_visualizations/saved_visualizations';
|
||||||
import './visualization_editor';
|
|
||||||
import './visualization';
|
|
||||||
|
|
||||||
import { ensureDefaultIndexPattern } from 'ui/legacy_compat';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
import { migrateAppState } from './lib';
|
import { migrateAppState } from './lib';
|
||||||
import editorTemplate from './editor.html';
|
|
||||||
import { DashboardConstants } from '../../dashboard/dashboard_constants';
|
import { DashboardConstants } from '../../dashboard/dashboard_constants';
|
||||||
import { VisualizeConstants } from '../visualize_constants';
|
import { VisualizeConstants } from '../visualize_constants';
|
||||||
import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs';
|
import { getEditBreadcrumbs } from '../breadcrumbs';
|
||||||
|
|
||||||
import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util';
|
import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util';
|
||||||
|
import { FilterStateManager } from '../../../../data/public/filter/filter_manager';
|
||||||
|
import { unhashUrl } from '../../../../../../plugins/kibana_utils/public';
|
||||||
|
|
||||||
|
import { initVisEditorDirective } from './visualization_editor';
|
||||||
|
import { initVisualizationDirective } from './visualization';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getServices,
|
|
||||||
angular,
|
|
||||||
absoluteToParsedUrl,
|
absoluteToParsedUrl,
|
||||||
KibanaParsedUrl,
|
KibanaParsedUrl,
|
||||||
migrateLegacyQuery,
|
migrateLegacyQuery,
|
||||||
|
@ -45,108 +45,24 @@ import {
|
||||||
showSaveModal,
|
showSaveModal,
|
||||||
stateMonitorFactory,
|
stateMonitorFactory,
|
||||||
subscribeWithScope,
|
subscribeWithScope,
|
||||||
unhashUrl
|
} from '../legacy_imports';
|
||||||
} from '../kibana_services';
|
|
||||||
|
|
||||||
const {
|
import { getServices } from '../kibana_services';
|
||||||
core,
|
|
||||||
capabilities,
|
|
||||||
chrome,
|
|
||||||
chromeLegacy,
|
|
||||||
npData,
|
|
||||||
docTitle,
|
|
||||||
FilterBarQueryFilterProvider,
|
|
||||||
getBasePath,
|
|
||||||
toastNotifications,
|
|
||||||
uiModules,
|
|
||||||
uiRoutes,
|
|
||||||
visualizations,
|
|
||||||
share,
|
|
||||||
} = getServices();
|
|
||||||
|
|
||||||
const savedQueryService = npData.query.savedQueries;
|
export function initEditorDirective(app, deps) {
|
||||||
const { timefilter } = npData.query.timefilter;
|
app.directive('visualizeApp', function () {
|
||||||
|
|
||||||
uiRoutes
|
|
||||||
.when(VisualizeConstants.CREATE_PATH, {
|
|
||||||
template: editorTemplate,
|
|
||||||
k7Breadcrumbs: getCreateBreadcrumbs,
|
|
||||||
resolve: {
|
|
||||||
savedVis: function (savedVisualizations, redirectWhenMissing, $route, $rootScope, kbnUrl) {
|
|
||||||
const visTypes = visualizations.types.all();
|
|
||||||
const visType = _.find(visTypes, { name: $route.current.params.type });
|
|
||||||
const shouldHaveIndex = visType.requiresSearch && visType.options.showIndexSelection;
|
|
||||||
const hasIndex = $route.current.params.indexPattern || $route.current.params.savedSearchId;
|
|
||||||
if (shouldHaveIndex && !hasIndex) {
|
|
||||||
throw new Error(
|
|
||||||
i18n.translate('kbn.visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage', {
|
|
||||||
defaultMessage: 'You must provide either an indexPattern or a savedSearchId',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ensureDefaultIndexPattern(core, npData, $rootScope, kbnUrl).then(() => savedVisualizations.get($route.current.params))
|
|
||||||
.then(savedVis => {
|
|
||||||
if (savedVis.vis.type.setup) {
|
|
||||||
return savedVis.vis.type.setup(savedVis)
|
|
||||||
.catch(() => savedVis);
|
|
||||||
}
|
|
||||||
return savedVis;
|
|
||||||
})
|
|
||||||
.catch(redirectWhenMissing({
|
|
||||||
'*': '/visualize'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.when(`${VisualizeConstants.EDIT_PATH}/:id`, {
|
|
||||||
template: editorTemplate,
|
|
||||||
k7Breadcrumbs: getEditBreadcrumbs,
|
|
||||||
resolve: {
|
|
||||||
savedVis: function (savedVisualizations, redirectWhenMissing, $route, $rootScope, kbnUrl) {
|
|
||||||
return ensureDefaultIndexPattern(core, npData, $rootScope, kbnUrl)
|
|
||||||
.then(() => savedVisualizations.get($route.current.params.id))
|
|
||||||
.then((savedVis) => {
|
|
||||||
chrome.recentlyAccessed.add(
|
|
||||||
savedVis.getFullPath(),
|
|
||||||
savedVis.title,
|
|
||||||
savedVis.id
|
|
||||||
);
|
|
||||||
return savedVis;
|
|
||||||
})
|
|
||||||
.then(savedVis => {
|
|
||||||
if (savedVis.vis.type.setup) {
|
|
||||||
return savedVis.vis.type.setup(savedVis).catch(() => savedVis);
|
|
||||||
}
|
|
||||||
return savedVis;
|
|
||||||
})
|
|
||||||
.catch(
|
|
||||||
redirectWhenMissing({
|
|
||||||
visualization: '/visualize',
|
|
||||||
search: '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
|
|
||||||
'index-pattern':
|
|
||||||
'/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
|
|
||||||
'index-pattern-field':
|
|
||||||
'/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
uiModules
|
|
||||||
.get('app/visualize', [
|
|
||||||
'kibana/url'
|
|
||||||
])
|
|
||||||
.directive('visualizeApp', function () {
|
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
controllerAs: 'visualizeApp',
|
controllerAs: 'visualizeApp',
|
||||||
controller: VisEditor,
|
controller: VisualizeAppController,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function VisEditor(
|
initVisEditorDirective(app, deps);
|
||||||
|
initVisualizationDirective(app, deps);
|
||||||
|
}
|
||||||
|
|
||||||
|
function VisualizeAppController(
|
||||||
$scope,
|
$scope,
|
||||||
$element,
|
$element,
|
||||||
$route,
|
$route,
|
||||||
|
@ -154,19 +70,42 @@ function VisEditor(
|
||||||
$window,
|
$window,
|
||||||
$injector,
|
$injector,
|
||||||
$timeout,
|
$timeout,
|
||||||
indexPatterns,
|
|
||||||
kbnUrl,
|
kbnUrl,
|
||||||
redirectWhenMissing,
|
redirectWhenMissing,
|
||||||
Private,
|
|
||||||
Promise,
|
Promise,
|
||||||
config,
|
|
||||||
kbnBaseUrl,
|
kbnBaseUrl,
|
||||||
localStorage,
|
getAppState,
|
||||||
|
globalState,
|
||||||
) {
|
) {
|
||||||
const queryFilter = Private(FilterBarQueryFilterProvider);
|
const {
|
||||||
|
indexPatterns,
|
||||||
|
localStorage,
|
||||||
|
visualizeCapabilities,
|
||||||
|
share,
|
||||||
|
data: {
|
||||||
|
query: {
|
||||||
|
filterManager,
|
||||||
|
timefilter: { timefilter },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
toastNotifications,
|
||||||
|
legacyChrome,
|
||||||
|
chrome,
|
||||||
|
getBasePath,
|
||||||
|
core: { docLinks },
|
||||||
|
savedQueryService,
|
||||||
|
uiSettings,
|
||||||
|
} = getServices();
|
||||||
|
|
||||||
|
const filterStateManager = new FilterStateManager(globalState, getAppState, filterManager);
|
||||||
|
const queryFilter = filterManager;
|
||||||
// Retrieve the resolved SavedVis instance.
|
// Retrieve the resolved SavedVis instance.
|
||||||
const savedVis = $route.current.locals.savedVis;
|
const savedVis = $route.current.locals.savedVis;
|
||||||
|
const _applyVis = () => {
|
||||||
|
$scope.$apply();
|
||||||
|
};
|
||||||
|
// This will trigger a digest cycle. This is needed when vis is updated from a global angular like in visualize_embeddable.js.
|
||||||
|
savedVis.vis.on('apply', _applyVis);
|
||||||
// vis is instance of src/legacy/ui/public/vis/vis.js.
|
// vis is instance of src/legacy/ui/public/vis/vis.js.
|
||||||
// SearchSource is a promise-based stream of search results that can inherit from other search sources.
|
// SearchSource is a promise-based stream of search results that can inherit from other search sources.
|
||||||
const { vis, searchSource } = savedVis;
|
const { vis, searchSource } = savedVis;
|
||||||
|
@ -177,7 +116,7 @@ function VisEditor(
|
||||||
dirty: !savedVis.id
|
dirty: !savedVis.id
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.topNavMenu = [...(capabilities.visualize.save ? [{
|
$scope.topNavMenu = [...(visualizeCapabilities.save ? [{
|
||||||
id: 'save',
|
id: 'save',
|
||||||
label: i18n.translate('kbn.topNavMenu.saveVisualizationButtonLabel', { defaultMessage: 'save' }),
|
label: i18n.translate('kbn.topNavMenu.saveVisualizationButtonLabel', { defaultMessage: 'save' }),
|
||||||
description: i18n.translate('kbn.visualize.topNavMenu.saveVisualizationButtonAriaLabel', {
|
description: i18n.translate('kbn.visualize.topNavMenu.saveVisualizationButtonAriaLabel', {
|
||||||
|
@ -246,7 +185,7 @@ function VisEditor(
|
||||||
share.toggleShareContextMenu({
|
share.toggleShareContextMenu({
|
||||||
anchorElement,
|
anchorElement,
|
||||||
allowEmbed: true,
|
allowEmbed: true,
|
||||||
allowShortUrl: capabilities.visualize.createShortUrl,
|
allowShortUrl: visualizeCapabilities.createShortUrl,
|
||||||
shareableUrl: unhashUrl(window.location.href),
|
shareableUrl: unhashUrl(window.location.href),
|
||||||
objectId: savedVis.id,
|
objectId: savedVis.id,
|
||||||
objectType: 'visualization',
|
objectType: 'visualization',
|
||||||
|
@ -295,7 +234,7 @@ function VisEditor(
|
||||||
let stateMonitor;
|
let stateMonitor;
|
||||||
|
|
||||||
if (savedVis.id) {
|
if (savedVis.id) {
|
||||||
docTitle.change(savedVis.title);
|
chrome.docTitle.change(savedVis.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract visualization state with filtered aggs. You can see these filtered aggs in the URL.
|
// Extract visualization state with filtered aggs. You can see these filtered aggs in the URL.
|
||||||
|
@ -306,7 +245,7 @@ function VisEditor(
|
||||||
linked: !!savedVis.savedSearchId,
|
linked: !!savedVis.savedSearchId,
|
||||||
query: searchSource.getOwnField('query') || {
|
query: searchSource.getOwnField('query') || {
|
||||||
query: '',
|
query: '',
|
||||||
language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage')
|
language: localStorage.get('kibana.userQueryLanguage') || uiSettings.get('search:queryLanguage')
|
||||||
},
|
},
|
||||||
filters: searchSource.getOwnField('filter') || [],
|
filters: searchSource.getOwnField('filter') || [],
|
||||||
vis: savedVisState
|
vis: savedVisState
|
||||||
|
@ -345,9 +284,9 @@ function VisEditor(
|
||||||
queryFilter.setFilters(filters);
|
queryFilter.setFilters(filters);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.showSaveQuery = capabilities.visualize.saveQuery;
|
$scope.showSaveQuery = visualizeCapabilities.saveQuery;
|
||||||
|
|
||||||
$scope.$watch(() => capabilities.visualize.saveQuery, (newCapability) => {
|
$scope.$watch(() => visualizeCapabilities.saveQuery, (newCapability) => {
|
||||||
$scope.showSaveQuery = newCapability;
|
$scope.showSaveQuery = newCapability;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -455,13 +394,15 @@ function VisEditor(
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
$scope.$on('$destroy', function () {
|
$scope.$on('$destroy', () => {
|
||||||
if ($scope._handler) {
|
if ($scope._handler) {
|
||||||
$scope._handler.destroy();
|
$scope._handler.destroy();
|
||||||
}
|
}
|
||||||
savedVis.destroy();
|
savedVis.destroy();
|
||||||
stateMonitor.destroy();
|
stateMonitor.destroy();
|
||||||
|
filterStateManager.destroy();
|
||||||
subscriptions.unsubscribe();
|
subscriptions.unsubscribe();
|
||||||
|
$scope.vis.off('apply', _applyVis);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -503,7 +444,7 @@ function VisEditor(
|
||||||
delete $state.savedQuery;
|
delete $state.savedQuery;
|
||||||
$state.query = {
|
$state.query = {
|
||||||
query: '',
|
query: '',
|
||||||
language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage')
|
language: localStorage.get('kibana.userQueryLanguage') || uiSettings.get('search:queryLanguage')
|
||||||
};
|
};
|
||||||
queryFilter.removeAll();
|
queryFilter.removeAll();
|
||||||
$state.save();
|
$state.save();
|
||||||
|
@ -589,14 +530,14 @@ function VisEditor(
|
||||||
// Since we aren't reloading the page, only inserting a new browser history item, we need to manually update
|
// Since we aren't reloading the page, only inserting a new browser history item, we need to manually update
|
||||||
// the last url for this app, so directly clicking on the Visualize tab will also bring the user to the saved
|
// the last url for this app, so directly clicking on the Visualize tab will also bring the user to the saved
|
||||||
// url, not the unsaved one.
|
// url, not the unsaved one.
|
||||||
chromeLegacy.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl);
|
legacyChrome.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl);
|
||||||
|
|
||||||
const lastDashboardAbsoluteUrl = chrome.navLinks.get('kibana:dashboard').url;
|
const lastDashboardAbsoluteUrl = chrome.navLinks.get('kibana:dashboard').url;
|
||||||
const dashboardParsedUrl = absoluteToParsedUrl(lastDashboardAbsoluteUrl, getBasePath());
|
const dashboardParsedUrl = absoluteToParsedUrl(lastDashboardAbsoluteUrl, getBasePath());
|
||||||
dashboardParsedUrl.addQueryParameter(DashboardConstants.NEW_VISUALIZATION_ID_PARAM, savedVis.id);
|
dashboardParsedUrl.addQueryParameter(DashboardConstants.NEW_VISUALIZATION_ID_PARAM, savedVis.id);
|
||||||
kbnUrl.change(dashboardParsedUrl.appPath);
|
kbnUrl.change(dashboardParsedUrl.appPath);
|
||||||
} else if (savedVis.id === $route.current.params.id) {
|
} else if (savedVis.id === $route.current.params.id) {
|
||||||
docTitle.change(savedVis.lastSavedTitle);
|
chrome.docTitle.change(savedVis.lastSavedTitle);
|
||||||
chrome.setBreadcrumbs($injector.invoke(getEditBreadcrumbs));
|
chrome.setBreadcrumbs($injector.invoke(getEditBreadcrumbs));
|
||||||
savedVis.vis.title = savedVis.title;
|
savedVis.vis.title = savedVis.title;
|
||||||
savedVis.vis.description = savedVis.description;
|
savedVis.vis.description = savedVis.description;
|
||||||
|
@ -661,7 +602,7 @@ function VisEditor(
|
||||||
vis.type.feedbackMessage;
|
vis.type.feedbackMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
addHelpMenuToAppChrome(chrome);
|
addHelpMenuToAppChrome(chrome, docLinks);
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,8 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getServices } from '../kibana_services';
|
export function initVisualizationDirective(app, deps) {
|
||||||
|
app.directive('visualizationEmbedded', function ($timeout, getAppState) {
|
||||||
const { embeddables, uiModules } = getServices();
|
|
||||||
|
|
||||||
uiModules
|
|
||||||
.get('kibana/directive', ['ngSanitize'])
|
|
||||||
.directive('visualizationEmbedded', function (Private, $timeout, getAppState) {
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
|
@ -37,7 +32,7 @@ uiModules
|
||||||
link: function ($scope, element) {
|
link: function ($scope, element) {
|
||||||
$scope.renderFunction = async () => {
|
$scope.renderFunction = async () => {
|
||||||
if (!$scope._handler) {
|
if (!$scope._handler) {
|
||||||
$scope._handler = await embeddables.getEmbeddableFactory('visualization').createFromObject($scope.savedObj, {
|
$scope._handler = await deps.embeddables.getEmbeddableFactory('visualization').createFromObject($scope.savedObj, {
|
||||||
timeRange: $scope.timeRange,
|
timeRange: $scope.timeRange,
|
||||||
filters: $scope.filters || [],
|
filters: $scope.filters || [],
|
||||||
query: $scope.query,
|
query: $scope.query,
|
||||||
|
@ -66,3 +61,4 @@ uiModules
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -17,15 +17,8 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getServices, VisEditorTypesRegistryProvider } from '../kibana_services';
|
export function initVisEditorDirective(app, deps) {
|
||||||
|
app.directive('visualizationEditor', function ($timeout, getAppState) {
|
||||||
const { uiModules } = getServices();
|
|
||||||
|
|
||||||
uiModules
|
|
||||||
.get('kibana/directive', ['ngSanitize'])
|
|
||||||
.directive('visualizationEditor', function (Private, $timeout, getAppState) {
|
|
||||||
const editorTypes = Private(VisEditorTypesRegistryProvider);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
|
@ -38,7 +31,8 @@ uiModules
|
||||||
link: function ($scope, element) {
|
link: function ($scope, element) {
|
||||||
const editorType = $scope.savedObj.vis.type.editor;
|
const editorType = $scope.savedObj.vis.type.editor;
|
||||||
const Editor = typeof editorType === 'function' ? editorType :
|
const Editor = typeof editorType === 'function' ? editorType :
|
||||||
editorTypes.find(editor => editor.key === editorType);
|
deps.editorTypes.find(editor => editor.key === editorType);
|
||||||
|
|
||||||
const editor = new Editor(element[0], $scope.savedObj);
|
const editor = new Editor(element[0], $scope.savedObj);
|
||||||
|
|
||||||
$scope.renderFunction = () => {
|
$scope.renderFunction = () => {
|
||||||
|
@ -62,3 +56,4 @@ uiModules
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Embeddable, EmbeddableOutput } from '../../../../../../plugins/embeddable/public';
|
||||||
|
|
||||||
import { Embeddable, EmbeddableOutput } from '../kibana_services';
|
|
||||||
import { DisabledLabVisualization } from './disabled_lab_visualization';
|
import { DisabledLabVisualization } from './disabled_lab_visualization';
|
||||||
import { VisualizeInput } from './visualize_embeddable';
|
import { VisualizeInput } from './visualize_embeddable';
|
||||||
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
|
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
|
||||||
|
|
|
@ -17,11 +17,10 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getServices, getFromSavedObject, VisSavedObject } from '../kibana_services';
|
import { npStart } from 'ui/new_platform';
|
||||||
|
|
||||||
import { IIndexPattern } from '../../../../../../plugins/data/public';
|
import { VisSavedObject } from './visualize_embeddable';
|
||||||
|
import { indexPatterns, IIndexPattern } from '../../../../../../plugins/data/public';
|
||||||
const { savedObjectsClient, uiSettings } = getServices();
|
|
||||||
|
|
||||||
export async function getIndexPattern(
|
export async function getIndexPattern(
|
||||||
savedVis: VisSavedObject
|
savedVis: VisSavedObject
|
||||||
|
@ -30,7 +29,8 @@ export async function getIndexPattern(
|
||||||
return savedVis.vis.indexPattern;
|
return savedVis.vis.indexPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultIndex = uiSettings.get('defaultIndex');
|
const savedObjectsClient = npStart.core.savedObjects.client;
|
||||||
|
const defaultIndex = npStart.core.uiSettings.get('defaultIndex');
|
||||||
|
|
||||||
if (savedVis.vis.params.index_pattern) {
|
if (savedVis.vis.params.index_pattern) {
|
||||||
const indexPatternObjects = await savedObjectsClient.find({
|
const indexPatternObjects = await savedObjectsClient.find({
|
||||||
|
@ -39,10 +39,10 @@ export async function getIndexPattern(
|
||||||
search: `"${savedVis.vis.params.index_pattern}"`,
|
search: `"${savedVis.vis.params.index_pattern}"`,
|
||||||
searchFields: ['title'],
|
searchFields: ['title'],
|
||||||
});
|
});
|
||||||
const [indexPattern] = indexPatternObjects.savedObjects.map(getFromSavedObject);
|
const [indexPattern] = indexPatternObjects.savedObjects.map(indexPatterns.getFromSavedObject);
|
||||||
return indexPattern;
|
return indexPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedObject = await savedObjectsClient.get('index-pattern', defaultIndex);
|
const savedObject = await savedObjectsClient.get('index-pattern', defaultIndex);
|
||||||
return getFromSavedObject(savedObject);
|
return indexPatterns.getFromSavedObject(savedObject);
|
||||||
}
|
}
|
||||||
|
|
|
@ -375,6 +375,8 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
|
||||||
if (this.handler) {
|
if (this.handler) {
|
||||||
this.handler.update(this.expression, expressionParams);
|
this.handler.update(this.expression, expressionParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.vis.emit('apply');
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleVisUpdate = async () => {
|
private handleVisUpdate = async () => {
|
||||||
|
|
|
@ -17,37 +17,49 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'uiExports/contextMenuActions';
|
||||||
|
import 'uiExports/devTools';
|
||||||
|
import 'uiExports/docViews';
|
||||||
|
import 'uiExports/embeddableActions';
|
||||||
|
import 'uiExports/fieldFormatEditors';
|
||||||
|
import 'uiExports/fieldFormats';
|
||||||
|
import 'uiExports/home';
|
||||||
|
import 'uiExports/indexManagement';
|
||||||
|
import 'uiExports/inspectorViews';
|
||||||
|
import 'uiExports/savedObjectTypes';
|
||||||
|
import 'uiExports/search';
|
||||||
|
import 'uiExports/shareContextMenuExtensions';
|
||||||
|
import 'uiExports/visTypes';
|
||||||
|
import 'uiExports/visualize';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
|
import chrome from 'ui/chrome';
|
||||||
|
import { npStart } from 'ui/new_platform';
|
||||||
|
|
||||||
import { Legacy } from 'kibana';
|
import { Legacy } from 'kibana';
|
||||||
|
|
||||||
import { SavedObjectAttributes } from 'kibana/server';
|
import { SavedObjectAttributes } from 'kibana/server';
|
||||||
|
import {
|
||||||
|
EmbeddableFactory,
|
||||||
|
ErrorEmbeddable,
|
||||||
|
Container,
|
||||||
|
EmbeddableOutput,
|
||||||
|
} from '../../../../../../plugins/embeddable/public';
|
||||||
|
import { start as visualizations } from '../../../../visualizations/public/np_ready/public/legacy';
|
||||||
import { showNewVisModal } from '../wizard';
|
import { showNewVisModal } from '../wizard';
|
||||||
import { SavedVisualizations } from '../types';
|
import { SavedVisualizations } from '../types';
|
||||||
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
|
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
|
||||||
import { getIndexPattern } from './get_index_pattern';
|
import { getIndexPattern } from './get_index_pattern';
|
||||||
import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable';
|
import {
|
||||||
|
VisualizeEmbeddable,
|
||||||
|
VisualizeInput,
|
||||||
|
VisualizeOutput,
|
||||||
|
VisSavedObject,
|
||||||
|
} from './visualize_embeddable';
|
||||||
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
|
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
|
||||||
import { TypesStart } from '../../../../visualizations/public/np_ready/public/types';
|
import { TypesStart } from '../../../../visualizations/public/np_ready/public/types';
|
||||||
|
|
||||||
import {
|
|
||||||
getServices,
|
|
||||||
Container,
|
|
||||||
EmbeddableFactory,
|
|
||||||
EmbeddableOutput,
|
|
||||||
ErrorEmbeddable,
|
|
||||||
VisSavedObject,
|
|
||||||
} from '../kibana_services';
|
|
||||||
|
|
||||||
const {
|
|
||||||
addBasePath,
|
|
||||||
capabilities,
|
|
||||||
embeddable,
|
|
||||||
getInjector,
|
|
||||||
uiSettings,
|
|
||||||
visualizations,
|
|
||||||
} = getServices();
|
|
||||||
|
|
||||||
interface VisualizationAttributes extends SavedObjectAttributes {
|
interface VisualizationAttributes extends SavedObjectAttributes {
|
||||||
visState: string;
|
visState: string;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +108,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
|
||||||
if (!visType) {
|
if (!visType) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (uiSettings.get('visualize:enableLabs')) {
|
if (npStart.core.uiSettings.get('visualize:enableLabs')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return visType.stage !== 'experimental';
|
return visType.stage !== 'experimental';
|
||||||
|
@ -108,7 +120,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
|
||||||
}
|
}
|
||||||
|
|
||||||
public isEditable() {
|
public isEditable() {
|
||||||
return capabilities.visualize.save as boolean;
|
return npStart.core.application.capabilities.visualize.save as boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDisplayName() {
|
public getDisplayName() {
|
||||||
|
@ -122,14 +134,16 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
|
||||||
input: Partial<VisualizeInput> & { id: string },
|
input: Partial<VisualizeInput> & { id: string },
|
||||||
parent?: Container
|
parent?: Container
|
||||||
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
|
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
|
||||||
const $injector = await getInjector();
|
const $injector = await chrome.dangerouslyGetActiveInjector();
|
||||||
const config = $injector.get<Legacy.KibanaConfig>('config');
|
const config = $injector.get<Legacy.KibanaConfig>('config');
|
||||||
const savedVisualizations = $injector.get<SavedVisualizations>('savedVisualizations');
|
const savedVisualizations = $injector.get<SavedVisualizations>('savedVisualizations');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const visId = savedObject.id as string;
|
const visId = savedObject.id as string;
|
||||||
|
|
||||||
const editUrl = visId ? addBasePath(`/app/kibana${savedVisualizations.urlFor(visId)}`) : '';
|
const editUrl = visId
|
||||||
|
? npStart.core.http.basePath.prepend(`/app/kibana${savedVisualizations.urlFor(visId)}`)
|
||||||
|
: '';
|
||||||
const isLabsEnabled = config.get<boolean>('visualize:enableLabs');
|
const isLabsEnabled = config.get<boolean>('visualize:enableLabs');
|
||||||
|
|
||||||
if (!isLabsEnabled && savedObject.vis.type.stage === 'experimental') {
|
if (!isLabsEnabled && savedObject.vis.type.stage === 'experimental') {
|
||||||
|
@ -161,7 +175,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
|
||||||
input: Partial<VisualizeInput> & { id: string },
|
input: Partial<VisualizeInput> & { id: string },
|
||||||
parent?: Container
|
parent?: Container
|
||||||
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
|
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
|
||||||
const $injector = await getInjector();
|
const $injector = await chrome.dangerouslyGetActiveInjector();
|
||||||
const savedVisualizations = $injector.get<SavedVisualizations>('savedVisualizations');
|
const savedVisualizations = $injector.get<SavedVisualizations>('savedVisualizations');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -179,14 +193,15 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
|
||||||
// TODO: This is a bit of a hack to preserve the original functionality. Ideally we will clean this up
|
// TODO: This is a bit of a hack to preserve the original functionality. Ideally we will clean this up
|
||||||
// to allow for in place creation of visualizations without having to navigate away to a new URL.
|
// to allow for in place creation of visualizations without having to navigate away to a new URL.
|
||||||
if (this.visTypes) {
|
if (this.visTypes) {
|
||||||
showNewVisModal(this.visTypes, {
|
showNewVisModal(
|
||||||
|
this.visTypes,
|
||||||
|
{
|
||||||
editorParams: ['addToDashboard'],
|
editorParams: ['addToDashboard'],
|
||||||
});
|
},
|
||||||
|
npStart.core.http.basePath.prepend,
|
||||||
|
npStart.core.uiSettings
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VisualizeEmbeddableFactory.createVisualizeEmbeddableFactory().then(embeddableFactory => {
|
|
||||||
embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory);
|
|
||||||
});
|
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { State } from './legacy_imports';
|
||||||
|
import { DataPublicPluginStart as DataStart } from '../../../../../plugins/data/public';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to sync the global state with the various state providers
|
||||||
|
* when a local angular application mounts. There are three different ways
|
||||||
|
* global state can be passed into the application:
|
||||||
|
* * parameter in the URL hash - e.g. shared link
|
||||||
|
* * in-memory state in the data plugin exports (timefilter and filterManager) - e.g. default values
|
||||||
|
*
|
||||||
|
* This function looks up the three sources (earlier in the list means it takes precedence),
|
||||||
|
* puts it into the globalState object and syncs it with the url.
|
||||||
|
*
|
||||||
|
* Currently the legacy chrome takes care of restoring the global state when navigating from
|
||||||
|
* one app to another - to migrate away from that it will become necessary to also write the current
|
||||||
|
* state to local storage
|
||||||
|
*/
|
||||||
|
export function syncOnMount(
|
||||||
|
globalState: State,
|
||||||
|
{
|
||||||
|
query: {
|
||||||
|
filterManager,
|
||||||
|
timefilter: { timefilter },
|
||||||
|
},
|
||||||
|
}: DataStart
|
||||||
|
) {
|
||||||
|
// pull in global state information from the URL
|
||||||
|
globalState.fetch();
|
||||||
|
// remember whether there were info in the URL
|
||||||
|
const hasGlobalURLState = Boolean(Object.keys(globalState.toObject()).length);
|
||||||
|
|
||||||
|
// sync kibana platform state with the angular global state
|
||||||
|
if (!globalState.time) {
|
||||||
|
globalState.time = timefilter.getTime();
|
||||||
|
}
|
||||||
|
if (!globalState.refreshInterval) {
|
||||||
|
globalState.refreshInterval = timefilter.getRefreshInterval();
|
||||||
|
}
|
||||||
|
if (!globalState.filters && filterManager.getGlobalFilters().length > 0) {
|
||||||
|
globalState.filters = filterManager.getGlobalFilters();
|
||||||
|
}
|
||||||
|
// only inject cross app global state if there is none in the url itself (that takes precedence)
|
||||||
|
if (hasGlobalURLState) {
|
||||||
|
// set flag the global state is set from the URL
|
||||||
|
globalState.$inheritedGlobalState = true;
|
||||||
|
}
|
||||||
|
globalState.save();
|
||||||
|
}
|
|
@ -18,10 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { getServices } from '../kibana_services';
|
|
||||||
const { docLinks } = getServices();
|
|
||||||
|
|
||||||
export function addHelpMenuToAppChrome(chrome) {
|
export function addHelpMenuToAppChrome(chrome, docLinks) {
|
||||||
chrome.setHelpExtension({
|
chrome.setHelpExtension({
|
||||||
appName: i18n.translate('kbn.visualize.helpMenu.appName', {
|
appName: i18n.translate('kbn.visualize.helpMenu.appName', {
|
||||||
defaultMessage: 'Visualize',
|
defaultMessage: 'Visualize',
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ensureDefaultIndexPattern } from 'ui/legacy_compat';
|
|
||||||
import './editor/editor';
|
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
import './saved_visualizations/_saved_vis';
|
|
||||||
import './saved_visualizations/saved_visualizations';
|
|
||||||
import visualizeListingTemplate from './listing/visualize_listing.html';
|
|
||||||
import { VisualizeListingController } from './listing/visualize_listing';
|
|
||||||
import { VisualizeConstants } from './visualize_constants';
|
|
||||||
import { getLandingBreadcrumbs, getWizardStep1Breadcrumbs } from './breadcrumbs';
|
|
||||||
|
|
||||||
import { getServices, FeatureCatalogueCategory } from './kibana_services';
|
|
||||||
|
|
||||||
const { FeatureCatalogueRegistryProvider, uiRoutes } = getServices();
|
|
||||||
|
|
||||||
uiRoutes
|
|
||||||
.defaults(/visualize/, {
|
|
||||||
requireUICapability: 'visualize.show',
|
|
||||||
badge: uiCapabilities => {
|
|
||||||
if (uiCapabilities.visualize.save) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
text: i18n.translate('kbn.visualize.badge.readOnly.text', {
|
|
||||||
defaultMessage: 'Read only',
|
|
||||||
}),
|
|
||||||
tooltip: i18n.translate('kbn.visualize.badge.readOnly.tooltip', {
|
|
||||||
defaultMessage: 'Unable to save visualizations',
|
|
||||||
}),
|
|
||||||
iconType: 'glasses'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.when(VisualizeConstants.LANDING_PAGE_PATH, {
|
|
||||||
template: visualizeListingTemplate,
|
|
||||||
k7Breadcrumbs: getLandingBreadcrumbs,
|
|
||||||
controller: VisualizeListingController,
|
|
||||||
controllerAs: 'listingController',
|
|
||||||
resolve: {
|
|
||||||
createNewVis: () => false,
|
|
||||||
hasDefaultIndex: ($rootScope, kbnUrl) => ensureDefaultIndexPattern(getServices().core, getServices().npData, $rootScope, kbnUrl)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.when(VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, {
|
|
||||||
template: visualizeListingTemplate,
|
|
||||||
k7Breadcrumbs: getWizardStep1Breadcrumbs,
|
|
||||||
controller: VisualizeListingController,
|
|
||||||
controllerAs: 'listingController',
|
|
||||||
resolve: {
|
|
||||||
createNewVis: () => true,
|
|
||||||
hasDefaultIndex: ($rootScope, kbnUrl) => ensureDefaultIndexPattern(getServices().core, getServices().npData, $rootScope, kbnUrl)
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
FeatureCatalogueRegistryProvider.register(() => {
|
|
||||||
return {
|
|
||||||
id: 'visualize',
|
|
||||||
title: 'Visualize',
|
|
||||||
description: i18n.translate(
|
|
||||||
'kbn.visualize.visualizeDescription',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Create visualizations and aggregate data stores in your Elasticsearch indices.',
|
|
||||||
}
|
|
||||||
),
|
|
||||||
icon: 'visualizeApp',
|
|
||||||
path: `/app/kibana#${VisualizeConstants.LANDING_PAGE_PATH}`,
|
|
||||||
showOnHomePage: true,
|
|
||||||
category: FeatureCatalogueCategory.DATA
|
|
||||||
};
|
|
||||||
});
|
|
70
src/legacy/core_plugins/kibana/public/visualize/index.ts
Normal file
70
src/legacy/core_plugins/kibana/public/visualize/index.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'ui/collapsible_sidebar'; // used in default editor
|
||||||
|
import 'ui/vis/editors/default/sidebar';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IPrivate,
|
||||||
|
legacyChrome,
|
||||||
|
npSetup,
|
||||||
|
npStart,
|
||||||
|
SavedObjectRegistryProvider,
|
||||||
|
VisEditorTypesRegistryProvider,
|
||||||
|
} from './legacy_imports';
|
||||||
|
import { VisualizePlugin, LegacyAngularInjectedDependencies } from './plugin';
|
||||||
|
import { start as embeddables } from '../../../embeddable_api/public/np_ready/public/legacy';
|
||||||
|
import { start as navigation } from '../../../navigation/public/legacy';
|
||||||
|
import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get dependencies relying on the global angular context.
|
||||||
|
* They also have to get resolved together with the legacy imports above
|
||||||
|
*/
|
||||||
|
async function getAngularDependencies(): Promise<LegacyAngularInjectedDependencies> {
|
||||||
|
const injector = await legacyChrome.dangerouslyGetActiveInjector();
|
||||||
|
|
||||||
|
const Private = injector.get<IPrivate>('Private');
|
||||||
|
|
||||||
|
const editorTypes = Private(VisEditorTypesRegistryProvider);
|
||||||
|
const savedObjectRegistry = Private(SavedObjectRegistryProvider);
|
||||||
|
|
||||||
|
return {
|
||||||
|
legacyChrome,
|
||||||
|
editorTypes,
|
||||||
|
savedObjectRegistry,
|
||||||
|
savedVisualizations: injector.get('savedVisualizations'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
const instance = new VisualizePlugin();
|
||||||
|
instance.setup(npSetup.core, {
|
||||||
|
...npSetup.plugins,
|
||||||
|
__LEGACY: {
|
||||||
|
getAngularDependencies,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
instance.start(npStart.core, {
|
||||||
|
...npStart.plugins,
|
||||||
|
embeddables,
|
||||||
|
navigation,
|
||||||
|
visualizations,
|
||||||
|
});
|
||||||
|
})();
|
|
@ -17,108 +17,59 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'angular-sanitize'; // used in visualization_editor.js and visualization.js
|
import {
|
||||||
import 'ui/collapsible_sidebar'; // used in default editor
|
ChromeStart,
|
||||||
import 'ui/vis/editors/default/sidebar';
|
LegacyCoreStart,
|
||||||
// load directives
|
SavedObjectsClientContract,
|
||||||
import '../../../data/public';
|
ToastsStart,
|
||||||
|
IUiSettingsClient,
|
||||||
|
} from 'kibana/public';
|
||||||
|
|
||||||
import { npStart } from 'ui/new_platform';
|
import { NavigationStart } from '../../../navigation/public';
|
||||||
import angular from 'angular'; // just used in editor.js
|
import { Storage } from '../../../../../plugins/kibana_utils/public';
|
||||||
import chromeLegacy from 'ui/chrome';
|
import { IEmbeddableStart } from '../../../../../plugins/embeddable/public';
|
||||||
|
import { SharePluginStart } from '../../../../../plugins/share/public';
|
||||||
|
import { DataPublicPluginStart, IndexPatternsContract } from '../../../../../plugins/data/public';
|
||||||
|
import { VisualizationsStart } from '../../../visualizations/public';
|
||||||
|
import { SavedVisualizations } from './types';
|
||||||
|
|
||||||
import uiRoutes from 'ui/routes';
|
export interface VisualizeKibanaServices {
|
||||||
|
addBasePath: (url: string) => string;
|
||||||
|
chrome: ChromeStart;
|
||||||
|
core: LegacyCoreStart;
|
||||||
|
data: DataPublicPluginStart;
|
||||||
|
editorTypes: any;
|
||||||
|
embeddables: IEmbeddableStart;
|
||||||
|
getBasePath: () => string;
|
||||||
|
indexPatterns: IndexPatternsContract;
|
||||||
|
legacyChrome: any;
|
||||||
|
localStorage: Storage;
|
||||||
|
navigation: NavigationStart;
|
||||||
|
toastNotifications: ToastsStart;
|
||||||
|
savedObjectsClient: SavedObjectsClientContract;
|
||||||
|
savedObjectRegistry: any;
|
||||||
|
savedQueryService: DataPublicPluginStart['query']['savedQueries'];
|
||||||
|
savedVisualizations: SavedVisualizations;
|
||||||
|
share: SharePluginStart;
|
||||||
|
uiSettings: IUiSettingsClient;
|
||||||
|
visualizeCapabilities: any;
|
||||||
|
visualizations: VisualizationsStart;
|
||||||
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
let services: VisualizeKibanaServices | null = null;
|
||||||
import { docTitle } from 'ui/doc_title';
|
export function setServices(newServices: VisualizeKibanaServices) {
|
||||||
import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter';
|
services = newServices;
|
||||||
import { wrapInI18nContext } from 'ui/i18n';
|
}
|
||||||
// @ts-ignore
|
|
||||||
import { uiModules } from 'ui/modules';
|
|
||||||
import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue';
|
|
||||||
|
|
||||||
// Saved objects
|
|
||||||
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
|
||||||
// @ts-ignore
|
|
||||||
import { SavedObject, SavedObjectProvider } from 'ui/saved_objects/saved_object';
|
|
||||||
import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry';
|
|
||||||
|
|
||||||
import { createUiStatsReporter, METRIC_TYPE } from '../../../ui_metric/public';
|
|
||||||
import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy';
|
|
||||||
import { start as embeddables } from '../../../../core_plugins/embeddable_api/public/np_ready/public/legacy';
|
|
||||||
import { start as data } from '../../../data/public/legacy';
|
|
||||||
|
|
||||||
const services = {
|
|
||||||
// new platform
|
|
||||||
addBasePath: npStart.core.http.basePath.prepend,
|
|
||||||
capabilities: npStart.core.application.capabilities,
|
|
||||||
chrome: npStart.core.chrome,
|
|
||||||
docLinks: npStart.core.docLinks,
|
|
||||||
embeddable: npStart.plugins.embeddable,
|
|
||||||
getBasePath: npStart.core.http.basePath.get,
|
|
||||||
savedObjectsClient: npStart.core.savedObjects.client,
|
|
||||||
toastNotifications: npStart.core.notifications.toasts,
|
|
||||||
uiSettings: npStart.core.uiSettings,
|
|
||||||
core: npStart.core,
|
|
||||||
|
|
||||||
share: npStart.plugins.share,
|
|
||||||
npData: npStart.plugins.data,
|
|
||||||
data,
|
|
||||||
embeddables,
|
|
||||||
visualizations,
|
|
||||||
|
|
||||||
// legacy
|
|
||||||
chromeLegacy,
|
|
||||||
docTitle,
|
|
||||||
FeatureCatalogueRegistryProvider,
|
|
||||||
FilterBarQueryFilterProvider,
|
|
||||||
getInjector: () => {
|
|
||||||
return chromeLegacy.dangerouslyGetActiveInjector();
|
|
||||||
},
|
|
||||||
SavedObjectProvider,
|
|
||||||
SavedObjectRegistryProvider,
|
|
||||||
SavedObjectsClientProvider,
|
|
||||||
timefilter: npStart.plugins.data.query.timefilter.timefilter,
|
|
||||||
uiModules,
|
|
||||||
uiRoutes,
|
|
||||||
wrapInI18nContext,
|
|
||||||
|
|
||||||
createUiStatsReporter,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getServices() {
|
export function getServices() {
|
||||||
|
if (!services) {
|
||||||
|
throw new Error(
|
||||||
|
'Kibana services not set - are you trying to import this module from outside of the visualize app?'
|
||||||
|
);
|
||||||
|
}
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
// export legacy static dependencies
|
export function clearServices() {
|
||||||
export { angular };
|
services = null;
|
||||||
export { getFromSavedObject } from 'ui/index_patterns';
|
}
|
||||||
export { PersistedState } from 'ui/persisted_state';
|
|
||||||
// @ts-ignore
|
|
||||||
export { VisEditorTypesRegistryProvider } from 'ui/registry/vis_editor_types';
|
|
||||||
export { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal';
|
|
||||||
export { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
|
|
||||||
export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url';
|
|
||||||
export { KibanaParsedUrl } from 'ui/url/kibana_parsed_url';
|
|
||||||
export { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query';
|
|
||||||
export { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
|
|
||||||
export { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal';
|
|
||||||
export { unhashUrl } from '../../../../../plugins/kibana_utils/public';
|
|
||||||
export {
|
|
||||||
Container,
|
|
||||||
Embeddable,
|
|
||||||
EmbeddableFactory,
|
|
||||||
EmbeddableInput,
|
|
||||||
EmbeddableOutput,
|
|
||||||
ErrorEmbeddable,
|
|
||||||
} from '../../../../../plugins/embeddable/public';
|
|
||||||
|
|
||||||
// export types
|
|
||||||
export { METRIC_TYPE };
|
|
||||||
export { AppState } from 'ui/state_management/app_state';
|
|
||||||
export { VisType } from 'ui/vis';
|
|
||||||
|
|
||||||
// export const
|
|
||||||
export { FeatureCatalogueCategory } from 'ui/registry/feature_catalogue';
|
|
||||||
|
|
||||||
export { VisSavedObject } from './embeddable/visualize_embeddable';
|
|
||||||
|
|
169
src/legacy/core_plugins/kibana/public/visualize/legacy_app.js
Normal file
169
src/legacy/core_plugins/kibana/public/visualize/legacy_app.js
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { find } from 'lodash';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
|
import editorTemplate from './editor/editor.html';
|
||||||
|
import visualizeListingTemplate from './listing/visualize_listing.html';
|
||||||
|
|
||||||
|
import { initVisualizeAppDirective } from './visualize_app';
|
||||||
|
import { VisualizeConstants } from './visualize_constants';
|
||||||
|
import { VisualizeListingController } from './listing/visualize_listing';
|
||||||
|
import { ensureDefaultIndexPattern, registerTimefilterWithGlobalStateFactory } from './legacy_imports';
|
||||||
|
import { syncOnMount } from './global_state_sync';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getLandingBreadcrumbs,
|
||||||
|
getWizardStep1Breadcrumbs,
|
||||||
|
getCreateBreadcrumbs,
|
||||||
|
getEditBreadcrumbs
|
||||||
|
} from './breadcrumbs';
|
||||||
|
|
||||||
|
export function initVisualizeApp(app, deps) {
|
||||||
|
initVisualizeAppDirective(app, deps);
|
||||||
|
|
||||||
|
app.run(globalState => {
|
||||||
|
syncOnMount(globalState, deps.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.run((globalState, $rootScope) => {
|
||||||
|
registerTimefilterWithGlobalStateFactory(
|
||||||
|
deps.data.query.timefilter.timefilter,
|
||||||
|
globalState,
|
||||||
|
$rootScope
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.config(function ($routeProvider) {
|
||||||
|
const defaults = {
|
||||||
|
reloadOnSearch: false,
|
||||||
|
requireUICapability: 'visualize.show',
|
||||||
|
badge: () => {
|
||||||
|
if (deps.visualizeCapabilities.save) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: i18n.translate('kbn.visualize.badge.readOnly.text', {
|
||||||
|
defaultMessage: 'Read only',
|
||||||
|
}),
|
||||||
|
tooltip: i18n.translate('kbn.visualize.badge.readOnly.tooltip', {
|
||||||
|
defaultMessage: 'Unable to save visualizations',
|
||||||
|
}),
|
||||||
|
iconType: 'glasses',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$routeProvider
|
||||||
|
.when(VisualizeConstants.LANDING_PAGE_PATH, {
|
||||||
|
...defaults,
|
||||||
|
template: visualizeListingTemplate,
|
||||||
|
k7Breadcrumbs: getLandingBreadcrumbs,
|
||||||
|
controller: VisualizeListingController,
|
||||||
|
controllerAs: 'listingController',
|
||||||
|
resolve: {
|
||||||
|
createNewVis: () => false,
|
||||||
|
hasDefaultIndex: ($rootScope, kbnUrl) => ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.when(VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, {
|
||||||
|
...defaults,
|
||||||
|
template: visualizeListingTemplate,
|
||||||
|
k7Breadcrumbs: getWizardStep1Breadcrumbs,
|
||||||
|
controller: VisualizeListingController,
|
||||||
|
controllerAs: 'listingController',
|
||||||
|
resolve: {
|
||||||
|
createNewVis: () => true,
|
||||||
|
hasDefaultIndex: ($rootScope, kbnUrl) => ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.when(VisualizeConstants.CREATE_PATH, {
|
||||||
|
...defaults,
|
||||||
|
template: editorTemplate,
|
||||||
|
k7Breadcrumbs: getCreateBreadcrumbs,
|
||||||
|
resolve: {
|
||||||
|
savedVis: function (redirectWhenMissing, $route, $rootScope, kbnUrl) {
|
||||||
|
const { core, data, savedVisualizations, visualizations } = deps;
|
||||||
|
const visTypes = visualizations.types.all();
|
||||||
|
const visType = find(visTypes, { name: $route.current.params.type });
|
||||||
|
const shouldHaveIndex = visType.requiresSearch && visType.options.showIndexSelection;
|
||||||
|
const hasIndex = $route.current.params.indexPattern || $route.current.params.savedSearchId;
|
||||||
|
if (shouldHaveIndex && !hasIndex) {
|
||||||
|
throw new Error(
|
||||||
|
i18n.translate('kbn.visualize.createVisualization.noIndexPatternOrSavedSearchIdErrorMessage', {
|
||||||
|
defaultMessage: 'You must provide either an indexPattern or a savedSearchId',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl).then(() => savedVisualizations.get($route.current.params))
|
||||||
|
.then(savedVis => {
|
||||||
|
if (savedVis.vis.type.setup) {
|
||||||
|
return savedVis.vis.type.setup(savedVis)
|
||||||
|
.catch(() => savedVis);
|
||||||
|
}
|
||||||
|
return savedVis;
|
||||||
|
})
|
||||||
|
.catch(redirectWhenMissing({
|
||||||
|
'*': '/visualize'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.when(`${VisualizeConstants.EDIT_PATH}/:id`, {
|
||||||
|
...defaults,
|
||||||
|
template: editorTemplate,
|
||||||
|
k7Breadcrumbs: getEditBreadcrumbs,
|
||||||
|
resolve: {
|
||||||
|
savedVis: function (redirectWhenMissing, $route, $rootScope, kbnUrl) {
|
||||||
|
const { chrome, core, data, savedVisualizations } = deps;
|
||||||
|
return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl)
|
||||||
|
.then(() => savedVisualizations.get($route.current.params.id))
|
||||||
|
.then(savedVis => {
|
||||||
|
chrome.recentlyAccessed.add(
|
||||||
|
savedVis.getFullPath(),
|
||||||
|
savedVis.title,
|
||||||
|
savedVis.id
|
||||||
|
);
|
||||||
|
return savedVis;
|
||||||
|
})
|
||||||
|
.then(savedVis => {
|
||||||
|
if (savedVis.vis.type.setup) {
|
||||||
|
return savedVis.vis.type.setup(savedVis).catch(() => savedVis);
|
||||||
|
}
|
||||||
|
return savedVis;
|
||||||
|
})
|
||||||
|
.catch(
|
||||||
|
redirectWhenMissing({
|
||||||
|
'visualization': '/visualize',
|
||||||
|
'search': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
|
||||||
|
'index-pattern': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
|
||||||
|
'index-pattern-field': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.when(`visualize/:tail*?`, {
|
||||||
|
redirectTo: `/${deps.core.injectedMetadata.getInjectedVar('kbnDefaultAppId')}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The imports in this file are static functions and types which still live in legacy folders and are used
|
||||||
|
* within dashboard. To consolidate them all in one place, they are re-exported from this file. Eventually
|
||||||
|
* this list should become empty. Imports from the top level of shimmed or moved plugins can be imported
|
||||||
|
* directly where they are needed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import chrome from 'ui/chrome';
|
||||||
|
|
||||||
|
export const legacyChrome = chrome;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export { AppState, AppStateProvider } from 'ui/state_management/app_state';
|
||||||
|
export { State } from 'ui/state_management/state';
|
||||||
|
// @ts-ignore
|
||||||
|
export { GlobalStateProvider } from 'ui/state_management/global_state';
|
||||||
|
// @ts-ignore
|
||||||
|
export { StateManagementConfigProvider } from 'ui/state_management/config_provider';
|
||||||
|
export { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
|
||||||
|
export { PersistedState } from 'ui/persisted_state';
|
||||||
|
|
||||||
|
export { npSetup, npStart } from 'ui/new_platform';
|
||||||
|
export { IPrivate } from 'ui/private';
|
||||||
|
// @ts-ignore
|
||||||
|
export { PrivateProvider } from 'ui/private/private';
|
||||||
|
|
||||||
|
export { SavedObjectRegistryProvider } from 'ui/saved_objects';
|
||||||
|
export { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal';
|
||||||
|
export { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder';
|
||||||
|
export { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal';
|
||||||
|
|
||||||
|
export { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
|
||||||
|
export { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query';
|
||||||
|
// @ts-ignore
|
||||||
|
export { EventsProvider } from 'ui/events';
|
||||||
|
// @ts-ignore
|
||||||
|
export { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav';
|
||||||
|
// @ts-ignore
|
||||||
|
export { PromiseServiceCreator } from 'ui/promises/promises';
|
||||||
|
// @ts-ignore
|
||||||
|
export { confirmModalFactory } from 'ui/modals/confirm_modal';
|
||||||
|
export { configureAppAngularModule, ensureDefaultIndexPattern } from 'ui/legacy_compat';
|
||||||
|
export { registerTimefilterWithGlobalStateFactory } from 'ui/timefilter/setup_router';
|
||||||
|
// @ts-ignore
|
||||||
|
export { VisEditorTypesRegistryProvider } from 'ui/registry/vis_editor_types';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url';
|
||||||
|
export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url';
|
||||||
|
export { KibanaParsedUrl } from 'ui/url/kibana_parsed_url';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export { defaultEditor } from 'ui/vis/editors/default/default';
|
||||||
|
export { VisType } from 'ui/vis';
|
||||||
|
export { wrapInI18nContext } from 'ui/i18n';
|
||||||
|
|
||||||
|
export { VisSavedObject } from './embeddable/visualize_embeddable';
|
|
@ -14,6 +14,8 @@
|
||||||
is-open="listingController.showNewVisModal"
|
is-open="listingController.showNewVisModal"
|
||||||
on-close="listingController.closeNewVisModal"
|
on-close="listingController.closeNewVisModal"
|
||||||
vis-types-registry="listingController.visTypeRegistry"
|
vis-types-registry="listingController.visTypeRegistry"
|
||||||
|
add-base-path="listingController.addBasePath"
|
||||||
|
ui-settings="listingController.uiSettings"
|
||||||
></new-vis-modal>
|
></new-vis-modal>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,36 +24,46 @@ import { VisualizeConstants } from '../visualize_constants';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { getServices } from '../kibana_services';
|
import { getServices } from '../kibana_services';
|
||||||
|
import { wrapInI18nContext } from '../legacy_imports';
|
||||||
|
|
||||||
const {
|
export function initListingDirective(app) {
|
||||||
addBasePath,
|
app.directive('visualizeListingTable', reactDirective => reactDirective(wrapInI18nContext(VisualizeListingTable)));
|
||||||
chrome,
|
app.directive('newVisModal', reactDirective =>
|
||||||
chromeLegacy,
|
reactDirective(wrapInI18nContext(NewVisModal), [
|
||||||
SavedObjectRegistryProvider,
|
['visTypesRegistry', { watchDepth: 'collection' }],
|
||||||
SavedObjectsClientProvider,
|
['onClose', { watchDepth: 'reference' }],
|
||||||
timefilter,
|
['addBasePath', { watchDepth: 'reference' }],
|
||||||
toastNotifications,
|
['uiSettings', { watchDepth: 'reference' }],
|
||||||
uiModules,
|
'isOpen',
|
||||||
wrapInI18nContext,
|
])
|
||||||
visualizations,
|
);
|
||||||
} = getServices();
|
}
|
||||||
|
|
||||||
const app = uiModules.get('app/visualize', ['ngRoute', 'react']);
|
|
||||||
app.directive('visualizeListingTable', reactDirective =>
|
|
||||||
reactDirective(wrapInI18nContext(VisualizeListingTable))
|
|
||||||
);
|
|
||||||
app.directive('newVisModal', reactDirective => reactDirective(wrapInI18nContext(NewVisModal)));
|
|
||||||
|
|
||||||
export function VisualizeListingController($injector, createNewVis) {
|
export function VisualizeListingController($injector, createNewVis) {
|
||||||
const Private = $injector.get('Private');
|
const {
|
||||||
const config = $injector.get('config');
|
addBasePath,
|
||||||
|
chrome,
|
||||||
|
legacyChrome,
|
||||||
|
savedObjectRegistry,
|
||||||
|
savedObjectsClient,
|
||||||
|
data: {
|
||||||
|
query: {
|
||||||
|
timefilter: { timefilter },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
toastNotifications,
|
||||||
|
uiSettings,
|
||||||
|
visualizations,
|
||||||
|
core: { docLinks },
|
||||||
|
} = getServices();
|
||||||
const kbnUrl = $injector.get('kbnUrl');
|
const kbnUrl = $injector.get('kbnUrl');
|
||||||
const savedObjectClient = Private(SavedObjectsClientProvider);
|
|
||||||
|
|
||||||
timefilter.disableAutoRefreshSelector();
|
timefilter.disableAutoRefreshSelector();
|
||||||
timefilter.disableTimeRangeSelector();
|
timefilter.disableTimeRangeSelector();
|
||||||
|
|
||||||
this.showNewVisModal = false;
|
this.showNewVisModal = false;
|
||||||
|
this.addBasePath = addBasePath;
|
||||||
|
this.uiSettings = uiSettings;
|
||||||
|
|
||||||
this.createNewVis = () => {
|
this.createNewVis = () => {
|
||||||
this.showNewVisModal = true;
|
this.showNewVisModal = true;
|
||||||
|
@ -82,14 +92,14 @@ export function VisualizeListingController($injector, createNewVis) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Extract this into an external service.
|
// TODO: Extract this into an external service.
|
||||||
const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName;
|
const services = savedObjectRegistry.byLoaderPropertiesName;
|
||||||
const visualizationService = services.visualizations;
|
const visualizationService = services.visualizations;
|
||||||
this.visTypeRegistry = visualizations.types;
|
this.visTypeRegistry = visualizations.types;
|
||||||
|
|
||||||
this.fetchItems = filter => {
|
this.fetchItems = filter => {
|
||||||
const isLabsEnabled = config.get('visualize:enableLabs');
|
const isLabsEnabled = uiSettings.get('visualize:enableLabs');
|
||||||
return visualizationService
|
return visualizationService
|
||||||
.findListItems(filter, config.get('savedObjects:listingLimit'))
|
.findListItems(filter, uiSettings.get('savedObjects:listingLimit'))
|
||||||
.then(result => {
|
.then(result => {
|
||||||
this.totalItems = result.total;
|
this.totalItems = result.total;
|
||||||
|
|
||||||
|
@ -103,11 +113,11 @@ export function VisualizeListingController($injector, createNewVis) {
|
||||||
this.deleteSelectedItems = function deleteSelectedItems(selectedItems) {
|
this.deleteSelectedItems = function deleteSelectedItems(selectedItems) {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
selectedItems.map(item => {
|
selectedItems.map(item => {
|
||||||
return savedObjectClient.delete(item.savedObjectType, item.id);
|
return savedObjectsClient.delete(item.savedObjectType, item.id);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
chromeLegacy.untrackNavLinksForDeletedSavedObjects(selectedItems.map(item => item.id));
|
legacyChrome.untrackNavLinksForDeletedSavedObjects(selectedItems.map(item => item.id));
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
toastNotifications.addError(error, {
|
toastNotifications.addError(error, {
|
||||||
|
@ -126,7 +136,7 @@ export function VisualizeListingController($injector, createNewVis) {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.listingLimit = config.get('savedObjects:listingLimit');
|
this.listingLimit = uiSettings.get('savedObjects:listingLimit');
|
||||||
|
|
||||||
addHelpMenuToAppChrome(chrome);
|
addHelpMenuToAppChrome(chrome, docLinks);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,22 +27,21 @@ import { EuiIcon, EuiBetaBadge, EuiLink, EuiButton, EuiEmptyPrompt } from '@elas
|
||||||
|
|
||||||
import { getServices } from '../kibana_services';
|
import { getServices } from '../kibana_services';
|
||||||
|
|
||||||
const { capabilities, toastNotifications, uiSettings } = getServices();
|
|
||||||
|
|
||||||
class VisualizeListingTable extends Component {
|
class VisualizeListingTable extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { visualizeCapabilities, uiSettings, toastNotifications } = getServices();
|
||||||
return (
|
return (
|
||||||
<TableListView
|
<TableListView
|
||||||
// we allow users to create visualizations even if they can't save them
|
// we allow users to create visualizations even if they can't save them
|
||||||
// for data exploration purposes
|
// for data exploration purposes
|
||||||
createItem={this.props.createItem}
|
createItem={this.props.createItem}
|
||||||
findItems={this.props.findItems}
|
findItems={this.props.findItems}
|
||||||
deleteItems={capabilities.visualize.delete ? this.props.deleteItems : null}
|
deleteItems={visualizeCapabilities.delete ? this.props.deleteItems : null}
|
||||||
editItem={capabilities.visualize.save ? this.props.editItem : null}
|
editItem={visualizeCapabilities.save ? this.props.editItem : null}
|
||||||
tableColumns={this.getTableColumns()}
|
tableColumns={this.getTableColumns()}
|
||||||
listingLimit={this.props.listingLimit}
|
listingLimit={this.props.listingLimit}
|
||||||
selectable={item => item.canDelete}
|
selectable={item => item.canDelete}
|
||||||
|
|
161
src/legacy/core_plugins/kibana/public/visualize/plugin.ts
Normal file
161
src/legacy/core_plugins/kibana/public/visualize/plugin.ts
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CoreSetup,
|
||||||
|
CoreStart,
|
||||||
|
LegacyCoreStart,
|
||||||
|
Plugin,
|
||||||
|
SavedObjectsClientContract,
|
||||||
|
} from 'kibana/public';
|
||||||
|
|
||||||
|
import { Storage } from '../../../../../plugins/kibana_utils/public';
|
||||||
|
import { DataPublicPluginStart } from '../../../../../plugins/data/public';
|
||||||
|
import { IEmbeddableStart } from '../../../../../plugins/embeddable/public';
|
||||||
|
import { NavigationStart } from '../../../navigation/public';
|
||||||
|
import { SharePluginStart } from '../../../../../plugins/share/public';
|
||||||
|
import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public';
|
||||||
|
import { VisualizationsStart } from '../../../visualizations/public';
|
||||||
|
import { VisualizeEmbeddableFactory } from './embeddable/visualize_embeddable_factory';
|
||||||
|
import { VISUALIZE_EMBEDDABLE_TYPE } from './embeddable/constants';
|
||||||
|
import { VisualizeConstants } from './visualize_constants';
|
||||||
|
import { setServices, VisualizeKibanaServices } from './kibana_services';
|
||||||
|
import {
|
||||||
|
FeatureCatalogueCategory,
|
||||||
|
HomePublicPluginSetup,
|
||||||
|
} from '../../../../../plugins/home/public';
|
||||||
|
import { defaultEditor, VisEditorTypesRegistryProvider } from './legacy_imports';
|
||||||
|
import { SavedVisualizations } from './types';
|
||||||
|
|
||||||
|
export interface LegacyAngularInjectedDependencies {
|
||||||
|
legacyChrome: any;
|
||||||
|
editorTypes: any;
|
||||||
|
savedObjectRegistry: any;
|
||||||
|
savedVisualizations: SavedVisualizations;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VisualizePluginStartDependencies {
|
||||||
|
data: DataPublicPluginStart;
|
||||||
|
embeddables: IEmbeddableStart;
|
||||||
|
navigation: NavigationStart;
|
||||||
|
share: SharePluginStart;
|
||||||
|
visualizations: VisualizationsStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VisualizePluginSetupDependencies {
|
||||||
|
__LEGACY: {
|
||||||
|
getAngularDependencies: () => Promise<LegacyAngularInjectedDependencies>;
|
||||||
|
};
|
||||||
|
home: HomePublicPluginSetup;
|
||||||
|
kibana_legacy: KibanaLegacySetup;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VisualizePlugin implements Plugin {
|
||||||
|
private startDependencies: {
|
||||||
|
data: DataPublicPluginStart;
|
||||||
|
embeddables: IEmbeddableStart;
|
||||||
|
navigation: NavigationStart;
|
||||||
|
savedObjectsClient: SavedObjectsClientContract;
|
||||||
|
share: SharePluginStart;
|
||||||
|
visualizations: VisualizationsStart;
|
||||||
|
} | null = null;
|
||||||
|
|
||||||
|
public async setup(
|
||||||
|
core: CoreSetup,
|
||||||
|
{ home, kibana_legacy, __LEGACY: { getAngularDependencies } }: VisualizePluginSetupDependencies
|
||||||
|
) {
|
||||||
|
kibana_legacy.registerLegacyApp({
|
||||||
|
id: 'visualize',
|
||||||
|
title: 'Visualize',
|
||||||
|
mount: async ({ core: contextCore }, params) => {
|
||||||
|
if (this.startDependencies === null) {
|
||||||
|
throw new Error('not started yet');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
savedObjectsClient,
|
||||||
|
embeddables,
|
||||||
|
navigation,
|
||||||
|
visualizations,
|
||||||
|
data,
|
||||||
|
share,
|
||||||
|
} = this.startDependencies;
|
||||||
|
|
||||||
|
const angularDependencies = await getAngularDependencies();
|
||||||
|
const deps: VisualizeKibanaServices = {
|
||||||
|
...angularDependencies,
|
||||||
|
addBasePath: contextCore.http.basePath.prepend,
|
||||||
|
core: contextCore as LegacyCoreStart,
|
||||||
|
chrome: contextCore.chrome,
|
||||||
|
data,
|
||||||
|
embeddables,
|
||||||
|
getBasePath: core.http.basePath.get,
|
||||||
|
indexPatterns: data.indexPatterns,
|
||||||
|
localStorage: new Storage(localStorage),
|
||||||
|
navigation,
|
||||||
|
savedObjectsClient,
|
||||||
|
savedQueryService: data.query.savedQueries,
|
||||||
|
share,
|
||||||
|
toastNotifications: contextCore.notifications.toasts,
|
||||||
|
uiSettings: contextCore.uiSettings,
|
||||||
|
visualizeCapabilities: contextCore.application.capabilities.visualize,
|
||||||
|
visualizations,
|
||||||
|
};
|
||||||
|
setServices(deps);
|
||||||
|
|
||||||
|
const { renderApp } = await import('./application');
|
||||||
|
return renderApp(params.element, params.appBasePath, deps);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
home.featureCatalogue.register({
|
||||||
|
id: 'visualize',
|
||||||
|
title: 'Visualize',
|
||||||
|
description: i18n.translate('kbn.visualize.visualizeDescription', {
|
||||||
|
defaultMessage:
|
||||||
|
'Create visualizations and aggregate data stores in your Elasticsearch indices.',
|
||||||
|
}),
|
||||||
|
icon: 'visualizeApp',
|
||||||
|
path: `/app/kibana#${VisualizeConstants.LANDING_PAGE_PATH}`,
|
||||||
|
showOnHomePage: true,
|
||||||
|
category: FeatureCatalogueCategory.DATA,
|
||||||
|
});
|
||||||
|
|
||||||
|
VisEditorTypesRegistryProvider.register(defaultEditor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public start(
|
||||||
|
{ savedObjects: { client: savedObjectsClient } }: CoreStart,
|
||||||
|
{ embeddables, navigation, data, share, visualizations }: VisualizePluginStartDependencies
|
||||||
|
) {
|
||||||
|
this.startDependencies = {
|
||||||
|
data,
|
||||||
|
embeddables,
|
||||||
|
navigation,
|
||||||
|
savedObjectsClient,
|
||||||
|
share,
|
||||||
|
visualizations,
|
||||||
|
};
|
||||||
|
|
||||||
|
const embeddableFactory = new VisualizeEmbeddableFactory(visualizations.types);
|
||||||
|
embeddables.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory);
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { VisSavedObject } from './kibana_services';
|
import { VisSavedObject } from './legacy_imports';
|
||||||
|
|
||||||
export interface SavedVisualizations {
|
export interface SavedVisualizations {
|
||||||
urlFor: (id: string) => string;
|
urlFor: (id: string) => string;
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IModule } from 'angular';
|
||||||
|
import { VisualizeKibanaServices } from './kibana_services';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import { initEditorDirective } from './editor/editor';
|
||||||
|
// @ts-ignore
|
||||||
|
import { initListingDirective } from './listing/visualize_listing';
|
||||||
|
|
||||||
|
export function initVisualizeAppDirective(app: IModule, deps: VisualizeKibanaServices) {
|
||||||
|
initEditorDirective(app, deps);
|
||||||
|
initListingDirective(app);
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
exports[`NewVisModal filter for visualization types should render as expected 1`] = `
|
exports[`NewVisModal filter for visualization types should render as expected 1`] = `
|
||||||
<NewVisModal
|
<NewVisModal
|
||||||
|
addBasePath={[Function]}
|
||||||
editorParams={Array []}
|
editorParams={Array []}
|
||||||
intl={
|
intl={
|
||||||
Object {
|
Object {
|
||||||
|
@ -107,6 +108,23 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
||||||
}
|
}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={[Function]}
|
onClose={[Function]}
|
||||||
|
uiSettings={
|
||||||
|
Object {
|
||||||
|
"get": [MockFunction] {
|
||||||
|
"calls": Array [
|
||||||
|
Array [
|
||||||
|
"visualize:enableLabs",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
"results": Array [
|
||||||
|
Object {
|
||||||
|
"type": "return",
|
||||||
|
"value": undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
visTypesRegistry={
|
visTypesRegistry={
|
||||||
Object {
|
Object {
|
||||||
"all": [Function],
|
"all": [Function],
|
||||||
|
@ -865,6 +883,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
||||||
className="euiModal__flex"
|
className="euiModal__flex"
|
||||||
>
|
>
|
||||||
<TypeSelection
|
<TypeSelection
|
||||||
|
addBasePath={[Function]}
|
||||||
onVisTypeSelected={[Function]}
|
onVisTypeSelected={[Function]}
|
||||||
visTypesRegistry={
|
visTypesRegistry={
|
||||||
Object {
|
Object {
|
||||||
|
@ -1235,6 +1254,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
||||||
/>
|
/>
|
||||||
</EuiSpacer>
|
</EuiSpacer>
|
||||||
<NewVisHelp
|
<NewVisHelp
|
||||||
|
addBasePath={[Function]}
|
||||||
promotedTypes={Array []}
|
promotedTypes={Array []}
|
||||||
>
|
>
|
||||||
<EuiText>
|
<EuiText>
|
||||||
|
@ -1287,6 +1307,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
||||||
|
|
||||||
exports[`NewVisModal should render as expected 1`] = `
|
exports[`NewVisModal should render as expected 1`] = `
|
||||||
<NewVisModal
|
<NewVisModal
|
||||||
|
addBasePath={[Function]}
|
||||||
editorParams={Array []}
|
editorParams={Array []}
|
||||||
intl={
|
intl={
|
||||||
Object {
|
Object {
|
||||||
|
@ -1392,6 +1413,23 @@ exports[`NewVisModal should render as expected 1`] = `
|
||||||
}
|
}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={[Function]}
|
onClose={[Function]}
|
||||||
|
uiSettings={
|
||||||
|
Object {
|
||||||
|
"get": [MockFunction] {
|
||||||
|
"calls": Array [
|
||||||
|
Array [
|
||||||
|
"visualize:enableLabs",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
"results": Array [
|
||||||
|
Object {
|
||||||
|
"type": "return",
|
||||||
|
"value": undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
visTypesRegistry={
|
visTypesRegistry={
|
||||||
Object {
|
Object {
|
||||||
"all": [Function],
|
"all": [Function],
|
||||||
|
@ -2141,6 +2179,7 @@ exports[`NewVisModal should render as expected 1`] = `
|
||||||
className="euiModal__flex"
|
className="euiModal__flex"
|
||||||
>
|
>
|
||||||
<TypeSelection
|
<TypeSelection
|
||||||
|
addBasePath={[Function]}
|
||||||
onVisTypeSelected={[Function]}
|
onVisTypeSelected={[Function]}
|
||||||
visTypesRegistry={
|
visTypesRegistry={
|
||||||
Object {
|
Object {
|
||||||
|
@ -2499,6 +2538,7 @@ exports[`NewVisModal should render as expected 1`] = `
|
||||||
/>
|
/>
|
||||||
</EuiSpacer>
|
</EuiSpacer>
|
||||||
<NewVisHelp
|
<NewVisHelp
|
||||||
|
addBasePath={[Function]}
|
||||||
promotedTypes={Array []}
|
promotedTypes={Array []}
|
||||||
>
|
>
|
||||||
<EuiText>
|
<EuiText>
|
||||||
|
|
|
@ -20,33 +20,17 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||||
|
|
||||||
import { NewVisModal } from './new_vis_modal';
|
import { VisType } from '../legacy_imports';
|
||||||
import { VisType } from '../kibana_services';
|
|
||||||
import { TypesStart } from '../../../../visualizations/public/np_ready/public/types';
|
import { TypesStart } from '../../../../visualizations/public/np_ready/public/types';
|
||||||
|
|
||||||
jest.mock('../kibana_services', () => {
|
jest.mock('../legacy_imports', () => ({
|
||||||
const mock = {
|
State: () => null,
|
||||||
addBasePath: jest.fn(path => `root${path}`),
|
AppState: () => null,
|
||||||
uiSettings: { get: jest.fn() },
|
}));
|
||||||
createUiStatsReporter: () => jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
import { NewVisModal } from './new_vis_modal';
|
||||||
getServices: () => mock,
|
|
||||||
VisType: {},
|
|
||||||
METRIC_TYPE: 'metricType',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
import { getServices } from '../kibana_services';
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('NewVisModal', () => {
|
describe('NewVisModal', () => {
|
||||||
const settingsGet = getServices().uiSettings.get as jest.Mock;
|
|
||||||
|
|
||||||
const defaultVisTypeParams = {
|
const defaultVisTypeParams = {
|
||||||
hidden: false,
|
hidden: false,
|
||||||
visualization: class Controller {
|
visualization: class Controller {
|
||||||
|
@ -76,17 +60,36 @@ describe('NewVisModal', () => {
|
||||||
},
|
},
|
||||||
getAliases: () => [],
|
getAliases: () => [],
|
||||||
};
|
};
|
||||||
|
const addBasePath = (url: string) => `testbasepath${url}`;
|
||||||
|
const settingsGet = jest.fn();
|
||||||
|
const uiSettings: any = { get: settingsGet };
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('should render as expected', () => {
|
it('should render as expected', () => {
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
<NewVisModal
|
||||||
|
isOpen={true}
|
||||||
|
onClose={() => null}
|
||||||
|
visTypesRegistry={visTypes}
|
||||||
|
addBasePath={addBasePath}
|
||||||
|
uiSettings={uiSettings}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show a button for regular visualizations', () => {
|
it('should show a button for regular visualizations', () => {
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
<NewVisModal
|
||||||
|
isOpen={true}
|
||||||
|
onClose={() => null}
|
||||||
|
visTypesRegistry={visTypes}
|
||||||
|
addBasePath={addBasePath}
|
||||||
|
uiSettings={uiSettings}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
expect(wrapper.find('[data-test-subj="visType-vis"]').exists()).toBe(true);
|
expect(wrapper.find('[data-test-subj="visType-vis"]').exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
@ -95,7 +98,13 @@ describe('NewVisModal', () => {
|
||||||
it('should open the editor for visualizations without search', () => {
|
it('should open the editor for visualizations without search', () => {
|
||||||
window.location.assign = jest.fn();
|
window.location.assign = jest.fn();
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
<NewVisModal
|
||||||
|
isOpen={true}
|
||||||
|
onClose={() => null}
|
||||||
|
visTypesRegistry={visTypes}
|
||||||
|
addBasePath={addBasePath}
|
||||||
|
uiSettings={uiSettings}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
const visButton = wrapper.find('button[data-test-subj="visType-vis"]');
|
const visButton = wrapper.find('button[data-test-subj="visType-vis"]');
|
||||||
visButton.simulate('click');
|
visButton.simulate('click');
|
||||||
|
@ -110,6 +119,8 @@ describe('NewVisModal', () => {
|
||||||
onClose={() => null}
|
onClose={() => null}
|
||||||
visTypesRegistry={visTypes}
|
visTypesRegistry={visTypes}
|
||||||
editorParams={['foo=true', 'bar=42']}
|
editorParams={['foo=true', 'bar=42']}
|
||||||
|
addBasePath={addBasePath}
|
||||||
|
uiSettings={uiSettings}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
const visButton = wrapper.find('button[data-test-subj="visType-vis"]');
|
const visButton = wrapper.find('button[data-test-subj="visType-vis"]');
|
||||||
|
@ -121,7 +132,13 @@ describe('NewVisModal', () => {
|
||||||
describe('filter for visualization types', () => {
|
describe('filter for visualization types', () => {
|
||||||
it('should render as expected', () => {
|
it('should render as expected', () => {
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
<NewVisModal
|
||||||
|
isOpen={true}
|
||||||
|
onClose={() => null}
|
||||||
|
visTypesRegistry={visTypes}
|
||||||
|
addBasePath={addBasePath}
|
||||||
|
uiSettings={uiSettings}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
const searchBox = wrapper.find('input[data-test-subj="filterVisType"]');
|
const searchBox = wrapper.find('input[data-test-subj="filterVisType"]');
|
||||||
searchBox.simulate('change', { target: { value: 'with' } });
|
searchBox.simulate('change', { target: { value: 'with' } });
|
||||||
|
@ -133,7 +150,13 @@ describe('NewVisModal', () => {
|
||||||
it('should not show experimental visualizations if visualize:enableLabs is false', () => {
|
it('should not show experimental visualizations if visualize:enableLabs is false', () => {
|
||||||
settingsGet.mockReturnValue(false);
|
settingsGet.mockReturnValue(false);
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
<NewVisModal
|
||||||
|
isOpen={true}
|
||||||
|
onClose={() => null}
|
||||||
|
visTypesRegistry={visTypes}
|
||||||
|
addBasePath={addBasePath}
|
||||||
|
uiSettings={uiSettings}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(false);
|
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(false);
|
||||||
});
|
});
|
||||||
|
@ -141,7 +164,13 @@ describe('NewVisModal', () => {
|
||||||
it('should show experimental visualizations if visualize:enableLabs is true', () => {
|
it('should show experimental visualizations if visualize:enableLabs is true', () => {
|
||||||
settingsGet.mockReturnValue(true);
|
settingsGet.mockReturnValue(true);
|
||||||
const wrapper = mountWithIntl(
|
const wrapper = mountWithIntl(
|
||||||
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
|
<NewVisModal
|
||||||
|
isOpen={true}
|
||||||
|
onClose={() => null}
|
||||||
|
visTypesRegistry={visTypes}
|
||||||
|
addBasePath={addBasePath}
|
||||||
|
uiSettings={uiSettings}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(true);
|
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,20 +22,21 @@ import React from 'react';
|
||||||
import { EuiModal, EuiOverlayMask } from '@elastic/eui';
|
import { EuiModal, EuiOverlayMask } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
|
import { IUiSettingsClient } from 'kibana/public';
|
||||||
|
import { VisType } from '../legacy_imports';
|
||||||
import { VisualizeConstants } from '../visualize_constants';
|
import { VisualizeConstants } from '../visualize_constants';
|
||||||
|
import { createUiStatsReporter, METRIC_TYPE } from '../../../../ui_metric/public';
|
||||||
import { SearchSelection } from './search_selection';
|
import { SearchSelection } from './search_selection';
|
||||||
import { TypeSelection } from './type_selection';
|
import { TypeSelection } from './type_selection';
|
||||||
import { TypesStart, VisTypeAlias } from '../../../../visualizations/public/np_ready/public/types';
|
import { TypesStart, VisTypeAlias } from '../../../../visualizations/public/np_ready/public/types';
|
||||||
|
|
||||||
import { getServices, METRIC_TYPE, VisType } from '../kibana_services';
|
|
||||||
|
|
||||||
const { addBasePath, createUiStatsReporter, uiSettings } = getServices();
|
|
||||||
|
|
||||||
interface TypeSelectionProps {
|
interface TypeSelectionProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
visTypesRegistry: TypesStart;
|
visTypesRegistry: TypesStart;
|
||||||
editorParams?: string[];
|
editorParams?: string[];
|
||||||
|
addBasePath: (path: string) => string;
|
||||||
|
uiSettings: IUiSettingsClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TypeSelectionState {
|
interface TypeSelectionState {
|
||||||
|
@ -55,7 +56,7 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
|
||||||
|
|
||||||
constructor(props: TypeSelectionProps) {
|
constructor(props: TypeSelectionProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.isLabsEnabled = uiSettings.get('visualize:enableLabs');
|
this.isLabsEnabled = props.uiSettings.get('visualize:enableLabs');
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showSearchVisModal: false,
|
showSearchVisModal: false,
|
||||||
|
@ -93,6 +94,7 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
|
||||||
showExperimental={this.isLabsEnabled}
|
showExperimental={this.isLabsEnabled}
|
||||||
onVisTypeSelected={this.onVisTypeSelected}
|
onVisTypeSelected={this.onVisTypeSelected}
|
||||||
visTypesRegistry={this.props.visTypesRegistry}
|
visTypesRegistry={this.props.visTypesRegistry}
|
||||||
|
addBasePath={this.props.addBasePath}
|
||||||
/>
|
/>
|
||||||
</EuiModal>
|
</EuiModal>
|
||||||
);
|
);
|
||||||
|
@ -124,7 +126,7 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
|
||||||
this.trackUiMetric(METRIC_TYPE.CLICK, visType.name);
|
this.trackUiMetric(METRIC_TYPE.CLICK, visType.name);
|
||||||
|
|
||||||
if ('aliasUrl' in visType) {
|
if ('aliasUrl' in visType) {
|
||||||
window.location.href = addBasePath(visType.aliasUrl);
|
window.location.href = this.props.addBasePath(visType.aliasUrl);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,7 @@ import { i18n } from '@kbn/i18n';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder';
|
import { SavedObjectFinder, VisType } from '../../legacy_imports';
|
||||||
|
|
||||||
import { VisType } from '../../kibana_services';
|
|
||||||
|
|
||||||
interface SearchSelectionProps {
|
interface SearchSelectionProps {
|
||||||
onSearchSelected: (searchId: string, searchType: string) => void;
|
onSearchSelected: (searchId: string, searchType: string) => void;
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import { I18nContext } from 'ui/i18n';
|
import { I18nProvider } from '@kbn/i18n/react';
|
||||||
|
import { IUiSettingsClient } from 'kibana/public';
|
||||||
import { NewVisModal } from './new_vis_modal';
|
import { NewVisModal } from './new_vis_modal';
|
||||||
import { TypesStart } from '../../../../visualizations/public/np_ready/public/types';
|
import { TypesStart } from '../../../../visualizations/public/np_ready/public/types';
|
||||||
|
|
||||||
|
@ -30,7 +31,9 @@ interface ShowNewVisModalParams {
|
||||||
|
|
||||||
export function showNewVisModal(
|
export function showNewVisModal(
|
||||||
visTypeRegistry: TypesStart,
|
visTypeRegistry: TypesStart,
|
||||||
{ editorParams = [] }: ShowNewVisModalParams = {}
|
{ editorParams = [] }: ShowNewVisModalParams = {},
|
||||||
|
addBasePath: (path: string) => string,
|
||||||
|
uiSettings: IUiSettingsClient
|
||||||
) {
|
) {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
|
@ -40,14 +43,16 @@ export function showNewVisModal(
|
||||||
|
|
||||||
document.body.appendChild(container);
|
document.body.appendChild(container);
|
||||||
const element = (
|
const element = (
|
||||||
<I18nContext>
|
<I18nProvider>
|
||||||
<NewVisModal
|
<NewVisModal
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
visTypesRegistry={visTypeRegistry}
|
visTypesRegistry={visTypeRegistry}
|
||||||
editorParams={editorParams}
|
editorParams={editorParams}
|
||||||
|
addBasePath={addBasePath}
|
||||||
|
uiSettings={uiSettings}
|
||||||
/>
|
/>
|
||||||
</I18nContext>
|
</I18nProvider>
|
||||||
);
|
);
|
||||||
ReactDOM.render(element, container);
|
ReactDOM.render(element, container);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,18 +21,6 @@ import React from 'react';
|
||||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||||
import { NewVisHelp } from './new_vis_help';
|
import { NewVisHelp } from './new_vis_help';
|
||||||
|
|
||||||
jest.mock('../../kibana_services', () => {
|
|
||||||
return {
|
|
||||||
getServices: () => ({
|
|
||||||
addBasePath: jest.fn((url: string) => `testbasepath${url}`),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('NewVisHelp', () => {
|
describe('NewVisHelp', () => {
|
||||||
it('should render as expected', () => {
|
it('should render as expected', () => {
|
||||||
expect(
|
expect(
|
||||||
|
@ -53,6 +41,7 @@ describe('NewVisHelp', () => {
|
||||||
stage: 'production',
|
stage: 'production',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
addBasePath={(url: string) => `testbasepath${url}`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
|
|
|
@ -22,10 +22,9 @@ import React, { Fragment } from 'react';
|
||||||
import { EuiText, EuiButton } from '@elastic/eui';
|
import { EuiText, EuiButton } from '@elastic/eui';
|
||||||
import { VisTypeAliasListEntry } from './type_selection';
|
import { VisTypeAliasListEntry } from './type_selection';
|
||||||
|
|
||||||
import { getServices } from '../../kibana_services';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
promotedTypes: VisTypeAliasListEntry[];
|
promotedTypes: VisTypeAliasListEntry[];
|
||||||
|
addBasePath: (path: string) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NewVisHelp(props: Props) {
|
export function NewVisHelp(props: Props) {
|
||||||
|
@ -43,7 +42,7 @@ export function NewVisHelp(props: Props) {
|
||||||
<strong>{t.promotion!.description}</strong>
|
<strong>{t.promotion!.description}</strong>
|
||||||
</p>
|
</p>
|
||||||
<EuiButton
|
<EuiButton
|
||||||
href={getServices().addBasePath(t.aliasUrl)}
|
href={props.addBasePath(t.aliasUrl)}
|
||||||
fill
|
fill
|
||||||
size="s"
|
size="s"
|
||||||
iconType="popout"
|
iconType="popout"
|
||||||
|
|
|
@ -34,8 +34,8 @@ import {
|
||||||
EuiSpacer,
|
EuiSpacer,
|
||||||
EuiTitle,
|
EuiTitle,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
import { VisType } from '../../legacy_imports';
|
||||||
import { memoizeLast } from '../../../../../visualizations/public/np_ready/public/legacy/memoize';
|
import { memoizeLast } from '../../../../../visualizations/public/np_ready/public/legacy/memoize';
|
||||||
import { VisType } from '../../kibana_services';
|
|
||||||
import { VisTypeAlias } from '../../../../../visualizations/public';
|
import { VisTypeAlias } from '../../../../../visualizations/public';
|
||||||
import { NewVisHelp } from './new_vis_help';
|
import { NewVisHelp } from './new_vis_help';
|
||||||
import { VisHelpText } from './vis_help_text';
|
import { VisHelpText } from './vis_help_text';
|
||||||
|
@ -51,6 +51,7 @@ export interface VisTypeAliasListEntry extends VisTypeAlias {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TypeSelectionProps {
|
interface TypeSelectionProps {
|
||||||
|
addBasePath: (path: string) => string;
|
||||||
onVisTypeSelected: (visType: VisType | VisTypeAlias) => void;
|
onVisTypeSelected: (visType: VisType | VisTypeAlias) => void;
|
||||||
visTypesRegistry: TypesStart;
|
visTypesRegistry: TypesStart;
|
||||||
showExperimental: boolean;
|
showExperimental: boolean;
|
||||||
|
@ -153,6 +154,7 @@ class TypeSelection extends React.Component<TypeSelectionProps, TypeSelectionSta
|
||||||
<EuiSpacer size="m" />
|
<EuiSpacer size="m" />
|
||||||
<NewVisHelp
|
<NewVisHelp
|
||||||
promotedTypes={(visTypes as VisTypeAliasListEntry[]).filter(t => t.promotion)}
|
promotedTypes={(visTypes as VisTypeAliasListEntry[]).filter(t => t.promotion)}
|
||||||
|
addBasePath={this.props.addBasePath}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -33,12 +33,11 @@ import { keyCodes } from '@elastic/eui';
|
||||||
import { parentPipelineAggHelper } from 'ui/agg_types/metrics/lib/parent_pipeline_agg_helper';
|
import { parentPipelineAggHelper } from 'ui/agg_types/metrics/lib/parent_pipeline_agg_helper';
|
||||||
import { DefaultEditorSize } from '../../editor_size';
|
import { DefaultEditorSize } from '../../editor_size';
|
||||||
|
|
||||||
import { VisEditorTypesRegistryProvider } from '../../../registry/vis_editor_types';
|
|
||||||
import { AggGroupNames } from './agg_groups';
|
import { AggGroupNames } from './agg_groups';
|
||||||
|
|
||||||
import { start as embeddables } from '../../../../../core_plugins/embeddable_api/public/np_ready/public/legacy';
|
import { start as embeddables } from '../../../../../core_plugins/embeddable_api/public/np_ready/public/legacy';
|
||||||
|
|
||||||
const defaultEditor = function ($rootScope, $compile, getAppState) {
|
const defaultEditor = function ($rootScope, $compile) {
|
||||||
return class DefaultEditor {
|
return class DefaultEditor {
|
||||||
static key = 'default';
|
static key = 'default';
|
||||||
|
|
||||||
|
@ -58,7 +57,7 @@ const defaultEditor = function ($rootScope, $compile, getAppState) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render({ uiState, timeRange, filters, query }) {
|
render({ uiState, timeRange, filters, query, appState }) {
|
||||||
let $scope;
|
let $scope;
|
||||||
|
|
||||||
const updateScope = () => {
|
const updateScope = () => {
|
||||||
|
@ -161,7 +160,7 @@ const defaultEditor = function ($rootScope, $compile, getAppState) {
|
||||||
|
|
||||||
this._handler = await embeddables.getEmbeddableFactory('visualization').createFromObject(this.savedObj, {
|
this._handler = await embeddables.getEmbeddableFactory('visualization').createFromObject(this.savedObj, {
|
||||||
uiState: uiState,
|
uiState: uiState,
|
||||||
appState: getAppState(),
|
appState,
|
||||||
timeRange: timeRange,
|
timeRange: timeRange,
|
||||||
filters: filters || [],
|
filters: filters || [],
|
||||||
query: query,
|
query: query,
|
||||||
|
@ -195,6 +194,4 @@ const defaultEditor = function ($rootScope, $compile, getAppState) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
VisEditorTypesRegistryProvider.register(defaultEditor);
|
|
||||||
|
|
||||||
export { defaultEditor };
|
export { defaultEditor };
|
||||||
|
|
|
@ -50,12 +50,6 @@ export const UI_EXPORT_DEFAULTS = {
|
||||||
fieldFormatEditors: [
|
fieldFormatEditors: [
|
||||||
'ui/field_editor/components/field_format_editor/register'
|
'ui/field_editor/components/field_format_editor/register'
|
||||||
],
|
],
|
||||||
visEditorTypes: [
|
|
||||||
'ui/vis/editors/default/default',
|
|
||||||
],
|
|
||||||
embeddableFactories: [
|
|
||||||
'plugins/kibana/visualize/embeddable/visualize_embeddable_factory',
|
|
||||||
],
|
|
||||||
search: [
|
search: [
|
||||||
'ui/courier/search_strategy/default_search_strategy',
|
'ui/courier/search_strategy/default_search_strategy',
|
||||||
],
|
],
|
||||||
|
|
|
@ -269,7 +269,6 @@ module.exports = function (grunt) {
|
||||||
'--config', 'test/plugin_functional/config.js',
|
'--config', 'test/plugin_functional/config.js',
|
||||||
'--bail',
|
'--bail',
|
||||||
'--debug',
|
'--debug',
|
||||||
'--kibana-install-dir', KIBANA_INSTALL_DIR,
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import 'ui/autoload/all';
|
||||||
import 'uiExports/visTypes';
|
import 'uiExports/visTypes';
|
||||||
import 'uiExports/visResponseHandlers';
|
import 'uiExports/visResponseHandlers';
|
||||||
import 'uiExports/visRequestHandlers';
|
import 'uiExports/visRequestHandlers';
|
||||||
import 'uiExports/visEditorTypes';
|
|
||||||
import 'uiExports/savedObjectTypes';
|
import 'uiExports/savedObjectTypes';
|
||||||
import 'uiExports/spyModes';
|
import 'uiExports/spyModes';
|
||||||
import 'uiExports/embeddableFactories';
|
import 'uiExports/embeddableFactories';
|
||||||
|
|
|
@ -18,7 +18,6 @@ import 'uiExports/contextMenuActions';
|
||||||
import 'uiExports/visTypes';
|
import 'uiExports/visTypes';
|
||||||
import 'uiExports/visResponseHandlers';
|
import 'uiExports/visResponseHandlers';
|
||||||
import 'uiExports/visRequestHandlers';
|
import 'uiExports/visRequestHandlers';
|
||||||
import 'uiExports/visEditorTypes';
|
|
||||||
import 'uiExports/inspectorViews';
|
import 'uiExports/inspectorViews';
|
||||||
import 'uiExports/interpreter';
|
import 'uiExports/interpreter';
|
||||||
import 'uiExports/savedObjectTypes';
|
import 'uiExports/savedObjectTypes';
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue