[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:
Maryia Lapata 2019-12-10 13:50:45 +03:00 committed by GitHub
parent 23edb41739
commit 33989b0805
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1113 additions and 474 deletions

View file

@ -62,6 +62,7 @@ export default function (kibana) {
hacks: [
'plugins/kibana/discover',
'plugins/kibana/dev_tools',
'plugins/kibana/visualize',
],
savedObjectTypes: [
'plugins/kibana/visualize/saved_visualizations/saved_visualization_register',

View file

@ -35,7 +35,7 @@
-->
<kbn-top-nav
ng-if="showFilterBar() && !isVisible"
app-name="'dashboard'"
show-search-bar="true"
show-filter-bar="true"

View file

@ -29,7 +29,6 @@ import { npSetup } from 'ui/new_platform';
import 'uiExports/home';
import 'uiExports/visTypes';
import 'uiExports/visEditorTypes';
import 'uiExports/visualize';
import 'uiExports/savedObjectTypes';
import 'uiExports/fieldFormatEditors';

View 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);
}

View file

@ -39,7 +39,7 @@
show-search-bar="true"
show-query-bar="true"
show-query-input="showQueryInput()"
show-filter-bar="showFilterBar() && chrome.getVisible()"
show-filter-bar="showFilterBar() && isVisible"
show-date-picker="showQueryBarTimePicker()"
show-auto-refresh-only="!showQueryBarTimePicker()"
query="state.query"
@ -67,7 +67,7 @@
-->
<kbn-top-nav
ng-if="showFilterBar() && !isVisible"
app-name="'visualize'"
show-search-bar="true"
show-filter-bar="true"
@ -91,7 +91,7 @@
</div>
<visualization-embedded
ng-if="!chrome.getVisible()"
ng-if="!isVisible"
class="visualize"
saved-obj="savedVis"
ui-state="uiState"
@ -110,7 +110,7 @@
>
</h1>
<visualization-editor
ng-if="chrome.getVisible()"
ng-if="isVisible"
saved-obj="savedVis"
ui-state="uiState"
time-range="timeRange"

View file

@ -17,27 +17,27 @@
* under the License.
*/
import angular from 'angular';
import _ from 'lodash';
import { Subscription } from 'rxjs';
import { i18n } from '@kbn/i18n';
import '../saved_visualizations/saved_visualizations';
import './visualization_editor';
import './visualization';
import { ensureDefaultIndexPattern } from 'ui/legacy_compat';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { migrateAppState } from './lib';
import editorTemplate from './editor.html';
import { DashboardConstants } from '../../dashboard/dashboard_constants';
import { VisualizeConstants } from '../visualize_constants';
import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs';
import { getEditBreadcrumbs } from '../breadcrumbs';
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 {
getServices,
angular,
absoluteToParsedUrl,
KibanaParsedUrl,
migrateLegacyQuery,
@ -45,108 +45,24 @@ import {
showSaveModal,
stateMonitorFactory,
subscribeWithScope,
unhashUrl
} from '../kibana_services';
} from '../legacy_imports';
const {
core,
capabilities,
chrome,
chromeLegacy,
npData,
docTitle,
FilterBarQueryFilterProvider,
getBasePath,
toastNotifications,
uiModules,
uiRoutes,
visualizations,
share,
} = getServices();
import { getServices } from '../kibana_services';
const savedQueryService = npData.query.savedQueries;
const { timefilter } = npData.query.timefilter;
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 () {
export function initEditorDirective(app, deps) {
app.directive('visualizeApp', function () {
return {
restrict: 'E',
controllerAs: 'visualizeApp',
controller: VisEditor,
controller: VisualizeAppController,
};
});
function VisEditor(
initVisEditorDirective(app, deps);
initVisualizationDirective(app, deps);
}
function VisualizeAppController(
$scope,
$element,
$route,
@ -154,19 +70,42 @@ function VisEditor(
$window,
$injector,
$timeout,
indexPatterns,
kbnUrl,
redirectWhenMissing,
Private,
Promise,
config,
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.
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.
// SearchSource is a promise-based stream of search results that can inherit from other search sources.
const { vis, searchSource } = savedVis;
@ -177,7 +116,7 @@ function VisEditor(
dirty: !savedVis.id
};
$scope.topNavMenu = [...(capabilities.visualize.save ? [{
$scope.topNavMenu = [...(visualizeCapabilities.save ? [{
id: 'save',
label: i18n.translate('kbn.topNavMenu.saveVisualizationButtonLabel', { defaultMessage: 'save' }),
description: i18n.translate('kbn.visualize.topNavMenu.saveVisualizationButtonAriaLabel', {
@ -246,7 +185,7 @@ function VisEditor(
share.toggleShareContextMenu({
anchorElement,
allowEmbed: true,
allowShortUrl: capabilities.visualize.createShortUrl,
allowShortUrl: visualizeCapabilities.createShortUrl,
shareableUrl: unhashUrl(window.location.href),
objectId: savedVis.id,
objectType: 'visualization',
@ -295,7 +234,7 @@ function VisEditor(
let stateMonitor;
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.
@ -306,7 +245,7 @@ function VisEditor(
linked: !!savedVis.savedSearchId,
query: searchSource.getOwnField('query') || {
query: '',
language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage')
language: localStorage.get('kibana.userQueryLanguage') || uiSettings.get('search:queryLanguage')
},
filters: searchSource.getOwnField('filter') || [],
vis: savedVisState
@ -345,9 +284,9 @@ function VisEditor(
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;
});
@ -455,13 +394,15 @@ function VisEditor(
}
}));
$scope.$on('$destroy', function () {
$scope.$on('$destroy', () => {
if ($scope._handler) {
$scope._handler.destroy();
}
savedVis.destroy();
stateMonitor.destroy();
filterStateManager.destroy();
subscriptions.unsubscribe();
$scope.vis.off('apply', _applyVis);
});
@ -503,7 +444,7 @@ function VisEditor(
delete $state.savedQuery;
$state.query = {
query: '',
language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage')
language: localStorage.get('kibana.userQueryLanguage') || uiSettings.get('search:queryLanguage')
};
queryFilter.removeAll();
$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
// 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.
chromeLegacy.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl);
legacyChrome.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl);
const lastDashboardAbsoluteUrl = chrome.navLinks.get('kibana:dashboard').url;
const dashboardParsedUrl = absoluteToParsedUrl(lastDashboardAbsoluteUrl, getBasePath());
dashboardParsedUrl.addQueryParameter(DashboardConstants.NEW_VISUALIZATION_ID_PARAM, savedVis.id);
kbnUrl.change(dashboardParsedUrl.appPath);
} else if (savedVis.id === $route.current.params.id) {
docTitle.change(savedVis.lastSavedTitle);
chrome.docTitle.change(savedVis.lastSavedTitle);
chrome.setBreadcrumbs($injector.invoke(getEditBreadcrumbs));
savedVis.vis.title = savedVis.title;
savedVis.vis.description = savedVis.description;
@ -661,7 +602,7 @@ function VisEditor(
vis.type.feedbackMessage;
};
addHelpMenuToAppChrome(chrome);
addHelpMenuToAppChrome(chrome, docLinks);
init();
}

View file

@ -17,13 +17,8 @@
* under the License.
*/
import { getServices } from '../kibana_services';
const { embeddables, uiModules } = getServices();
uiModules
.get('kibana/directive', ['ngSanitize'])
.directive('visualizationEmbedded', function (Private, $timeout, getAppState) {
export function initVisualizationDirective(app, deps) {
app.directive('visualizationEmbedded', function ($timeout, getAppState) {
return {
restrict: 'E',
@ -37,7 +32,7 @@ uiModules
link: function ($scope, element) {
$scope.renderFunction = async () => {
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,
filters: $scope.filters || [],
query: $scope.query,
@ -66,3 +61,4 @@ uiModules
}
};
});
}

View file

@ -17,15 +17,8 @@
* under the License.
*/
import { getServices, VisEditorTypesRegistryProvider } from '../kibana_services';
const { uiModules } = getServices();
uiModules
.get('kibana/directive', ['ngSanitize'])
.directive('visualizationEditor', function (Private, $timeout, getAppState) {
const editorTypes = Private(VisEditorTypesRegistryProvider);
export function initVisEditorDirective(app, deps) {
app.directive('visualizationEditor', function ($timeout, getAppState) {
return {
restrict: 'E',
scope: {
@ -38,7 +31,8 @@ uiModules
link: function ($scope, element) {
const editorType = $scope.savedObj.vis.type.editor;
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);
$scope.renderFunction = () => {
@ -62,3 +56,4 @@ uiModules
}
};
});
}

View file

@ -19,8 +19,8 @@
import React from 'react';
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 { VisualizeInput } from './visualize_embeddable';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';

View file

@ -17,11 +17,10 @@
* under the License.
*/
import { getServices, getFromSavedObject, VisSavedObject } from '../kibana_services';
import { npStart } from 'ui/new_platform';
import { IIndexPattern } from '../../../../../../plugins/data/public';
const { savedObjectsClient, uiSettings } = getServices();
import { VisSavedObject } from './visualize_embeddable';
import { indexPatterns, IIndexPattern } from '../../../../../../plugins/data/public';
export async function getIndexPattern(
savedVis: VisSavedObject
@ -30,7 +29,8 @@ export async function getIndexPattern(
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) {
const indexPatternObjects = await savedObjectsClient.find({
@ -39,10 +39,10 @@ export async function getIndexPattern(
search: `"${savedVis.vis.params.index_pattern}"`,
searchFields: ['title'],
});
const [indexPattern] = indexPatternObjects.savedObjects.map(getFromSavedObject);
const [indexPattern] = indexPatternObjects.savedObjects.map(indexPatterns.getFromSavedObject);
return indexPattern;
}
const savedObject = await savedObjectsClient.get('index-pattern', defaultIndex);
return getFromSavedObject(savedObject);
return indexPatterns.getFromSavedObject(savedObject);
}

View file

@ -375,6 +375,8 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
if (this.handler) {
this.handler.update(this.expression, expressionParams);
}
this.vis.emit('apply');
}
private handleVisUpdate = async () => {

View file

@ -17,37 +17,49 @@
* 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 chrome from 'ui/chrome';
import { npStart } from 'ui/new_platform';
import { Legacy } from 'kibana';
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 { SavedVisualizations } from '../types';
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
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 { 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 {
visState: string;
}
@ -96,7 +108,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
if (!visType) {
return false;
}
if (uiSettings.get('visualize:enableLabs')) {
if (npStart.core.uiSettings.get('visualize:enableLabs')) {
return true;
}
return visType.stage !== 'experimental';
@ -108,7 +120,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
}
public isEditable() {
return capabilities.visualize.save as boolean;
return npStart.core.application.capabilities.visualize.save as boolean;
}
public getDisplayName() {
@ -122,14 +134,16 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
input: Partial<VisualizeInput> & { id: string },
parent?: Container
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
const $injector = await getInjector();
const $injector = await chrome.dangerouslyGetActiveInjector();
const config = $injector.get<Legacy.KibanaConfig>('config');
const savedVisualizations = $injector.get<SavedVisualizations>('savedVisualizations');
try {
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');
if (!isLabsEnabled && savedObject.vis.type.stage === 'experimental') {
@ -161,7 +175,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
input: Partial<VisualizeInput> & { id: string },
parent?: Container
): Promise<VisualizeEmbeddable | ErrorEmbeddable | DisabledLabEmbeddable> {
const $injector = await getInjector();
const $injector = await chrome.dangerouslyGetActiveInjector();
const savedVisualizations = $injector.get<SavedVisualizations>('savedVisualizations');
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
// to allow for in place creation of visualizations without having to navigate away to a new URL.
if (this.visTypes) {
showNewVisModal(this.visTypes, {
editorParams: ['addToDashboard'],
});
showNewVisModal(
this.visTypes,
{
editorParams: ['addToDashboard'],
},
npStart.core.http.basePath.prepend,
npStart.core.uiSettings
);
}
return undefined;
}
}
VisualizeEmbeddableFactory.createVisualizeEmbeddableFactory().then(embeddableFactory => {
embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory);
});

View file

@ -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();
}

View file

@ -18,10 +18,8 @@
*/
import { i18n } from '@kbn/i18n';
import { getServices } from '../kibana_services';
const { docLinks } = getServices();
export function addHelpMenuToAppChrome(chrome) {
export function addHelpMenuToAppChrome(chrome, docLinks) {
chrome.setHelpExtension({
appName: i18n.translate('kbn.visualize.helpMenu.appName', {
defaultMessage: 'Visualize',

View file

@ -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
};
});

View 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,
});
})();

View file

@ -17,108 +17,59 @@
* under the License.
*/
import 'angular-sanitize'; // used in visualization_editor.js and visualization.js
import 'ui/collapsible_sidebar'; // used in default editor
import 'ui/vis/editors/default/sidebar';
// load directives
import '../../../data/public';
import {
ChromeStart,
LegacyCoreStart,
SavedObjectsClientContract,
ToastsStart,
IUiSettingsClient,
} from 'kibana/public';
import { npStart } from 'ui/new_platform';
import angular from 'angular'; // just used in editor.js
import chromeLegacy from 'ui/chrome';
import { NavigationStart } from '../../../navigation/public';
import { Storage } from '../../../../../plugins/kibana_utils/public';
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
import { docTitle } from 'ui/doc_title';
import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter';
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,
};
let services: VisualizeKibanaServices | null = null;
export function setServices(newServices: VisualizeKibanaServices) {
services = newServices;
}
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;
}
// export legacy static dependencies
export { angular };
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';
export function clearServices() {
services = null;
}

View 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')}`,
});
});
}

View file

@ -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';

View file

@ -14,6 +14,8 @@
is-open="listingController.showNewVisModal"
on-close="listingController.closeNewVisModal"
vis-types-registry="listingController.visTypeRegistry"
add-base-path="listingController.addBasePath"
ui-settings="listingController.uiSettings"
></new-vis-modal>
</div>

View file

@ -24,36 +24,46 @@ import { VisualizeConstants } from '../visualize_constants';
import { i18n } from '@kbn/i18n';
import { getServices } from '../kibana_services';
import { wrapInI18nContext } from '../legacy_imports';
const {
addBasePath,
chrome,
chromeLegacy,
SavedObjectRegistryProvider,
SavedObjectsClientProvider,
timefilter,
toastNotifications,
uiModules,
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 initListingDirective(app) {
app.directive('visualizeListingTable', reactDirective => reactDirective(wrapInI18nContext(VisualizeListingTable)));
app.directive('newVisModal', reactDirective =>
reactDirective(wrapInI18nContext(NewVisModal), [
['visTypesRegistry', { watchDepth: 'collection' }],
['onClose', { watchDepth: 'reference' }],
['addBasePath', { watchDepth: 'reference' }],
['uiSettings', { watchDepth: 'reference' }],
'isOpen',
])
);
}
export function VisualizeListingController($injector, createNewVis) {
const Private = $injector.get('Private');
const config = $injector.get('config');
const {
addBasePath,
chrome,
legacyChrome,
savedObjectRegistry,
savedObjectsClient,
data: {
query: {
timefilter: { timefilter },
},
},
toastNotifications,
uiSettings,
visualizations,
core: { docLinks },
} = getServices();
const kbnUrl = $injector.get('kbnUrl');
const savedObjectClient = Private(SavedObjectsClientProvider);
timefilter.disableAutoRefreshSelector();
timefilter.disableTimeRangeSelector();
this.showNewVisModal = false;
this.addBasePath = addBasePath;
this.uiSettings = uiSettings;
this.createNewVis = () => {
this.showNewVisModal = true;
@ -82,14 +92,14 @@ export function VisualizeListingController($injector, createNewVis) {
}
// TODO: Extract this into an external service.
const services = Private(SavedObjectRegistryProvider).byLoaderPropertiesName;
const services = savedObjectRegistry.byLoaderPropertiesName;
const visualizationService = services.visualizations;
this.visTypeRegistry = visualizations.types;
this.fetchItems = filter => {
const isLabsEnabled = config.get('visualize:enableLabs');
const isLabsEnabled = uiSettings.get('visualize:enableLabs');
return visualizationService
.findListItems(filter, config.get('savedObjects:listingLimit'))
.findListItems(filter, uiSettings.get('savedObjects:listingLimit'))
.then(result => {
this.totalItems = result.total;
@ -103,11 +113,11 @@ export function VisualizeListingController($injector, createNewVis) {
this.deleteSelectedItems = function deleteSelectedItems(selectedItems) {
return Promise.all(
selectedItems.map(item => {
return savedObjectClient.delete(item.savedObjectType, item.id);
return savedObjectsClient.delete(item.savedObjectType, item.id);
})
)
.then(() => {
chromeLegacy.untrackNavLinksForDeletedSavedObjects(selectedItems.map(item => item.id));
legacyChrome.untrackNavLinksForDeletedSavedObjects(selectedItems.map(item => item.id));
})
.catch(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);
}

View file

@ -27,22 +27,21 @@ import { EuiIcon, EuiBetaBadge, EuiLink, EuiButton, EuiEmptyPrompt } from '@elas
import { getServices } from '../kibana_services';
const { capabilities, toastNotifications, uiSettings } = getServices();
class VisualizeListingTable extends Component {
constructor(props) {
super(props);
}
render() {
const { visualizeCapabilities, uiSettings, toastNotifications } = getServices();
return (
<TableListView
// we allow users to create visualizations even if they can't save them
// for data exploration purposes
createItem={this.props.createItem}
findItems={this.props.findItems}
deleteItems={capabilities.visualize.delete ? this.props.deleteItems : null}
editItem={capabilities.visualize.save ? this.props.editItem : null}
deleteItems={visualizeCapabilities.delete ? this.props.deleteItems : null}
editItem={visualizeCapabilities.save ? this.props.editItem : null}
tableColumns={this.getTableColumns()}
listingLimit={this.props.listingLimit}
selectable={item => item.canDelete}

View 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);
}
}

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { VisSavedObject } from './kibana_services';
import { VisSavedObject } from './legacy_imports';
export interface SavedVisualizations {
urlFor: (id: string) => string;

View file

@ -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);
}

View file

@ -2,6 +2,7 @@
exports[`NewVisModal filter for visualization types should render as expected 1`] = `
<NewVisModal
addBasePath={[Function]}
editorParams={Array []}
intl={
Object {
@ -107,6 +108,23 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
}
isOpen={true}
onClose={[Function]}
uiSettings={
Object {
"get": [MockFunction] {
"calls": Array [
Array [
"visualize:enableLabs",
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
],
},
}
}
visTypesRegistry={
Object {
"all": [Function],
@ -865,6 +883,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
className="euiModal__flex"
>
<TypeSelection
addBasePath={[Function]}
onVisTypeSelected={[Function]}
visTypesRegistry={
Object {
@ -1235,6 +1254,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
/>
</EuiSpacer>
<NewVisHelp
addBasePath={[Function]}
promotedTypes={Array []}
>
<EuiText>
@ -1287,6 +1307,7 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
exports[`NewVisModal should render as expected 1`] = `
<NewVisModal
addBasePath={[Function]}
editorParams={Array []}
intl={
Object {
@ -1392,6 +1413,23 @@ exports[`NewVisModal should render as expected 1`] = `
}
isOpen={true}
onClose={[Function]}
uiSettings={
Object {
"get": [MockFunction] {
"calls": Array [
Array [
"visualize:enableLabs",
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
],
},
}
}
visTypesRegistry={
Object {
"all": [Function],
@ -2141,6 +2179,7 @@ exports[`NewVisModal should render as expected 1`] = `
className="euiModal__flex"
>
<TypeSelection
addBasePath={[Function]}
onVisTypeSelected={[Function]}
visTypesRegistry={
Object {
@ -2499,6 +2538,7 @@ exports[`NewVisModal should render as expected 1`] = `
/>
</EuiSpacer>
<NewVisHelp
addBasePath={[Function]}
promotedTypes={Array []}
>
<EuiText>

View file

@ -20,33 +20,17 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { NewVisModal } from './new_vis_modal';
import { VisType } from '../kibana_services';
import { VisType } from '../legacy_imports';
import { TypesStart } from '../../../../visualizations/public/np_ready/public/types';
jest.mock('../kibana_services', () => {
const mock = {
addBasePath: jest.fn(path => `root${path}`),
uiSettings: { get: jest.fn() },
createUiStatsReporter: () => jest.fn(),
};
jest.mock('../legacy_imports', () => ({
State: () => null,
AppState: () => null,
}));
return {
getServices: () => mock,
VisType: {},
METRIC_TYPE: 'metricType',
};
});
import { getServices } from '../kibana_services';
beforeEach(() => {
jest.clearAllMocks();
});
import { NewVisModal } from './new_vis_modal';
describe('NewVisModal', () => {
const settingsGet = getServices().uiSettings.get as jest.Mock;
const defaultVisTypeParams = {
hidden: false,
visualization: class Controller {
@ -76,17 +60,36 @@ describe('NewVisModal', () => {
},
getAliases: () => [],
};
const addBasePath = (url: string) => `testbasepath${url}`;
const settingsGet = jest.fn();
const uiSettings: any = { get: settingsGet };
beforeEach(() => {
jest.clearAllMocks();
});
it('should render as expected', () => {
const wrapper = mountWithIntl(
<NewVisModal isOpen={true} onClose={() => null} visTypesRegistry={visTypes} />
<NewVisModal
isOpen={true}
onClose={() => null}
visTypesRegistry={visTypes}
addBasePath={addBasePath}
uiSettings={uiSettings}
/>
);
expect(wrapper).toMatchSnapshot();
});
it('should show a button for regular visualizations', () => {
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);
});
@ -95,7 +98,13 @@ describe('NewVisModal', () => {
it('should open the editor for visualizations without search', () => {
window.location.assign = jest.fn();
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"]');
visButton.simulate('click');
@ -110,6 +119,8 @@ describe('NewVisModal', () => {
onClose={() => null}
visTypesRegistry={visTypes}
editorParams={['foo=true', 'bar=42']}
addBasePath={addBasePath}
uiSettings={uiSettings}
/>
);
const visButton = wrapper.find('button[data-test-subj="visType-vis"]');
@ -121,7 +132,13 @@ describe('NewVisModal', () => {
describe('filter for visualization types', () => {
it('should render as expected', () => {
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"]');
searchBox.simulate('change', { target: { value: 'with' } });
@ -133,7 +150,13 @@ describe('NewVisModal', () => {
it('should not show experimental visualizations if visualize:enableLabs is false', () => {
settingsGet.mockReturnValue(false);
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);
});
@ -141,7 +164,13 @@ describe('NewVisModal', () => {
it('should show experimental visualizations if visualize:enableLabs is true', () => {
settingsGet.mockReturnValue(true);
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);
});

View file

@ -22,20 +22,21 @@ import React from 'react';
import { EuiModal, EuiOverlayMask } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { IUiSettingsClient } from 'kibana/public';
import { VisType } from '../legacy_imports';
import { VisualizeConstants } from '../visualize_constants';
import { createUiStatsReporter, METRIC_TYPE } from '../../../../ui_metric/public';
import { SearchSelection } from './search_selection';
import { TypeSelection } from './type_selection';
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 {
isOpen: boolean;
onClose: () => void;
visTypesRegistry: TypesStart;
editorParams?: string[];
addBasePath: (path: string) => string;
uiSettings: IUiSettingsClient;
}
interface TypeSelectionState {
@ -55,7 +56,7 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
constructor(props: TypeSelectionProps) {
super(props);
this.isLabsEnabled = uiSettings.get('visualize:enableLabs');
this.isLabsEnabled = props.uiSettings.get('visualize:enableLabs');
this.state = {
showSearchVisModal: false,
@ -93,6 +94,7 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
showExperimental={this.isLabsEnabled}
onVisTypeSelected={this.onVisTypeSelected}
visTypesRegistry={this.props.visTypesRegistry}
addBasePath={this.props.addBasePath}
/>
</EuiModal>
);
@ -124,7 +126,7 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
this.trackUiMetric(METRIC_TYPE.CLICK, visType.name);
if ('aliasUrl' in visType) {
window.location.href = addBasePath(visType.aliasUrl);
window.location.href = this.props.addBasePath(visType.aliasUrl);
return;
}

View file

@ -22,9 +22,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder';
import { VisType } from '../../kibana_services';
import { SavedObjectFinder, VisType } from '../../legacy_imports';
interface SearchSelectionProps {
onSearchSelected: (searchId: string, searchType: string) => void;

View file

@ -20,7 +20,8 @@
import React from 'react';
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 { TypesStart } from '../../../../visualizations/public/np_ready/public/types';
@ -30,7 +31,9 @@ interface ShowNewVisModalParams {
export function showNewVisModal(
visTypeRegistry: TypesStart,
{ editorParams = [] }: ShowNewVisModalParams = {}
{ editorParams = [] }: ShowNewVisModalParams = {},
addBasePath: (path: string) => string,
uiSettings: IUiSettingsClient
) {
const container = document.createElement('div');
const onClose = () => {
@ -40,14 +43,16 @@ export function showNewVisModal(
document.body.appendChild(container);
const element = (
<I18nContext>
<I18nProvider>
<NewVisModal
isOpen={true}
onClose={onClose}
visTypesRegistry={visTypeRegistry}
editorParams={editorParams}
addBasePath={addBasePath}
uiSettings={uiSettings}
/>
</I18nContext>
</I18nProvider>
);
ReactDOM.render(element, container);
}

View file

@ -21,18 +21,6 @@ import React from 'react';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { NewVisHelp } from './new_vis_help';
jest.mock('../../kibana_services', () => {
return {
getServices: () => ({
addBasePath: jest.fn((url: string) => `testbasepath${url}`),
}),
};
});
beforeEach(() => {
jest.clearAllMocks();
});
describe('NewVisHelp', () => {
it('should render as expected', () => {
expect(
@ -53,6 +41,7 @@ describe('NewVisHelp', () => {
stage: 'production',
},
]}
addBasePath={(url: string) => `testbasepath${url}`}
/>
)
).toMatchInlineSnapshot(`

View file

@ -22,10 +22,9 @@ import React, { Fragment } from 'react';
import { EuiText, EuiButton } from '@elastic/eui';
import { VisTypeAliasListEntry } from './type_selection';
import { getServices } from '../../kibana_services';
interface Props {
promotedTypes: VisTypeAliasListEntry[];
addBasePath: (path: string) => string;
}
export function NewVisHelp(props: Props) {
@ -43,7 +42,7 @@ export function NewVisHelp(props: Props) {
<strong>{t.promotion!.description}</strong>
</p>
<EuiButton
href={getServices().addBasePath(t.aliasUrl)}
href={props.addBasePath(t.aliasUrl)}
fill
size="s"
iconType="popout"

View file

@ -34,8 +34,8 @@ import {
EuiSpacer,
EuiTitle,
} from '@elastic/eui';
import { VisType } from '../../legacy_imports';
import { memoizeLast } from '../../../../../visualizations/public/np_ready/public/legacy/memoize';
import { VisType } from '../../kibana_services';
import { VisTypeAlias } from '../../../../../visualizations/public';
import { NewVisHelp } from './new_vis_help';
import { VisHelpText } from './vis_help_text';
@ -51,6 +51,7 @@ export interface VisTypeAliasListEntry extends VisTypeAlias {
}
interface TypeSelectionProps {
addBasePath: (path: string) => string;
onVisTypeSelected: (visType: VisType | VisTypeAlias) => void;
visTypesRegistry: TypesStart;
showExperimental: boolean;
@ -153,6 +154,7 @@ class TypeSelection extends React.Component<TypeSelectionProps, TypeSelectionSta
<EuiSpacer size="m" />
<NewVisHelp
promotedTypes={(visTypes as VisTypeAliasListEntry[]).filter(t => t.promotion)}
addBasePath={this.props.addBasePath}
/>
</React.Fragment>
)}

View file

@ -33,12 +33,11 @@ import { keyCodes } from '@elastic/eui';
import { parentPipelineAggHelper } from 'ui/agg_types/metrics/lib/parent_pipeline_agg_helper';
import { DefaultEditorSize } from '../../editor_size';
import { VisEditorTypesRegistryProvider } from '../../../registry/vis_editor_types';
import { AggGroupNames } from './agg_groups';
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 {
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;
const updateScope = () => {
@ -161,7 +160,7 @@ const defaultEditor = function ($rootScope, $compile, getAppState) {
this._handler = await embeddables.getEmbeddableFactory('visualization').createFromObject(this.savedObj, {
uiState: uiState,
appState: getAppState(),
appState,
timeRange: timeRange,
filters: filters || [],
query: query,
@ -195,6 +194,4 @@ const defaultEditor = function ($rootScope, $compile, getAppState) {
};
};
VisEditorTypesRegistryProvider.register(defaultEditor);
export { defaultEditor };

View file

@ -50,12 +50,6 @@ export const UI_EXPORT_DEFAULTS = {
fieldFormatEditors: [
'ui/field_editor/components/field_format_editor/register'
],
visEditorTypes: [
'ui/vis/editors/default/default',
],
embeddableFactories: [
'plugins/kibana/visualize/embeddable/visualize_embeddable_factory',
],
search: [
'ui/courier/search_strategy/default_search_strategy',
],

View file

@ -269,7 +269,6 @@ module.exports = function (grunt) {
'--config', 'test/plugin_functional/config.js',
'--bail',
'--debug',
'--kibana-install-dir', KIBANA_INSTALL_DIR,
],
}),

View file

@ -11,7 +11,6 @@ import 'ui/autoload/all';
import 'uiExports/visTypes';
import 'uiExports/visResponseHandlers';
import 'uiExports/visRequestHandlers';
import 'uiExports/visEditorTypes';
import 'uiExports/savedObjectTypes';
import 'uiExports/spyModes';
import 'uiExports/embeddableFactories';

View file

@ -18,7 +18,6 @@ import 'uiExports/contextMenuActions';
import 'uiExports/visTypes';
import 'uiExports/visResponseHandlers';
import 'uiExports/visRequestHandlers';
import 'uiExports/visEditorTypes';
import 'uiExports/inspectorViews';
import 'uiExports/interpreter';
import 'uiExports/savedObjectTypes';