From 70f1827019706629cb091ec301246674f4bfe106 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Wed, 3 May 2017 14:55:22 -0400 Subject: [PATCH] Move dashboard panel rendering logic to each registered type. Let external plugins register panel renderer providers keep loadedPanel promise on the scope for tests Not great to keep this around only for tests, but it follows the previous logic. Fix tests that expect the panel-content div to be the old style Communicate panel title from renderer. Fix tests with extra request Theres an extra request in there because of the getTitle function. Our tests assumed one, this makes it handle any number of requests in them mget (assuming the mock response for each one is sufficient). --- src/core_plugins/kibana/index.js | 3 +- .../public/dashboard/__tests__/panel.js | 16 ++- .../panel/get_object_loaders_for_dashboard.js | 11 -- .../dashboard/panel/load_saved_object.js | 15 -- .../kibana/public/dashboard/panel/panel.html | 40 +----- .../kibana/public/dashboard/panel/panel.js | 132 ++++++------------ .../embeddable/search_embeddable_handler.js | 79 +++++++++++ .../search_embeddable_handler_provider.js | 8 ++ .../discover/embeddable/search_template.html | 16 +++ .../kibana/public/discover/index.js | 4 + .../visualize_embeddable_handler.js | 52 +++++++ .../visualize_embeddable_handler_provider.js | 8 ++ .../embeddable/visualize_template.html | 11 ++ .../kibana/public/visualize/index.js | 4 + src/ui/public/registry/embeddable_handlers.js | 7 + src/ui/ui_exports.js | 1 + 16 files changed, 256 insertions(+), 151 deletions(-) delete mode 100644 src/core_plugins/kibana/public/dashboard/panel/get_object_loaders_for_dashboard.js delete mode 100644 src/core_plugins/kibana/public/dashboard/panel/load_saved_object.js create mode 100644 src/core_plugins/kibana/public/discover/embeddable/search_embeddable_handler.js create mode 100644 src/core_plugins/kibana/public/discover/embeddable/search_embeddable_handler_provider.js create mode 100644 src/core_plugins/kibana/public/discover/embeddable/search_template.html create mode 100644 src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_handler.js create mode 100644 src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_handler_provider.js create mode 100644 src/core_plugins/kibana/public/visualize/embeddable/visualize_template.html create mode 100644 src/ui/public/registry/embeddable_handlers.js diff --git a/src/core_plugins/kibana/index.js b/src/core_plugins/kibana/index.js index a8299059baf32..436e3e776911c 100644 --- a/src/core_plugins/kibana/index.js +++ b/src/core_plugins/kibana/index.js @@ -43,7 +43,8 @@ module.exports = function (kibana) { 'navbarExtensions', 'managementSections', 'devTools', - 'docViews' + 'docViews', + 'embeddableHandlers', ], injectVars, }, diff --git a/src/core_plugins/kibana/public/dashboard/__tests__/panel.js b/src/core_plugins/kibana/public/dashboard/__tests__/panel.js index 5d489ef34d84e..08ebb355bcbbe 100644 --- a/src/core_plugins/kibana/public/dashboard/__tests__/panel.js +++ b/src/core_plugins/kibana/public/dashboard/__tests__/panel.js @@ -15,7 +15,17 @@ describe('dashboard panel', function () { function init(mockDocResponse) { ngMock.module('kibana'); ngMock.inject(($rootScope, $compile, esAdmin) => { - sinon.stub(esAdmin, 'mget').returns(Promise.resolve({ docs: [ mockDocResponse ] })); + sinon.stub(esAdmin, 'mget', function (request) { + const response = { + docs: [] + }; + request.body.docs.forEach(() => { + response.docs.push(mockDocResponse); + }); + + return Promise.resolve(response); + }); + sinon.stub(esAdmin.indices, 'getFieldMapping').returns(Promise.resolve({ '.kibana': { mappings: { @@ -63,7 +73,7 @@ describe('dashboard panel', function () { expect($scope.error).to.be('Could not locate that visualization (id: foo1)'); parentScope.$digest(); const content = $el.find('.panel-content'); - expect(content).to.have.length(0); + expect(content.children().length).to.be(0); }); }); @@ -73,7 +83,7 @@ describe('dashboard panel', function () { expect($scope.error).not.to.be.ok(); parentScope.$digest(); const content = $el.find('.panel-content'); - expect(content).to.have.length(1); + expect(content.children().length).to.be.greaterThan(0); }); }); }); diff --git a/src/core_plugins/kibana/public/dashboard/panel/get_object_loaders_for_dashboard.js b/src/core_plugins/kibana/public/dashboard/panel/get_object_loaders_for_dashboard.js deleted file mode 100644 index 40c99b3c29372..0000000000000 --- a/src/core_plugins/kibana/public/dashboard/panel/get_object_loaders_for_dashboard.js +++ /dev/null @@ -1,11 +0,0 @@ -import { uiModules } from 'ui/modules'; -const module = uiModules.get('app/dashboard'); - -/** - * We have more types available than just 'search' and 'visualization' but as of now, they - * can't be added to a dashboard. - */ -module.factory('getObjectLoadersForDashboard', function (savedSearches, savedVisualizations) { - return () => [savedSearches, savedVisualizations]; -}); - diff --git a/src/core_plugins/kibana/public/dashboard/panel/load_saved_object.js b/src/core_plugins/kibana/public/dashboard/panel/load_saved_object.js deleted file mode 100644 index d9df46dbdcd37..0000000000000 --- a/src/core_plugins/kibana/public/dashboard/panel/load_saved_object.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Retrieves the saved object represented by the panel and returns it, along with the appropriate - * edit Url. - * @param {Array.} loaders - The available loaders for different panel types. - * @param {PanelState} panel - * @returns {Promise.<{savedObj: SavedObject, editUrl: String}>} - */ -export function loadSavedObject(loaders, panel) { - const loader = loaders.find((loader) => loader.type === panel.type); - if (!loader) { - throw new Error(`No loader for object of type ${panel.type}`); - } - return loader.get(panel.id) - .then(savedObj => ({ savedObj, editUrl: loader.urlFor(panel.id) })); -} diff --git a/src/core_plugins/kibana/public/dashboard/panel/panel.html b/src/core_plugins/kibana/public/dashboard/panel/panel.html index ddb46a61feafd..05bea3c746a3b 100644 --- a/src/core_plugins/kibana/public/dashboard/panel/panel.html +++ b/src/core_plugins/kibana/public/dashboard/panel/panel.html @@ -1,11 +1,11 @@ -
+
- {{::savedObj.title}} + {{::title}} - - - - - + >
diff --git a/src/core_plugins/kibana/public/dashboard/panel/panel.js b/src/core_plugins/kibana/public/dashboard/panel/panel.js index 310ec7cc547db..79aa8aae5a227 100644 --- a/src/core_plugins/kibana/public/dashboard/panel/panel.js +++ b/src/core_plugins/kibana/public/dashboard/panel/panel.js @@ -1,23 +1,17 @@ import _ from 'lodash'; import 'ui/visualize'; import 'ui/doc_table'; -import * as columnActions from 'ui/doc_table/actions/columns'; -import 'plugins/kibana/dashboard/panel/get_object_loaders_for_dashboard'; import 'plugins/kibana/visualize/saved_visualizations'; import 'plugins/kibana/discover/saved_searches'; -import { FilterManagerProvider } from 'ui/filter_manager'; import { uiModules } from 'ui/modules'; import panelTemplate from 'plugins/kibana/dashboard/panel/panel.html'; import { savedObjectManagementRegistry } from 'plugins/kibana/management/saved_object_registry'; -import { getPersistedStateId } from 'plugins/kibana/dashboard/panel/panel_state'; -import { loadSavedObject } from 'plugins/kibana/dashboard/panel/load_saved_object'; import { DashboardViewMode } from '../dashboard_view_mode'; +import { EmbeddableHandlersRegistryProvider } from 'ui/registry/embeddable_handlers'; uiModules .get('app/dashboard') -.directive('dashboardPanel', function (savedVisualizations, savedSearches, Notifier, Private, $injector, getObjectLoadersForDashboard) { - const filterManager = Private(FilterManagerProvider); - +.directive('dashboardPanel', function (Notifier, Private, $injector) { const services = savedObjectManagementRegistry.all().map(function (serviceObj) { const service = $injector.get(serviceObj.service); return { @@ -84,94 +78,58 @@ uiModules link: function ($scope, element) { if (!$scope.panel.id || !$scope.panel.type) return; - /** - * Initializes the panel for the saved object. - * @param {{savedObj: SavedObject, editUrl: String}} savedObjectInfo - */ - function initializePanel(savedObjectInfo) { - $scope.savedObj = savedObjectInfo.savedObj; - $scope.editUrl = savedObjectInfo.editUrl; - - element.on('$destroy', function () { - $scope.savedObj.destroy(); - $scope.$destroy(); - }); + const saveState = (panel) => { + $scope.panel = Object.assign($scope.panel, panel); + $scope.saveState(); + }; - // create child ui state from the savedObj - const uiState = $scope.savedObj.uiStateJSON ? JSON.parse($scope.savedObj.uiStateJSON) : {}; - $scope.uiState = $scope.createChildUiState(getPersistedStateId($scope.panel), uiState); + $scope.isViewOnlyMode = () => { + return $scope.dashboardViewMode === DashboardViewMode.VIEW || $scope.isFullScreenMode; + }; - if ($scope.panel.type === savedVisualizations.type && $scope.savedObj.vis) { - $scope.savedObj.vis.setUiState($scope.uiState); - $scope.savedObj.vis.listeners.click = $scope.getVisClickHandler(); - $scope.savedObj.vis.listeners.brush = $scope.getVisBrushHandler(); - } else if ($scope.panel.type === savedSearches.type) { - // This causes changes to a saved search to be hidden, but also allows - // the user to locally modify and save changes to a saved search only in a dashboard. - // See https://github.com/elastic/kibana/issues/9523 for more details. - $scope.panel.columns = $scope.panel.columns || $scope.savedObj.columns; - $scope.panel.sort = $scope.panel.sort || $scope.savedObj.sort; + // TODO: vis actions should be generalized for use by all panel renderers, e.g. updateFilters, updateTimeRange. + const actions = { + getVisClickHandler: $scope.getVisClickHandler, + getVisBrushHandler: $scope.getVisBrushHandler, + saveState, + getIsViewOnlyMode: $scope.isViewOnlyMode, + createChildUiState: $scope.createChildUiState + }; - $scope.setSortOrder = function setSortOrder(columnName, direction) { - $scope.panel.sort = [columnName, direction]; - $scope.saveState(); - }; + const handleError = (error) => { + $scope.error = error.message; - $scope.addColumn = function addColumn(columnName) { - $scope.savedObj.searchSource.get('index').popularizeField(columnName, 1); - columnActions.addColumn($scope.panel.columns, columnName); - $scope.saveState(); // sync to sharing url - }; + // Dashboard listens for this broadcast, once for every visualization (pendingVisCount). + // We need to broadcast even in the event of an error or it'll never fetch the data for + // other visualizations. + $scope.$root.$broadcast('ready:vis'); - $scope.removeColumn = function removeColumn(columnName) { - $scope.savedObj.searchSource.get('index').popularizeField(columnName, 1); - columnActions.removeColumn($scope.panel.columns, columnName); - $scope.saveState(); // sync to sharing url - }; + // If the savedObjectType matches the panel type, this means the object itself has been deleted, + // so we shouldn't even have an edit link. If they don't match, it means something else is wrong + // with the object (but the object still exists), so we link to the object editor instead. + const objectItselfDeleted = error.savedObjectType === $scope.panel.type; + if (objectItselfDeleted) return; - $scope.moveColumn = function moveColumn(columnName, newIndex) { - columnActions.moveColumn($scope.panel.columns, columnName, newIndex); - $scope.saveState(); // sync to sharing url - }; - } + const type = $scope.panel.type; + const id = $scope.panel.id; + const service = _.find(services, { type: type }); + if (!service) return; - $scope.filter = function (field, value, operator) { - const index = $scope.savedObj.searchSource.get('index').id; - filterManager.add(field, value, operator, index); - }; + $scope.editUrl = '#management/kibana/objects/' + service.name + '/' + id + '?notFound=' + error.savedObjectType; + }; + const embeddableHandlers = Private(EmbeddableHandlersRegistryProvider); + const embeddableHandler = embeddableHandlers.byName[$scope.panel.type]; + if (!embeddableHandler) { + handleError(`No embeddable handler for panel type ${$scope.panel.type} was found.`); + return; } - - $scope.loadedPanel = loadSavedObject(getObjectLoadersForDashboard(), $scope.panel) - .then(initializePanel) - .catch(function (e) { - $scope.error = e.message; - - // Dashboard listens for this broadcast, once for every visualization (pendingVisCount). - // We need to broadcast even in the event of an error or it'll never fetch the data for - // other visualizations. - $scope.$root.$broadcast('ready:vis'); - - // If the savedObjectType matches the panel type, this means the object itself has been deleted, - // so we shouldn't even have an edit link. If they don't match, it means something else is wrong - // with the object (but the object still exists), so we link to the object editor instead. - const objectItselfDeleted = e.savedObjectType === $scope.panel.type; - if (objectItselfDeleted) return; - - const type = $scope.panel.type; - const id = $scope.panel.id; - const service = _.find(services, { type: type }); - if (!service) return; - - $scope.editUrl = '#management/kibana/objects/' + service.name + '/' + id + '?notFound=' + e.savedObjectType; - }); - - /** - * @returns {boolean} True if the user can only view, not edit. - */ - $scope.isViewOnlyMode = () => { - return $scope.dashboardViewMode === DashboardViewMode.VIEW || $scope.isFullScreenMode; - }; + $scope.editUrl = embeddableHandler.getEditPath($scope.panel); + embeddableHandler.getTitleFor($scope.panel).then(title => { + $scope.title = title; + }); + $scope.loadedPanel = + embeddableHandler.renderAt(element.find('.panel-content').get(0), $scope.panel, actions).catch(handleError); } }; }); diff --git a/src/core_plugins/kibana/public/discover/embeddable/search_embeddable_handler.js b/src/core_plugins/kibana/public/discover/embeddable/search_embeddable_handler.js new file mode 100644 index 0000000000000..957bd6e3d36f8 --- /dev/null +++ b/src/core_plugins/kibana/public/discover/embeddable/search_embeddable_handler.js @@ -0,0 +1,79 @@ +import searchTemplate from './search_template.html'; +import angular from 'angular'; +import * as columnActions from 'ui/doc_table/actions/columns'; +import { getPersistedStateId } from 'plugins/kibana/dashboard/panel/panel_state'; +import { FilterManagerProvider } from 'ui/filter_manager'; + +export class SearchEmbeddableHandler { + constructor($compile, $rootScope, searchLoader, Private) { + this.$compile = $compile; + this.searchLoader = searchLoader; + this.filterManager = Private(FilterManagerProvider); + this.$rootScope = $rootScope; + this.name = 'search'; + this.title = 'Saved Searches'; + } + + getEditPath(panel) { + return this.searchLoader.urlFor(panel.id); + } + + canRenderType(type) { + return type === 'search'; + } + + getTitleFor(panel) { + return this.searchLoader.get(panel.id).then(savedObject => savedObject.title); + } + + renderAt(domNode, panel, actions) { + return this.searchLoader.get(panel.id).then((savedObject) => { + const editUrl = this.searchLoader.urlFor(panel.id); + const searchScope = this.$rootScope.$new(); + searchScope.editUrl = editUrl; + searchScope.savedObj = savedObject; + searchScope.panel = panel; + + // This causes changes to a saved search to be hidden, but also allows + // the user to locally modify and save changes to a saved search only in a dashboard. + // See https://github.com/elastic/kibana/issues/9523 for more details. + actions.saveState({ + columns: searchScope.panel.columns || searchScope.savedObj.columns, + sort: searchScope.panel.sort || searchScope.savedObj.sort + }); + + const uiState = savedObject.uiStateJSON ? JSON.parse(savedObject.uiStateJSON) : {}; + searchScope.uiState = actions.createChildUiState(getPersistedStateId(panel), uiState); + + searchScope.setSortOrder = function setSortOrder(columnName, direction) { + actions.saveState({ sort: [columnName, direction] }); + }; + + searchScope.addColumn = function addColumn(columnName) { + savedObject.searchSource.get('index').popularizeField(columnName, 1); + columnActions.addColumn(searchScope.panel.columns, columnName); + actions.saveState({}); // sync to sharing url + }; + + searchScope.removeColumn = function removeColumn(columnName) { + savedObject.searchSource.get('index').popularizeField(columnName, 1); + columnActions.removeColumn(searchScope.panel.columns, columnName); + actions.saveState({}); // sync to sharing url + }; + + searchScope.moveColumn = function moveColumn(columnName, newIndex) { + columnActions.moveColumn(searchScope.panel.columns, columnName, newIndex); + actions.saveState({}); // sync to sharing url + }; + + searchScope.filter = function (field, value, operator) { + const index = savedObject.searchSource.get('index').id; + this.filterManager.add(field, value, operator, index); + }; + + const searchInstance = this.$compile(searchTemplate)(searchScope); + const rootNode = angular.element(domNode); + rootNode.append(searchInstance); + }); + } +} diff --git a/src/core_plugins/kibana/public/discover/embeddable/search_embeddable_handler_provider.js b/src/core_plugins/kibana/public/discover/embeddable/search_embeddable_handler_provider.js new file mode 100644 index 0000000000000..5b08f33857dcd --- /dev/null +++ b/src/core_plugins/kibana/public/discover/embeddable/search_embeddable_handler_provider.js @@ -0,0 +1,8 @@ +import { SearchEmbeddableHandler } from './search_embeddable_handler'; + +export function searchEmbeddableHandlerProvider(Private) { + const SearchEmbeddableHandlerProvider = ($compile, $rootScope, savedSearches, Private) => { + return new SearchEmbeddableHandler($compile, $rootScope, savedSearches, Private); + }; + return Private(SearchEmbeddableHandlerProvider); +} diff --git a/src/core_plugins/kibana/public/discover/embeddable/search_template.html b/src/core_plugins/kibana/public/discover/embeddable/search_template.html new file mode 100644 index 0000000000000..0bb48561b27df --- /dev/null +++ b/src/core_plugins/kibana/public/discover/embeddable/search_template.html @@ -0,0 +1,16 @@ + + diff --git a/src/core_plugins/kibana/public/discover/index.js b/src/core_plugins/kibana/public/discover/index.js index 68a83fc5ae97a..77ce70e54b58a 100644 --- a/src/core_plugins/kibana/public/discover/index.js +++ b/src/core_plugins/kibana/public/discover/index.js @@ -6,7 +6,11 @@ import 'plugins/kibana/discover/components/field_chooser/field_chooser'; import 'plugins/kibana/discover/controllers/discover'; import 'plugins/kibana/discover/styles/main.less'; import 'ui/doc_table/components/table_row'; + import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; import { savedSearchProvider } from 'plugins/kibana/discover/saved_searches/saved_search_register'; +import { EmbeddableHandlersRegistryProvider } from 'ui/registry/embeddable_handlers'; +import { searchEmbeddableHandlerProvider } from './embeddable/search_embeddable_handler_provider'; SavedObjectRegistryProvider.register(savedSearchProvider); +EmbeddableHandlersRegistryProvider.register(searchEmbeddableHandlerProvider); diff --git a/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_handler.js b/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_handler.js new file mode 100644 index 0000000000000..843a92985150a --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_handler.js @@ -0,0 +1,52 @@ +import angular from 'angular'; + +import visualizationTemplate from './visualize_template.html'; +import { getPersistedStateId } from 'plugins/kibana/dashboard/panel/panel_state'; + +export class VisualizeEmbeddableHandler { + constructor($compile, $rootScope, visualizeLoader) { + this.$compile = $compile; + this.visualizeLoader = visualizeLoader; + this.$rootScope = $rootScope; + this.name = 'visualization'; + this.title = 'Visualizations'; + } + + getEditPath(panel) { + return this.visualizeLoader.urlFor(panel.id); + } + + canRenderType(type) { + return type === 'visualization'; + } + + getTitleFor(panel) { + return this.visualizeLoader.get(panel.id).then(savedObject => savedObject.title); + } + + renderAt(domNode, panel, actions) { + return this.visualizeLoader.get(panel.id).then((savedObject) => { + const visualizeScope = this.$rootScope.$new(); + visualizeScope.editUrl = this.getEditPath(panel); + visualizeScope.savedObj = savedObject; + visualizeScope.panel = panel; + + const uiState = savedObject.uiStateJSON ? JSON.parse(savedObject.uiStateJSON) : {}; + visualizeScope.uiState = actions.createChildUiState(getPersistedStateId(panel), uiState); + + visualizeScope.savedObj.vis.setUiState(uiState); + visualizeScope.savedObj.vis.listeners.click = actions.getVisClickHandler(); + visualizeScope.savedObj.vis.listeners.brush = actions.getVisBrushHandler(); + visualizeScope.isFullScreenMode = actions.getIsViewOnlyMode(); + + const visualizationInstance = this.$compile(visualizationTemplate)(visualizeScope); + const rootNode = angular.element(domNode); + rootNode.append(visualizationInstance); + + visualizationInstance.on('$destroy', function () { + visualizeScope.savedObj.destroy(); + visualizeScope.$destroy(); + }); + }); + } +} diff --git a/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_handler_provider.js b/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_handler_provider.js new file mode 100644 index 0000000000000..43868332aeef9 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable_handler_provider.js @@ -0,0 +1,8 @@ +import { VisualizeEmbeddableHandler } from './visualize_embeddable_handler'; + +export function visualizeEmbeddableHandlerProvider(Private) { + const VisualizeEmbeddableHandlerProvider = ($compile, $rootScope, savedVisualizations) => { + return new VisualizeEmbeddableHandler($compile, $rootScope, savedVisualizations); + }; + return Private(VisualizeEmbeddableHandlerProvider); +} diff --git a/src/core_plugins/kibana/public/visualize/embeddable/visualize_template.html b/src/core_plugins/kibana/public/visualize/embeddable/visualize_template.html new file mode 100644 index 0000000000000..c76a33ea535a6 --- /dev/null +++ b/src/core_plugins/kibana/public/visualize/embeddable/visualize_template.html @@ -0,0 +1,11 @@ + + diff --git a/src/core_plugins/kibana/public/visualize/index.js b/src/core_plugins/kibana/public/visualize/index.js index cb5ef507f4fb3..43c1cd26511a1 100644 --- a/src/core_plugins/kibana/public/visualize/index.js +++ b/src/core_plugins/kibana/public/visualize/index.js @@ -18,12 +18,15 @@ import 'plugins/kibana/visualize/saved_visualizations/_saved_vis'; import 'plugins/kibana/visualize/saved_visualizations/saved_visualizations'; import 'ui/directives/scroll_bottom'; import 'ui/filters/sort_prefix_first'; + import uiRoutes from 'ui/routes'; import visualizeListingTemplate from './listing/visualize_listing.html'; import { VisualizeListingController } from './listing/visualize_listing'; import { VisualizeConstants } from './visualize_constants'; import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry'; import { savedVisualizationProvider } from 'plugins/kibana/visualize/saved_visualizations/saved_visualization_register'; +import { EmbeddableHandlersRegistryProvider } from 'ui/registry/embeddable_handlers'; +import { visualizeEmbeddableHandlerProvider } from './embeddable/visualize_embeddable_handler_provider'; uiRoutes .defaults(/visualize/, { @@ -38,3 +41,4 @@ uiRoutes // preloading SavedObjectRegistryProvider.register(savedVisualizationProvider); +EmbeddableHandlersRegistryProvider.register(visualizeEmbeddableHandlerProvider); diff --git a/src/ui/public/registry/embeddable_handlers.js b/src/ui/public/registry/embeddable_handlers.js new file mode 100644 index 0000000000000..0e093928c35e9 --- /dev/null +++ b/src/ui/public/registry/embeddable_handlers.js @@ -0,0 +1,7 @@ +import { uiRegistry } from 'ui/registry/_registry'; + +export const EmbeddableHandlersRegistryProvider = uiRegistry({ + name: 'embeddableHandlers', + index: ['name'], + order: ['title'] +}); diff --git a/src/ui/ui_exports.js b/src/ui/ui_exports.js index 74cf53c07e077..78b8270d9a23f 100644 --- a/src/ui/ui_exports.js +++ b/src/ui/ui_exports.js @@ -89,6 +89,7 @@ class UiExports { }; case 'visTypes': + case 'embeddableHandlers': case 'fieldFormats': case 'spyModes': case 'chromeNavControls':