From 4337d91c787d970a89aa40aab5f9dd54f0435d26 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Tue, 24 Mar 2020 08:32:30 -0400 Subject: [PATCH 01/21] wip --- .../hello_world_embeddable_factory.ts | 4 +- .../list_container/list_container_factory.ts | 8 +- .../multi_task_todo_embeddable_factory.ts | 8 +- .../searchable_list_container_factory.ts | 11 +- .../public/todo/todo_embeddable_factory.tsx | 13 +- .../public/todo_embeddable_example.tsx | 15 +- .../np_ready/dashboard_app_controller.tsx | 147 +++++++++-------- .../embeddable/search_embeddable_factory.ts | 27 ++-- .../public/visualize/np_ready/legacy_app.js | 15 +- .../public/embeddable/visualize_embeddable.ts | 56 ++++--- .../visualize_embeddable_factory.tsx | 152 +++++++++--------- .../actions/expand_panel_action.test.tsx | 17 +- .../actions/replace_panel_action.test.tsx | 25 +-- .../embeddable/dashboard_container.test.tsx | 16 +- .../dashboard_container_factory.tsx | 23 ++- .../embeddable/grid/dashboard_grid.test.tsx | 2 +- .../viewport/dashboard_viewport.test.tsx | 2 +- .../tests/dashboard_container.test.tsx.rej | 10 ++ src/plugins/embeddable/public/index.ts | 1 + .../lib/actions/edit_panel_action.test.tsx | 17 +- .../embeddable_child_panel.test.tsx | 13 +- .../embeddables/create_embeddable_factory.ts | 49 ++++++ .../lib/embeddables/embeddable_factory.ts | 61 +++---- .../embeddable_factory_definition.ts | 44 +++++ .../embeddable_factory_renderer.test.tsx | 12 +- .../public/lib/embeddables/index.ts | 8 +- .../lib/panel/embeddable_panel.test.tsx | 39 +++-- .../add_panel/add_panel_action.test.tsx | 10 +- .../add_panel/add_panel_flyout.test.tsx | 22 +-- .../customize_panel_action.test.ts | 11 +- .../inspect_panel_action.test.tsx | 22 ++- .../remove_panel_action.test.tsx | 9 +- .../contact_card_embeddable_factory.tsx | 19 +-- .../slow_contact_card_embeddable_factory.ts | 12 +- .../filterable_container_factory.ts | 20 ++- .../filterable_embeddable_factory.ts | 5 +- src/plugins/embeddable/public/mocks.ts | 1 + src/plugins/embeddable/public/plugin.test.ts | 25 +++ src/plugins/embeddable/public/plugin.ts | 77 +++++++-- .../public/tests/apply_filter_action.test.ts | 22 +-- .../embeddable/public/tests/container.test.ts | 22 +-- .../tests/customize_panel_modal.test.tsx | 3 +- .../public/tests/explicit_input.test.ts | 3 +- .../tests/get_embeddable_factories.test.ts | 10 +- src/plugins/embeddable/public/types.ts | 18 ++- .../app/dashboard_container_example.tsx | 6 +- .../embeddable/embeddable_factory.ts | 23 ++- .../embeddable/map_embeddable_factory.js | 25 ++- .../public/custom_time_range_action.test.ts | 4 +- .../time_range_embeddable_factory.ts | 28 +++- .../public/embeddables/resolver/factory.ts | 4 +- 51 files changed, 698 insertions(+), 498 deletions(-) create mode 100644 src/plugins/dashboard/public/tests/dashboard_container.test.tsx.rej create mode 100644 src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts create mode 100644 src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts diff --git a/examples/embeddable_examples/public/hello_world/hello_world_embeddable_factory.ts b/examples/embeddable_examples/public/hello_world/hello_world_embeddable_factory.ts index 2995c99ac9e58..b81a229ea23e7 100644 --- a/examples/embeddable_examples/public/hello_world/hello_world_embeddable_factory.ts +++ b/examples/embeddable_examples/public/hello_world/hello_world_embeddable_factory.ts @@ -21,11 +21,11 @@ import { i18n } from '@kbn/i18n'; import { IContainer, EmbeddableInput, - EmbeddableFactory, + EmbeddableFactoryDefinition, } from '../../../../src/plugins/embeddable/public'; import { HelloWorldEmbeddable, HELLO_WORLD_EMBEDDABLE } from './hello_world_embeddable'; -export class HelloWorldEmbeddableFactory extends EmbeddableFactory { +export class HelloWorldEmbeddableFactory implements EmbeddableFactoryDefinition { public readonly type = HELLO_WORLD_EMBEDDABLE; /** diff --git a/examples/embeddable_examples/public/list_container/list_container_factory.ts b/examples/embeddable_examples/public/list_container/list_container_factory.ts index 247cf48b41bde..510946e94c6a5 100644 --- a/examples/embeddable_examples/public/list_container/list_container_factory.ts +++ b/examples/embeddable_examples/public/list_container/list_container_factory.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { - EmbeddableFactory, + EmbeddableFactoryDefinition, ContainerInput, EmbeddableStart, } from '../../../../src/plugins/embeddable/public'; @@ -29,13 +29,11 @@ interface StartServices { getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; } -export class ListContainerFactory extends EmbeddableFactory { +export class ListContainerFactory implements EmbeddableFactoryDefinition { public readonly type = LIST_CONTAINER; public readonly isContainerType = true; - constructor(private getStartServices: () => Promise) { - super(); - } + constructor(private getStartServices: () => Promise) {} public async isEditable() { return true; diff --git a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts index 9afdeabaee765..d010e25885ce6 100644 --- a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts +++ b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { IContainer, EmbeddableFactory } from '../../../../src/plugins/embeddable/public'; +import { IContainer, EmbeddableFactoryDefinition } from '../../../../src/plugins/embeddable/public'; import { MultiTaskTodoEmbeddable, MULTI_TASK_TODO_EMBEDDABLE, @@ -26,10 +26,8 @@ import { MultiTaskTodoOutput, } from './multi_task_todo_embeddable'; -export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory< - MultiTaskTodoInput, - MultiTaskTodoOutput -> { +export class MultiTaskTodoEmbeddableFactory + implements EmbeddableFactoryDefinition { public readonly type = MULTI_TASK_TODO_EMBEDDABLE; public async isEditable() { diff --git a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts index 79859cc015a80..74a568b3ebbf8 100644 --- a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts +++ b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts @@ -18,7 +18,10 @@ */ import { i18n } from '@kbn/i18n'; -import { EmbeddableFactory, EmbeddableStart } from '../../../../src/plugins/embeddable/public'; +import { + EmbeddableFactoryDefinition, + EmbeddableStart, +} from '../../../../src/plugins/embeddable/public'; import { SEARCHABLE_LIST_CONTAINER, SearchableListContainer, @@ -29,13 +32,11 @@ interface StartServices { getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; } -export class SearchableListContainerFactory extends EmbeddableFactory { +export class SearchableListContainerFactory implements EmbeddableFactoryDefinition { public readonly type = SEARCHABLE_LIST_CONTAINER; public readonly isContainerType = true; - constructor(private getStartServices: () => Promise) { - super(); - } + constructor(private getStartServices: () => Promise) {} public async isEditable() { return true; diff --git a/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx index d7be436905382..90386ba9ec2fe 100644 --- a/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx +++ b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx @@ -23,7 +23,7 @@ import { OverlayStart } from 'kibana/public'; import { EuiFieldText } from '@elastic/eui'; import { EuiButton } from '@elastic/eui'; import { toMountPoint } from '../../../../src/plugins/kibana_react/public'; -import { IContainer, EmbeddableFactory } from '../../../../src/plugins/embeddable/public'; +import { IContainer, EmbeddableFactoryDefinition } from '../../../../src/plugins/embeddable/public'; import { TodoEmbeddable, TODO_EMBEDDABLE, TodoInput, TodoOutput } from './todo_embeddable'; function TaskInput({ onSave }: { onSave: (task: string) => void }) { @@ -47,16 +47,11 @@ interface StartServices { openModal: OverlayStart['openModal']; } -export class TodoEmbeddableFactory extends EmbeddableFactory< - TodoInput, - TodoOutput, - TodoEmbeddable -> { +export class TodoEmbeddableFactory + implements EmbeddableFactoryDefinition { public readonly type = TODO_EMBEDDABLE; - constructor(private getStartServices: () => Promise) { - super(); - } + constructor(private getStartServices: () => Promise) {} public async isEditable() { return true; diff --git a/examples/embeddable_explorer/public/todo_embeddable_example.tsx b/examples/embeddable_explorer/public/todo_embeddable_example.tsx index ce92301236c2b..2af6c713593c6 100644 --- a/examples/embeddable_explorer/public/todo_embeddable_example.tsx +++ b/examples/embeddable_explorer/public/todo_embeddable_example.tsx @@ -37,9 +37,14 @@ import { import { TodoEmbeddable, TODO_EMBEDDABLE, - TodoEmbeddableFactory, + TodoInput, } from '../../../examples/embeddable_examples/public/todo'; -import { EmbeddableStart, EmbeddableRoot } from '../../../src/plugins/embeddable/public'; +import { + EmbeddableStart, + EmbeddableRoot, + EmbeddableOutput, + ErrorEmbeddable, +} from '../../../src/plugins/embeddable/public'; interface Props { getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; @@ -53,7 +58,7 @@ interface State { } export class TodoEmbeddableExample extends React.Component { - private embeddable?: TodoEmbeddable; + private embeddable?: TodoEmbeddable | ErrorEmbeddable; constructor(props: Props) { super(props); @@ -62,7 +67,9 @@ export class TodoEmbeddableExample extends React.Component { } public componentDidMount() { - const factory = this.props.getEmbeddableFactory(TODO_EMBEDDABLE) as TodoEmbeddableFactory; + const factory = this.props.getEmbeddableFactory( + TODO_EMBEDDABLE + ); if (factory === undefined) { throw new Error('Embeddable factory is undefined!'); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx index 0c6686c993371..8b03bc5b2b769 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx @@ -48,7 +48,6 @@ import { import { DASHBOARD_CONTAINER_TYPE, DashboardContainer, - DashboardContainerFactory, DashboardContainerInput, DashboardPanelState, } from '../../../../../../plugins/dashboard/public'; @@ -58,6 +57,7 @@ import { isErrorEmbeddable, openAddPanelFlyout, ViewMode, + ContainerOutput, } from '../../../../embeddable_api/public/np_ready/public'; import { NavAction, SavedDashboardPanel } from './types'; @@ -307,83 +307,92 @@ export class DashboardAppController { let outputSubscription: Subscription | undefined; const dashboardDom = document.getElementById('dashboardViewport'); - const dashboardFactory = embeddable.getEmbeddableFactory( - DASHBOARD_CONTAINER_TYPE - ) as DashboardContainerFactory; - dashboardFactory - .create(getDashboardInput()) - .then((container: DashboardContainer | ErrorEmbeddable) => { - if (!isErrorEmbeddable(container)) { - dashboardContainer = container; - - dashboardContainer.renderEmpty = () => { - const shouldShowEditHelp = getShouldShowEditHelp(); - const shouldShowViewHelp = getShouldShowViewHelp(); - const isEmptyInReadOnlyMode = shouldShowUnauthorizedEmptyState(); - const isEmptyState = shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadOnlyMode; - return isEmptyState ? ( - - ) : null; - }; - - updateIndexPatterns(dashboardContainer); - - outputSubscription = dashboardContainer.getOutput$().subscribe(() => { - updateIndexPatterns(dashboardContainer); - }); - - inputSubscription = dashboardContainer.getInput$().subscribe(() => { - let dirty = false; + const dashboardFactory = embeddable.getEmbeddableFactory< + DashboardContainerInput, + ContainerOutput, + DashboardContainer + >(DASHBOARD_CONTAINER_TYPE); + + if (dashboardFactory) { + dashboardFactory + .create(getDashboardInput()) + .then((container: DashboardContainer | ErrorEmbeddable | undefined) => { + if (container && !isErrorEmbeddable(container)) { + dashboardContainer = container; + + dashboardContainer.renderEmpty = () => { + const shouldShowEditHelp = getShouldShowEditHelp(); + const shouldShowViewHelp = getShouldShowViewHelp(); + const isEmptyInReadOnlyMode = shouldShowUnauthorizedEmptyState(); + const isEmptyState = + shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadOnlyMode; + return isEmptyState ? ( + + ) : null; + }; - // This has to be first because handleDashboardContainerChanges causes - // appState.save which will cause refreshDashboardContainer to be called. - - if ( - !esFilters.compareFilters( - container.getInput().filters, - queryFilter.getFilters(), - esFilters.COMPARE_ALL_OPTIONS - ) - ) { - // Add filters modifies the object passed to it, hence the clone deep. - queryFilter.addFilters(_.cloneDeep(container.getInput().filters)); + updateIndexPatterns(dashboardContainer); - dashboardStateManager.applyFilters($scope.model.query, container.getInput().filters); - dirty = true; - } + outputSubscription = dashboardContainer.getOutput$().subscribe(() => { + updateIndexPatterns(dashboardContainer); + }); - dashboardStateManager.handleDashboardContainerChanges(container); - $scope.$evalAsync(() => { - if (dirty) { - updateState(); + inputSubscription = dashboardContainer.getInput$().subscribe(() => { + let dirty = false; + + // This has to be first because handleDashboardContainerChanges causes + // appState.save which will cause refreshDashboardContainer to be called. + + if ( + !esFilters.compareFilters( + container.getInput().filters, + queryFilter.getFilters(), + esFilters.COMPARE_ALL_OPTIONS + ) + ) { + // Add filters modifies the object passed to it, hence the clone deep. + queryFilter.addFilters(_.cloneDeep(container.getInput().filters)); + + dashboardStateManager.applyFilters( + $scope.model.query, + container.getInput().filters + ); + dirty = true; } + + dashboardStateManager.handleDashboardContainerChanges(container); + $scope.$evalAsync(() => { + if (dirty) { + updateState(); + } + }); }); - }); - dashboardStateManager.registerChangeListener(() => { - // we aren't checking dirty state because there are changes the container needs to know about - // that won't make the dashboard "dirty" - like a view mode change. - refreshDashboardContainer(); - }); + dashboardStateManager.registerChangeListener(() => { + // we aren't checking dirty state because there are changes the container needs to know about + // that won't make the dashboard "dirty" - like a view mode change. + refreshDashboardContainer(); + }); - // This code needs to be replaced with a better mechanism for adding new embeddables of - // any type from the add panel. Likely this will happen via creating a visualization "inline", - // without navigating away from the UX. - if ($routeParams[DashboardConstants.ADD_EMBEDDABLE_TYPE]) { - const type = $routeParams[DashboardConstants.ADD_EMBEDDABLE_TYPE]; - const id = $routeParams[DashboardConstants.ADD_EMBEDDABLE_ID]; - container.addSavedObjectEmbeddable(type, id); - removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_TYPE); - removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_ID); + // This code needs to be replaced with a better mechanism for adding new embeddables of + // any type from the add panel. Likely this will happen via creating a visualization "inline", + // without navigating away from the UX. + if ($routeParams[DashboardConstants.ADD_EMBEDDABLE_TYPE]) { + const type = $routeParams[DashboardConstants.ADD_EMBEDDABLE_TYPE]; + const id = $routeParams[DashboardConstants.ADD_EMBEDDABLE_ID]; + container.addSavedObjectEmbeddable(type, id); + removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_TYPE); + removeQueryParam(history, DashboardConstants.ADD_EMBEDDABLE_ID); + } } - } - if (dashboardDom) { - container.render(dashboardDom); - } - }); + if (dashboardDom && container) { + container.render(dashboardDom); + } + }); + } // Part of the exposed plugin API - do not remove without careful consideration. this.appStatus = { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts index 6f3adc1f4fcce..0f190fa586056 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts @@ -22,9 +22,9 @@ import { i18n } from '@kbn/i18n'; import { UiActionsStart } from 'src/plugins/ui_actions/public'; import { getServices } from '../../kibana_services'; import { - EmbeddableFactory, - ErrorEmbeddable, + EmbeddableFactoryDefinition, Container, + ErrorEmbeddable, } from '../../../../../../../plugins/embeddable/public'; import { TimeRange } from '../../../../../../../plugins/data/public'; @@ -37,28 +37,23 @@ interface StartServices { isEditable: () => boolean; } -export class SearchEmbeddableFactory extends EmbeddableFactory< - SearchInput, - SearchOutput, - SearchEmbeddable -> { +export class SearchEmbeddableFactory + implements EmbeddableFactoryDefinition { public readonly type = SEARCH_EMBEDDABLE_TYPE; private $injector: auto.IInjectorService | null; private getInjector: () => Promise | null; + public readonly savedObjectMetaData = { + name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { + defaultMessage: 'Saved search', + }), + type: 'search', + getIconForSavedObject: () => 'search', + }; constructor( private getStartServices: () => Promise, getInjector: () => Promise ) { - super({ - savedObjectMetaData: { - name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { - defaultMessage: 'Saved search', - }), - type: 'search', - getIconForSavedObject: () => 'search', - }, - }); this.$injector = null; this.getInjector = getInjector; } diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js index b0b1ae31a02a5..d60c46608459f 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js @@ -51,16 +51,21 @@ const getResolvedResults = deps => { results.savedVis = savedVis; return visualizations .convertToSerializedVis(savedVis) - .then(serializedVis => visualizations.createVis(serializedVis.type, serializedVis)) - .then(vis => { + .then(serializedVis => ({ + vis: visualizations.createVis(serializedVis.type, serializedVis), + serializedVis, + })) + .then(({ vis, serializedVis }) => { if (vis.type.setup) { return vis.type.setup(vis).catch(() => vis); } - return vis; + return { vis, serializedVis }; }) - .then(vis => { + .then(({ vis, serializedVis }) => { results.vis = vis; - return deps.embeddable.getEmbeddableFactory('visualization').createFromObject(results.vis, { + return deps.embeddable.getEmbeddableFactory('visualization').create({ + visType: savedVis.type, + visObject: serializedVis, timeRange: data.query.timefilter.timefilter.getTime(), filters: data.query.filterManager.getFilters(), }); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts index 342824bade3dd..42d19fbeb934a 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts @@ -20,6 +20,8 @@ import _, { get } from 'lodash'; import { Subscription } from 'rxjs'; import * as Rx from 'rxjs'; +import { SavedObjectLoader } from '../../../../../../../plugins/saved_objects/public'; +import { getHttp } from '../services'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; import { IIndexPattern, @@ -33,40 +35,37 @@ import { EmbeddableInput, EmbeddableOutput, Embeddable, - Container, EmbeddableVisTriggerContext, + IContainer, } from '../../../../../../../plugins/embeddable/public'; import { dispatchRenderComplete } from '../../../../../../../plugins/kibana_utils/public'; import { IExpressionLoaderParams, ExpressionsStart, } from '../../../../../../../plugins/expressions/public'; -import { PersistedState } from '../../../../../../../plugins/visualizations/public'; import { buildPipeline } from '../legacy/build_pipeline'; import { Vis } from '../vis'; import { getExpressions, getUiActions } from '../services'; import { VIS_EVENT_TO_TRIGGER } from './events'; +import { SerializedVis } from '../types'; const getKeys = (o: T): Array => Object.keys(o) as Array; export interface VisualizeEmbeddableConfiguration { - vis: Vis; - indexPatterns?: IIndexPattern[]; - editUrl: string; editable: boolean; - appState?: { save(): void }; - uiState?: PersistedState; + savedVisualizations: SavedObjectLoader; } export interface VisualizeInput extends EmbeddableInput { timeRange?: TimeRange; query?: Query; filters?: Filter[]; + // This should probably be called "vis overrides". "vis" is a bad name. vis?: { colors?: { [key: string]: string }; }; - appState?: { save(): void }; - uiState?: PersistedState; + visObject: SerializedVis; + visType: string; } export interface VisualizeOutput extends EmbeddableOutput { @@ -77,6 +76,25 @@ export interface VisualizeOutput extends EmbeddableOutput { type ExpressionLoader = InstanceType; +function getOutput( + input: VisualizeInput, + { savedVisualizations, editable }: VisualizeEmbeddableConfiguration +): VisualizeOutput { + const vis = new Vis(input.visType, input.visObject); + const indexPattern = vis.data.indexPattern; + const indexPatterns = indexPattern ? [indexPattern] : []; + const editUrl = input.visObject.id + ? getHttp().basePath.prepend(`/app/kibana${savedVisualizations.urlFor(input.visObject.id)}`) + : ''; + return { + defaultTitle: vis.title, + editUrl, + editable, + indexPatterns, + visTypeName: vis.type.name, + }; +} + export class VisualizeEmbeddable extends Embeddable { private handler?: ExpressionLoader; private timefilter: TimefilterContract; @@ -94,23 +112,13 @@ export class VisualizeEmbeddable extends Embeddable { +export class VisualizeEmbeddableFactory + implements + EmbeddableFactoryDefinition< + VisualizeInput, + VisualizeOutput | EmbeddableOutput, + VisualizeEmbeddable | DisabledLabEmbeddable, + VisualizationAttributes + > { public readonly type = VISUALIZE_EMBEDDABLE_TYPE; - - constructor() { - super({ - savedObjectMetaData: { - name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }), - includeFields: ['visState'], - type: 'visualization', - getIconForSavedObject: savedObject => { - return ( - getTypes().get(JSON.parse(savedObject.attributes.visState).type).icon || 'visualizeApp' - ); - }, - getTooltipForSavedObject: savedObject => { - return `${savedObject.attributes.title} (${ - getTypes().get(JSON.parse(savedObject.attributes.visState).type).title - })`; - }, - showSavedObject: savedObject => { - const typeName: string = JSON.parse(savedObject.attributes.visState).type; - const visType = getTypes().get(typeName); - if (!visType) { - return false; - } - if (getUISettings().get('visualize:enableLabs')) { - return true; - } - return visType.stage !== 'experimental'; - }, - }, - }); - } + public readonly savedObjectMetaData: SavedObjectMetaData = { + name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }), + includeFields: ['visState'], + type: 'visualization', + getIconForSavedObject: savedObject => { + return ( + getTypes().get(JSON.parse(savedObject.attributes.visState).type).icon || 'visualizeApp' + ); + }, + getTooltipForSavedObject: savedObject => { + return `${savedObject.attributes.title} (${ + getTypes().get(JSON.parse(savedObject.attributes.visState).type).title + })`; + }, + showSavedObject: savedObject => { + const typeName: string = JSON.parse(savedObject.attributes.visState).type; + const visType = getTypes().get(typeName); + if (!visType) { + return false; + } + if (getUISettings().get('visualize:enableLabs')) { + return true; + } + return visType.stage !== 'experimental'; + }, + }; + constructor() {} public async isEditable() { return getCapabilities().visualize.save as boolean; @@ -93,37 +91,27 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< }); } - public async createFromObject( - vis: Vis, - input: Partial & { id: string }, - parent?: Container - ): Promise { + public createFromInput = async ( + input: VisualizeInput, + parent?: IContainer + ): Promise => { const savedVisualizations = getSavedVisualizationsLoader(); try { - const visId = vis.id as string; + const type = getTypes().get(input.visObject.type); - const editUrl = visId - ? getHttp().basePath.prepend(`/app/kibana${savedVisualizations.urlFor(visId)}`) - : ''; const isLabsEnabled = getUISettings().get('visualize:enableLabs'); - if (!isLabsEnabled && vis.type.stage === 'experimental') { - return new DisabledLabEmbeddable(vis.title, input); + if (!isLabsEnabled && type.stage === 'experimental') { + return new DisabledLabEmbeddable(input.visObject.title, input); } - const indexPattern = vis.data.indexPattern; - const indexPatterns = indexPattern ? [indexPattern] : []; const editable = await this.isEditable(); return new VisualizeEmbeddable( getTimeFilter(), { - vis, - indexPatterns, - editUrl, editable, - appState: input.appState, - uiState: input.uiState, + savedVisualizations, }, input, parent @@ -132,31 +120,51 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< console.error(e); // eslint-disable-line no-console return new ErrorEmbeddable(e, input, parent); } - } + }; - public async createFromSavedObject( + public createFromSavedObject = async ( savedObjectId: string, input: Partial & { id: string }, parent?: Container - ): Promise { + ): Promise => { const savedVisualizations = getSavedVisualizationsLoader(); try { const savedObject = await savedVisualizations.get(savedObjectId); - const vis = new Vis(savedObject.visState.type, await convertToSerializedVis(savedObject)); - return this.createFromObject(vis, input, parent); + const embeddable = await this.create( + { + ...input, + visType: savedObject.visState.type, + visObject: await convertToSerializedVis(savedObject), + }, + parent + ); + if (embeddable === undefined) { + return new ErrorEmbeddable( + i18n.translate('visualizations.errorCreatingVisualizeEmbeddable', { + defaultMessage: 'An error was encountered attempting to create a visualization.', + }), + input + ); + } else { + return embeddable; + } } catch (e) { console.error(e); // eslint-disable-line no-console return new ErrorEmbeddable(e, input, parent); } - } + }; - public async create() { - // 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. - showNewVisModal({ - editorParams: ['addToDashboard'], - }); - return undefined; - } + public create = async (input: VisualizeInput, parent?: IContainer) => { + if (input === undefined) { + // 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. + showNewVisModal({ + editorParams: ['addToDashboard'], + }); + return undefined; + } else { + return this.createFromInput(input, parent); + } + }; } diff --git a/src/plugins/dashboard/public/actions/expand_panel_action.test.tsx b/src/plugins/dashboard/public/actions/expand_panel_action.test.tsx index 22cf854a46623..e9696938b8629 100644 --- a/src/plugins/dashboard/public/actions/expand_panel_action.test.tsx +++ b/src/plugins/dashboard/public/actions/expand_panel_action.test.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { isErrorEmbeddable, EmbeddableFactory } from '../embeddable_plugin'; +import { isErrorEmbeddable } from '../embeddable_plugin'; import { ExpandPanelAction } from './expand_panel_action'; import { DashboardContainer } from '../embeddable'; import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers'; @@ -29,11 +29,16 @@ import { ContactCardEmbeddableOutput, } from '../embeddable_plugin_test_samples'; -const embeddableFactories = new Map(); -embeddableFactories.set( +// eslint-disable-next-line +import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks'; + +const { setup, doStart } = embeddablePluginMock.createInstance(); + +setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, - new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any) + new ContactCardEmbeddableFactory((() => null) as any, {} as any) ); +const start = doStart(); let container: DashboardContainer; let embeddable: ContactCardEmbeddable; @@ -43,9 +48,7 @@ beforeEach(async () => { ExitFullScreenButton: () => null, SavedObjectFinder: () => null, application: {} as any, - embeddable: { - getEmbeddableFactory: (id: string) => embeddableFactories.get(id)!, - } as any, + embeddable: start, inspector: {} as any, notifications: {} as any, overlays: {} as any, diff --git a/src/plugins/dashboard/public/actions/replace_panel_action.test.tsx b/src/plugins/dashboard/public/actions/replace_panel_action.test.tsx index 69346dc8c118a..b386bf0b7311e 100644 --- a/src/plugins/dashboard/public/actions/replace_panel_action.test.tsx +++ b/src/plugins/dashboard/public/actions/replace_panel_action.test.tsx @@ -30,12 +30,15 @@ import { import { coreMock } from '../../../../core/public/mocks'; import { CoreStart } from 'kibana/public'; -const embeddableFactories = new Map(); -embeddableFactories.set( +// eslint-disable-next-line +import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks'; + +const { setup, doStart } = embeddablePluginMock.createInstance(); +setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, - new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any) + new ContactCardEmbeddableFactory((() => null) as any, {} as any) ); -const getEmbeddableFactories = () => embeddableFactories.values(); +const start = doStart(); let container: DashboardContainer; let embeddable: ContactCardEmbeddable; @@ -46,9 +49,7 @@ beforeEach(async () => { ExitFullScreenButton: () => null, SavedObjectFinder: () => null, application: {} as any, - embeddable: { - getEmbeddableFactory: (id: string) => embeddableFactories.get(id)!, - } as any, + embeddable: start, inspector: {} as any, notifications: {} as any, overlays: coreStart.overlays, @@ -87,7 +88,7 @@ test('Executes the replace panel action', async () => { coreStart, SavedObjectFinder, notifications, - getEmbeddableFactories + start.getEmbeddableFactories ); action.execute({ embeddable }); }); @@ -99,7 +100,7 @@ test('Is not compatible when embeddable is not in a dashboard container', async coreStart, SavedObjectFinder, notifications, - getEmbeddableFactories + start.getEmbeddableFactories ); expect( await action.isCompatible({ @@ -118,7 +119,7 @@ test('Execute throws an error when called with an embeddable not in a parent', a coreStart, SavedObjectFinder, notifications, - getEmbeddableFactories + start.getEmbeddableFactories ); async function check() { await action.execute({ embeddable: container }); @@ -133,7 +134,7 @@ test('Returns title', async () => { coreStart, SavedObjectFinder, notifications, - getEmbeddableFactories + start.getEmbeddableFactories ); expect(action.getDisplayName({ embeddable })).toBeDefined(); }); @@ -145,7 +146,7 @@ test('Returns an icon', async () => { coreStart, SavedObjectFinder, notifications, - getEmbeddableFactories + start.getEmbeddableFactories ); expect(action.getIconType({ embeddable })).toBeDefined(); }); diff --git a/src/plugins/dashboard/public/embeddable/dashboard_container.test.tsx b/src/plugins/dashboard/public/embeddable/dashboard_container.test.tsx index 770c46c62e42f..fdcfa62110dd1 100644 --- a/src/plugins/dashboard/public/embeddable/dashboard_container.test.tsx +++ b/src/plugins/dashboard/public/embeddable/dashboard_container.test.tsx @@ -30,14 +30,12 @@ import { ContactCardEmbeddable, ContactCardEmbeddableOutput, } from '../embeddable_plugin_test_samples'; +// eslint-disable-next-line +import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks'; const options: DashboardContainerOptions = { application: {} as any, - embeddable: { - getTriggerCompatibleActions: (() => []) as any, - getEmbeddableFactories: (() => []) as any, - getEmbeddableFactory: undefined as any, - } as any, + embeddable: {} as any, notifications: {} as any, overlays: {} as any, inspector: {} as any, @@ -47,12 +45,12 @@ const options: DashboardContainerOptions = { }; beforeEach(() => { - const embeddableFactories = new Map(); - embeddableFactories.set( + const { setup, doStart } = embeddablePluginMock.createInstance(); + setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, - new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any) + new ContactCardEmbeddableFactory((() => null) as any, {} as any) ); - options.embeddable.getEmbeddableFactory = (id: string) => embeddableFactories.get(id) as any; + options.embeddable = doStart(); }); test('DashboardContainer initializes embeddables', async done => { diff --git a/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx index 0fa62fc875603..16cf0f5c3818b 100644 --- a/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx @@ -23,7 +23,7 @@ import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; import { CoreStart } from '../../../../core/public'; import { ContainerOutput, - EmbeddableFactory, + EmbeddableFactoryDefinition, ErrorEmbeddable, Container, } from '../embeddable_plugin'; @@ -43,21 +43,18 @@ interface StartServices { uiActions: UiActionsStart; } -export class DashboardContainerFactory extends EmbeddableFactory< - DashboardContainerInput, - ContainerOutput -> { +export class DashboardContainerFactory + implements + EmbeddableFactoryDefinition { public readonly isContainerType = true; public readonly type = DASHBOARD_CONTAINER_TYPE; - constructor(private readonly getStartServices: () => Promise) { - super(); - } + constructor(private readonly getStartServices: () => Promise) {} - public async isEditable() { + public isEditable = async () => { const { capabilities } = await this.getStartServices(); return !!capabilities.createNew && !!capabilities.showWriteControls; - } + }; public getDisplayName() { return i18n.translate('dashboard.factory.displayName', { @@ -73,11 +70,11 @@ export class DashboardContainerFactory extends EmbeddableFactory< }; } - public async create( + public create = async ( initialInput: DashboardContainerInput, parent?: Container - ): Promise { + ): Promise => { const services = await this.getStartServices(); return new DashboardContainer(initialInput, services, parent); - } + }; } diff --git a/src/plugins/dashboard/public/embeddable/grid/dashboard_grid.test.tsx b/src/plugins/dashboard/public/embeddable/grid/dashboard_grid.test.tsx index 0f1b9c6dc9307..87465ba4a4e0a 100644 --- a/src/plugins/dashboard/public/embeddable/grid/dashboard_grid.test.tsx +++ b/src/plugins/dashboard/public/embeddable/grid/dashboard_grid.test.tsx @@ -39,7 +39,7 @@ function prepare(props?: Partial) { const embeddableFactories = new Map(); embeddableFactories.set( CONTACT_CARD_EMBEDDABLE, - new ContactCardEmbeddableFactory({} as any, (() => {}) as any, {} as any) + new ContactCardEmbeddableFactory((() => {}) as any, {} as any) ); const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); const initialInput = getSampleDashboardInput({ diff --git a/src/plugins/dashboard/public/embeddable/viewport/dashboard_viewport.test.tsx b/src/plugins/dashboard/public/embeddable/viewport/dashboard_viewport.test.tsx index e3d9b8552f060..f9ba10a8fda42 100644 --- a/src/plugins/dashboard/public/embeddable/viewport/dashboard_viewport.test.tsx +++ b/src/plugins/dashboard/public/embeddable/viewport/dashboard_viewport.test.tsx @@ -44,7 +44,7 @@ function getProps( const embeddableFactories = new Map(); embeddableFactories.set( CONTACT_CARD_EMBEDDABLE, - new ContactCardEmbeddableFactory({}, (() => null) as any, {} as any) + new ContactCardEmbeddableFactory((() => null) as any, {} as any) ); const options: DashboardContainerOptions = { diff --git a/src/plugins/dashboard/public/tests/dashboard_container.test.tsx.rej b/src/plugins/dashboard/public/tests/dashboard_container.test.tsx.rej new file mode 100644 index 0000000000000..a038407f52f0d --- /dev/null +++ b/src/plugins/dashboard/public/tests/dashboard_container.test.tsx.rej @@ -0,0 +1,10 @@ +diff a/src/plugins/dashboard/public/tests/dashboard_container.test.tsx b/src/plugins/dashboard/public/tests/dashboard_container.test.tsx (rejected hunks) +@@ -52,7 +52,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { + uiActionsSetup.addTriggerAction(CONTEXT_MENU_TRIGGER, editModeAction); + setup.registerEmbeddableFactory( + CONTACT_CARD_EMBEDDABLE, +- new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any) ++ new ContactCardEmbeddableFactory((() => null) as any, {} as any) + ); + + const start = doStart(); diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index eca74af4ec253..23275fbe8e8f0 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -38,6 +38,7 @@ export { EmbeddableChildPanel, EmbeddableChildPanelProps, EmbeddableContext, + EmbeddableFactoryDefinition, EmbeddableFactory, EmbeddableFactoryNotFoundError, EmbeddableFactoryRenderer, diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx index 9aeaf34f3311b..151282f62d57c 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx @@ -18,14 +18,15 @@ */ import { EditPanelAction } from './edit_panel_action'; -import { EmbeddableFactory, Embeddable, EmbeddableInput } from '../embeddables'; +import { Embeddable, EmbeddableInput } from '../embeddables'; import { ViewMode } from '../types'; import { ContactCardEmbeddable } from '../test_samples'; import { EmbeddableStart } from '../../plugin'; +import { embeddablePluginMock } from '../../mocks'; -const embeddableFactories = new Map(); -const getFactory = ((id: string) => - embeddableFactories.get(id)) as EmbeddableStart['getEmbeddableFactory']; +const { doStart } = embeddablePluginMock.createInstance(); +const start = doStart(); +const getFactory = start.getEmbeddableFactory; class EditableEmbeddable extends Embeddable { public readonly type = 'EDITABLE_EMBEDDABLE'; @@ -83,9 +84,7 @@ test('is not compatible when edit url is not available', async () => { }); test('is not visible when edit url is available but in view mode', async () => { - embeddableFactories.clear(); - const action = new EditPanelAction((type => - embeddableFactories.get(type)) as EmbeddableStart['getEmbeddableFactory']); + const action = new EditPanelAction(getFactory); expect( await action.isCompatible({ embeddable: new EditableEmbeddable( @@ -100,9 +99,7 @@ test('is not visible when edit url is available but in view mode', async () => { }); test('is not compatible when edit url is available, in edit mode, but not editable', async () => { - embeddableFactories.clear(); - const action = new EditPanelAction((type => - embeddableFactories.get(type)) as EmbeddableStart['getEmbeddableFactory']); + const action = new EditPanelAction(getFactory); expect( await action.isCompatible({ embeddable: new EditableEmbeddable( diff --git a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx index 07915ce59e6ca..d4f138feea2ca 100644 --- a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx @@ -32,16 +32,17 @@ import { // eslint-disable-next-line import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks'; import { mount } from 'enzyme'; +import { embeddablePluginMock } from '../../mocks'; test('EmbeddableChildPanel renders an embeddable when it is done loading', async () => { const inspector = inspectorPluginMock.createStartContract(); - - const embeddableFactories = new Map(); - embeddableFactories.set( + const { setup, doStart } = embeddablePluginMock.createInstance(); + setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, new SlowContactCardEmbeddableFactory({ execAction: (() => null) as any }) ); - const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); + const start = doStart(); + const getEmbeddableFactory = start.getEmbeddableFactory; const container = new HelloWorldContainer({ id: 'hello', panels: {} }, { getEmbeddableFactory, @@ -63,8 +64,8 @@ test('EmbeddableChildPanel renders an embeddable when it is done loading', async container={container} embeddableId={newEmbeddable.id} getActions={() => Promise.resolve([])} - getAllEmbeddableFactories={(() => []) as any} - getEmbeddableFactory={(() => undefined) as any} + getAllEmbeddableFactories={start.getEmbeddableFactories} + getEmbeddableFactory={getEmbeddableFactory} notifications={{} as any} overlays={{} as any} inspector={inspector} diff --git a/src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts b/src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts new file mode 100644 index 0000000000000..c5577f1da5b98 --- /dev/null +++ b/src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts @@ -0,0 +1,49 @@ +/* + * 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 { SavedObjectAttributes } from 'kibana/public'; +import { EmbeddableFactoryDefinition } from './embeddable_factory_definition'; +import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable'; +import { EmbeddableFactory } from './embeddable_factory'; +import { IContainer } from '..'; + +export const defaultEmbeddableFactoryProvider = < + I extends EmbeddableInput = EmbeddableInput, + O extends EmbeddableOutput = EmbeddableOutput, + E extends IEmbeddable = IEmbeddable, + T extends SavedObjectAttributes = SavedObjectAttributes +>( + def: EmbeddableFactoryDefinition +): EmbeddableFactory => { + const factory: EmbeddableFactory = { + isContainerType: def.isContainerType ?? false, + canCreateNew: def.canCreateNew ?? (() => true), + getDefaultInput: def.getDefaultInput ?? (() => ({})), + getExplicitInput: def.getExplicitInput ?? (() => Promise.resolve({})), + createFromSavedObject: + def.createFromSavedObject ?? + ((savedObjectId: string, input: Partial, parent?: IContainer) => { + throw new Error(`Creation from saved object not supported by type ${def.type}`); + }), + create: def.create, + type: def.type, + isEditable: def.isEditable, + getDisplayName: def.getDisplayName, + }; + return factory; +}; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts index 81f7f35c900c9..7949b6fb8ba27 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts @@ -22,32 +22,21 @@ import { SavedObjectMetaData } from '../../../../saved_objects/public'; import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable'; import { ErrorEmbeddable } from './error_embeddable'; import { IContainer } from '../containers/i_container'; +import { PropertySpec } from '../types'; export interface EmbeddableInstanceConfiguration { id: string; savedObjectId?: string; } -export interface PropertySpec { - displayName: string; - accessPath: string; - id: string; - description: string; - value?: string; -} - export interface OutputSpec { [key: string]: PropertySpec; } -export interface EmbeddableFactoryOptions { - savedObjectMetaData?: SavedObjectMetaData; -} - /** - * The EmbeddableFactory creates and initializes an embeddable instance + * EmbeddableFactories create and initialize an embeddable instance */ -export abstract class EmbeddableFactory< +export interface EmbeddableFactory< TEmbeddableInput extends EmbeddableInput = EmbeddableInput, TEmbeddableOutput extends EmbeddableOutput = EmbeddableOutput, TEmbeddable extends IEmbeddable = IEmbeddable< @@ -58,9 +47,15 @@ export abstract class EmbeddableFactory< > { // A unique identified for this factory, which will be used to map an embeddable spec to // a factory that can generate an instance of it. - public abstract readonly type: string; + readonly type: string; + + /** + * Returns whether the current user should be allowed to edit this type of + * embeddable. Most of the time this should be based off the capabilities service, hence it's async. + */ + readonly isEditable: () => Promise; - public readonly savedObjectMetaData?: SavedObjectMetaData; + readonly savedObjectMetaData?: SavedObjectMetaData; /** * True if is this factory create embeddables that are Containers. Used in the add panel to @@ -68,31 +63,19 @@ export abstract class EmbeddableFactory< * supported right now, but once nested containers are officially supported we can probably get * rid of this interface. */ - public readonly isContainerType: boolean = false; - - constructor({ savedObjectMetaData }: EmbeddableFactoryOptions = {}) { - this.savedObjectMetaData = savedObjectMetaData; - } - - /** - * Returns whether the current user should be allowed to edit this type of - * embeddable. Most of the time this should be based off the capabilities service, hence it's async. - */ - public abstract async isEditable(): Promise; + readonly isContainerType: boolean; /** * Returns a display name for this type of embeddable. Used in "Create new... " options * in the add panel for containers. */ - public abstract getDisplayName(): string; + getDisplayName(): string; /** * If false, this type of embeddable can't be created with the "createNew" functionality. Instead, * use createFromSavedObject, where an existing saved object must first exist. */ - public canCreateNew() { - return true; - } + canCreateNew(): boolean; /** * Can be used to get any default input, to be passed in to during the creation process. Default @@ -100,18 +83,14 @@ export abstract class EmbeddableFactory< * default input parameters. * @param partial */ - public getDefaultInput(partial: Partial): Partial { - return {}; - } + getDefaultInput(partial: Partial): Partial; /** * Can be used to request explicit input from the user, to be passed in to `EmbeddableFactory:create`. * Explicit input is stored on the parent container for this embeddable. It overrides any inherited * input passed down from the parent container. */ - public async getExplicitInput(): Promise> { - return {}; - } + getExplicitInput(): Promise>; /** * Creates a new embeddable instance based off the saved object id. @@ -120,13 +99,11 @@ export abstract class EmbeddableFactory< * range of the parent container. * @param parent */ - public createFromSavedObject( + createFromSavedObject( savedObjectId: string, input: Partial, parent?: IContainer - ): Promise { - throw new Error(`Creation from saved object not supported by type ${this.type}`); - } + ): Promise; /** * Resolves to undefined if a new Embeddable cannot be directly created and the user will instead be redirected @@ -134,7 +111,7 @@ export abstract class EmbeddableFactory< * * This will likely change in future iterations when we improve in place editing capabilities. */ - public abstract create( + create( initialInput: TEmbeddableInput, parent?: IContainer ): Promise; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts new file mode 100644 index 0000000000000..b8985f7311ea9 --- /dev/null +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts @@ -0,0 +1,44 @@ +/* + * 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 { SavedObjectAttributes } from 'kibana/server'; +import { IEmbeddable } from './i_embeddable'; +import { EmbeddableFactory } from './embeddable_factory'; +import { EmbeddableInput, EmbeddableOutput } from '..'; + +export type EmbeddableFactoryDefinition< + I extends EmbeddableInput = EmbeddableInput, + O extends EmbeddableOutput = EmbeddableOutput, + E extends IEmbeddable = IEmbeddable, + T extends SavedObjectAttributes = SavedObjectAttributes +> = + // Required parameters + Pick, 'create' | 'type' | 'isEditable' | 'getDisplayName'> & + // Optional parameters + Partial< + Pick< + EmbeddableFactory, + | 'createFromSavedObject' + | 'isContainerType' + | 'getExplicitInput' + | 'savedObjectMetaData' + | 'canCreateNew' + | 'getDefaultInput' + > + >; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_renderer.test.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_renderer.test.tsx index 51b83ea0ecaa3..e27045495af5b 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_renderer.test.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_renderer.test.tsx @@ -21,22 +21,22 @@ import { HELLO_WORLD_EMBEDDABLE, HelloWorldEmbeddableFactory, } from '../../../../../../examples/embeddable_examples/public'; -import { EmbeddableFactory } from './embeddable_factory'; import { EmbeddableFactoryRenderer } from './embeddable_factory_renderer'; import { mount } from 'enzyme'; import { nextTick } from 'test_utils/enzyme_helpers'; // @ts-ignore import { findTestSubject } from '@elastic/eui/lib/test'; -import { EmbeddableStart } from '../../plugin'; +import { embeddablePluginMock } from '../../mocks'; test('EmbeddableFactoryRenderer renders an embeddable', async () => { - const embeddableFactories = new Map(); - embeddableFactories.set(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory()); - const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); + const { setup, doStart } = embeddablePluginMock.createInstance(); + setup.registerEmbeddableFactory(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory()); + + const getEmbeddableFactory = doStart().getEmbeddableFactory; const component = mount( diff --git a/src/plugins/embeddable/public/lib/embeddables/index.ts b/src/plugins/embeddable/public/lib/embeddables/index.ts index 2175c3a59aa58..d65f7c81d25ab 100644 --- a/src/plugins/embeddable/public/lib/embeddables/index.ts +++ b/src/plugins/embeddable/public/lib/embeddables/index.ts @@ -18,11 +18,9 @@ */ export { EmbeddableOutput, EmbeddableInput, IEmbeddable } from './i_embeddable'; export { Embeddable } from './embeddable'; -export { - EmbeddableInstanceConfiguration, - EmbeddableFactory, - OutputSpec, -} from './embeddable_factory'; +export * from './embeddable_factory'; +export * from './embeddable_factory_definition'; +export * from './create_embeddable_factory'; export { ErrorEmbeddable, isErrorEmbeddable } from './error_embeddable'; export { withEmbeddableSubscription } from './with_subscription'; export { EmbeddableFactoryRenderer } from './embeddable_factory_renderer'; diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx index 757d4e6bfddef..4e235d3cd5e3c 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx @@ -43,26 +43,25 @@ import { // eslint-disable-next-line import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks'; import { EuiBadge } from '@elastic/eui'; +import { embeddablePluginMock } from '../../mocks'; const actionRegistry = new Map>(); const triggerRegistry = new Map(); -const embeddableFactories = new Map(); -const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); + +const { setup, doStart } = embeddablePluginMock.createInstance(); const editModeAction = createEditModeAction(); const trigger: Trigger = { id: CONTEXT_MENU_TRIGGER, }; -const embeddableFactory = new ContactCardEmbeddableFactory( - {} as any, - (() => null) as any, - {} as any -); +const embeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); actionRegistry.set(editModeAction.id, editModeAction); triggerRegistry.set(trigger.id, trigger); -embeddableFactories.set(embeddableFactory.type, embeddableFactory); +setup.registerEmbeddableFactory(embeddableFactory.type, embeddableFactory); +const start = doStart(); +const getEmbeddableFactory = start.getEmbeddableFactory; test('HelloWorldContainer initializes embeddables', async done => { const container = new HelloWorldContainer( { @@ -157,8 +156,8 @@ test('HelloWorldContainer in view mode hides edit mode actions', async () => { Promise.resolve([])} - getAllEmbeddableFactories={(() => []) as any} - getEmbeddableFactory={(() => undefined) as any} + getAllEmbeddableFactories={start.getEmbeddableFactories} + getEmbeddableFactory={start.getEmbeddableFactory} notifications={{} as any} overlays={{} as any} inspector={inspector} @@ -195,8 +194,8 @@ const renderInEditModeAndOpenContextMenu = async ( []) as any} - getEmbeddableFactory={(() => undefined) as any} + getAllEmbeddableFactories={start.getEmbeddableFactories} + getEmbeddableFactory={start.getEmbeddableFactory} notifications={{} as any} overlays={{} as any} inspector={inspector} @@ -293,8 +292,8 @@ test('HelloWorldContainer in edit mode shows edit mode actions', async () => { Promise.resolve([])} - getAllEmbeddableFactories={(() => []) as any} - getEmbeddableFactory={(() => undefined) as any} + getAllEmbeddableFactories={start.getEmbeddableFactories} + getEmbeddableFactory={start.getEmbeddableFactory} notifications={{} as any} overlays={{} as any} inspector={inspector} @@ -355,8 +354,8 @@ test('Updates when hidePanelTitles is toggled', async () => { Promise.resolve([])} - getAllEmbeddableFactories={(() => []) as any} - getEmbeddableFactory={(() => undefined) as any} + getAllEmbeddableFactories={start.getEmbeddableFactories} + getEmbeddableFactory={start.getEmbeddableFactory} notifications={{} as any} overlays={{} as any} inspector={inspector} @@ -407,8 +406,8 @@ test('Check when hide header option is false', async () => { Promise.resolve([])} - getAllEmbeddableFactories={(() => []) as any} - getEmbeddableFactory={(() => undefined) as any} + getAllEmbeddableFactories={start.getEmbeddableFactories} + getEmbeddableFactory={start.getEmbeddableFactory} notifications={{} as any} overlays={{} as any} inspector={inspector} @@ -444,8 +443,8 @@ test('Check when hide header option is true', async () => { Promise.resolve([])} - getAllEmbeddableFactories={(() => []) as any} - getEmbeddableFactory={(() => undefined) as any} + getAllEmbeddableFactories={start.getEmbeddableFactories} + getEmbeddableFactory={start.getEmbeddableFactory} notifications={{} as any} overlays={{} as any} inspector={inspector} diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx index 8ee8c8dad9df3..74b08535bf27a 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx @@ -19,7 +19,6 @@ import { ViewMode, EmbeddableOutput, isErrorEmbeddable } from '../../../../'; import { AddPanelAction } from './add_panel_action'; -import { EmbeddableFactory } from '../../../../embeddables'; import { FILTERABLE_EMBEDDABLE, FilterableEmbeddable, @@ -31,11 +30,12 @@ import { FilterableContainer } from '../../../../test_samples/embeddables/filter import { coreMock } from '../../../../../../../../core/public/mocks'; import { ContactCardEmbeddable } from '../../../../test_samples'; import { esFilters, Filter } from '../../../../../../../../plugins/data/public'; -import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; +import { EmbeddableStart } from '../../../../../plugin'; +import { embeddablePluginMock } from '../../../../../mocks'; -const embeddableFactories = new Map(); -embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); -const getFactory = (id: string) => embeddableFactories.get(id); +const { setup, doStart } = embeddablePluginMock.createInstance(); +setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); +const getFactory = doStart().getEmbeddableFactory; let container: FilterableContainer; let embeddable: FilterableEmbeddable; diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx index 2fa21e40ca0f0..282b0f05891e0 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx @@ -31,7 +31,7 @@ import { ReactWrapper } from 'enzyme'; import { coreMock } from '../../../../../../../../core/public/mocks'; // @ts-ignore import { findTestSubject } from '@elastic/eui/lib/test'; -import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; +import { embeddablePluginMock } from '../../../../../mocks'; function DummySavedObjectFinder(props: { children: React.ReactNode }) { return ( @@ -43,10 +43,10 @@ function DummySavedObjectFinder(props: { children: React.ReactNode }) { } test('createNewEmbeddable() add embeddable to container', async () => { + const { setup, doStart } = embeddablePluginMock.createInstance(); const core = coreMock.createStart(); const { overlays } = core; const contactCardEmbeddableFactory = new ContactCardEmbeddableFactory( - {}, (() => null) as any, overlays ); @@ -55,7 +55,9 @@ test('createNewEmbeddable() add embeddable to container', async () => { firstName: 'foo', lastName: 'bar', } as any); - const getEmbeddableFactory = (id: string) => contactCardEmbeddableFactory; + setup.registerEmbeddableFactory(CONTACT_CARD_EMBEDDABLE, contactCardEmbeddableFactory); + const start = doStart(); + const getEmbeddableFactory = start.getEmbeddableFactory; const input: ContainerInput<{ firstName: string; lastName: string }> = { id: '1', panels: {}, @@ -66,8 +68,8 @@ test('createNewEmbeddable() add embeddable to container', async () => { new Set([contactCardEmbeddableFactory]).values()} + getFactory={getEmbeddableFactory} + getAllFactories={start.getEmbeddableFactories} notifications={core.notifications} SavedObjectFinder={() => null} /> @@ -88,10 +90,10 @@ test('createNewEmbeddable() add embeddable to container', async () => { }); test('selecting embeddable in "Create new ..." list calls createNewEmbeddable()', async () => { + const { setup, doStart } = embeddablePluginMock.createInstance(); const core = coreMock.createStart(); const { overlays } = core; const contactCardEmbeddableFactory = new ContactCardEmbeddableFactory( - {}, (() => null) as any, overlays ); @@ -100,8 +102,10 @@ test('selecting embeddable in "Create new ..." list calls createNewEmbeddable()' firstName: 'foo', lastName: 'bar', } as any); - const getEmbeddableFactory = ((id: string) => - contactCardEmbeddableFactory) as EmbeddableStart['getEmbeddableFactory']; + + setup.registerEmbeddableFactory(CONTACT_CARD_EMBEDDABLE, contactCardEmbeddableFactory); + const start = doStart(); + const getEmbeddableFactory = start.getEmbeddableFactory; const input: ContainerInput<{ firstName: string; lastName: string }> = { id: '1', panels: {}, @@ -113,7 +117,7 @@ test('selecting embeddable in "Create new ..." list calls createNewEmbeddable()' container={container} onClose={onClose} getFactory={getEmbeddableFactory} - getAllFactories={() => new Set([contactCardEmbeddableFactory]).values()} + getAllFactories={start.getEmbeddableFactories} notifications={core.notifications} SavedObjectFinder={props => } /> diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts index 3f7c917cd1617..2f66d8eb0d619 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts @@ -32,18 +32,19 @@ import { ContactCardEmbeddableFactory, } from '../../../../test_samples/embeddables/contact_card/contact_card_embeddable_factory'; import { HelloWorldContainer } from '../../../../test_samples/embeddables/hello_world_container'; -import { EmbeddableFactory } from '../../../../embeddables'; +import { embeddablePluginMock } from '../../../../../mocks'; let container: Container; let embeddable: ContactCardEmbeddable; function createHelloWorldContainer(input = { id: '123', panels: {} }) { - const embeddableFactories = new Map(); - const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); - embeddableFactories.set( + const { setup, doStart } = embeddablePluginMock.createInstance(); + setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, - new ContactCardEmbeddableFactory({}, (() => {}) as any, {} as any) + new ContactCardEmbeddableFactory((() => {}) as any, {} as any) ); + const getEmbeddableFactory = doStart().getEmbeddableFactory; + return new HelloWorldContainer(input, { getEmbeddableFactory } as any); } diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx index e19acda8419da..ee31127cb5a40 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx @@ -28,20 +28,16 @@ import { } from '../../../test_samples'; // eslint-disable-next-line import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks'; -import { - EmbeddableFactory, - EmbeddableOutput, - isErrorEmbeddable, - ErrorEmbeddable, -} from '../../../embeddables'; +import { EmbeddableOutput, isErrorEmbeddable, ErrorEmbeddable } from '../../../embeddables'; import { of } from '../../../../tests/helpers'; import { esFilters } from '../../../../../../../plugins/data/public'; -import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; +import { embeddablePluginMock } from '../../../../mocks'; +import { EmbeddableStart } from '../../../../plugin'; -const setup = async () => { - const embeddableFactories = new Map(); - embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); - const getFactory = (id: string) => embeddableFactories.get(id); +const setupTests = async () => { + const { setup, doStart } = embeddablePluginMock.createInstance(); + setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); + const getFactory = doStart().getEmbeddableFactory; const container = new FilterableContainer( { id: 'hello', @@ -79,7 +75,7 @@ test('Is compatible when inspector adapters are available', async () => { const inspector = inspectorPluginMock.createStartContract(); inspector.isAvailable.mockImplementation(() => true); - const { embeddable } = await setup(); + const { embeddable } = await setupTests(); const inspectAction = new InspectPanelAction(inspector); expect(await inspectAction.isCompatible({ embeddable })).toBe(true); @@ -114,7 +110,7 @@ test('Executes when inspector adapters are available', async () => { const inspector = inspectorPluginMock.createStartContract(); inspector.isAvailable.mockImplementation(() => true); - const { embeddable } = await setup(); + const { embeddable } = await setupTests(); const inspectAction = new InspectPanelAction(inspector); expect(inspector.open).toHaveBeenCalledTimes(0); diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx index f4d5aa148373b..dea4a88bda082 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx @@ -19,7 +19,6 @@ import { EmbeddableOutput, isErrorEmbeddable } from '../../../'; import { RemovePanelAction } from './remove_panel_action'; -import { EmbeddableFactory } from '../../../embeddables'; import { EmbeddableStart } from '../../../../plugin'; import { FILTERABLE_EMBEDDABLE, @@ -31,11 +30,11 @@ import { FilterableContainer } from '../../../test_samples/embeddables/filterabl import { ViewMode } from '../../../types'; import { ContactCardEmbeddable } from '../../../test_samples/embeddables/contact_card/contact_card_embeddable'; import { esFilters, Filter } from '../../../../../../../plugins/data/public'; +import { embeddablePluginMock } from '../../../../mocks'; -const embeddableFactories = new Map(); -embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); -const getFactory = (id: string) => embeddableFactories.get(id); - +const { setup, doStart } = embeddablePluginMock.createInstance(); +setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); +const getFactory = doStart().getEmbeddableFactory; let container: FilterableContainer; let embeddable: FilterableEmbeddable; diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx index 20a5a8112f4d3..f977329562b9b 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx @@ -23,24 +23,21 @@ import { UiActionsStart } from 'src/plugins/ui_actions/public'; import { CoreStart } from 'src/core/public'; import { toMountPoint } from '../../../../../../kibana_react/public'; -import { EmbeddableFactory } from '../../../embeddables'; +import { EmbeddableFactoryDefinition } from '../../../embeddables'; import { Container } from '../../../containers'; import { ContactCardEmbeddable, ContactCardEmbeddableInput } from './contact_card_embeddable'; import { ContactCardInitializer } from './contact_card_initializer'; -import { EmbeddableFactoryOptions } from '../../../embeddables/embeddable_factory'; export const CONTACT_CARD_EMBEDDABLE = 'CONTACT_CARD_EMBEDDABLE'; -export class ContactCardEmbeddableFactory extends EmbeddableFactory { +export class ContactCardEmbeddableFactory + implements EmbeddableFactoryDefinition { public readonly type = CONTACT_CARD_EMBEDDABLE; constructor( - options: EmbeddableFactoryOptions, private readonly execTrigger: UiActionsStart['executeTriggerActions'], private readonly overlays: CoreStart['overlays'] - ) { - super(options); - } + ) {} public async isEditable() { return true; @@ -52,7 +49,7 @@ export class ContactCardEmbeddableFactory extends EmbeddableFactory> { + public getExplicitInput = (): Promise> => { return new Promise(resolve => { const modalSession = this.overlays.openModal( toMountPoint( @@ -72,9 +69,9 @@ export class ContactCardEmbeddableFactory extends EmbeddableFactory { return new ContactCardEmbeddable( initialInput, { @@ -82,5 +79,5 @@ export class ContactCardEmbeddableFactory extends EmbeddableFactory { +export class SlowContactCardEmbeddableFactory + implements EmbeddableFactoryDefinition { private loadTickCount = 0; public readonly type = CONTACT_CARD_EMBEDDABLE; constructor(private readonly options: SlowContactCardEmbeddableFactoryOptions) { - super(); if (options.loadTickCount) { this.loadTickCount = options.loadTickCount; } @@ -48,10 +46,10 @@ export class SlowContactCardEmbeddableFactory extends EmbeddableFactory< return 'slow to load contact card'; } - public async create(initialInput: ContactCardEmbeddableInput, parent?: Container) { + public create = async (initialInput: ContactCardEmbeddableInput, parent?: Container) => { for (let i = 0; i < this.loadTickCount; i++) { await Promise.resolve(); } return new ContactCardEmbeddable(initialInput, { execAction: this.options.execAction }, parent); - } + }; } diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container_factory.ts b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container_factory.ts index 3488f6a2e038d..f27c7e8b011fd 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container_factory.ts +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container_factory.ts @@ -18,24 +18,21 @@ */ import { i18n } from '@kbn/i18n'; -import { Container, EmbeddableFactory } from '../..'; +import { Container, EmbeddableFactoryDefinition } from '../..'; import { FilterableContainer, FilterableContainerInput, FILTERABLE_CONTAINER, } from './filterable_container'; -import { EmbeddableFactoryOptions } from '../../embeddables/embeddable_factory'; import { EmbeddableStart } from '../../../plugin'; -export class FilterableContainerFactory extends EmbeddableFactory { +export class FilterableContainerFactory + implements EmbeddableFactoryDefinition { public readonly type = FILTERABLE_CONTAINER; constructor( - private readonly getFactory: EmbeddableStart['getEmbeddableFactory'], - options: EmbeddableFactoryOptions = {} - ) { - super(options); - } + private readonly getFactory: () => Promise + ) {} public getDisplayName() { return i18n.translate('embeddableApi.samples.filterableContainer.displayName', { @@ -47,7 +44,8 @@ export class FilterableContainerFactory extends EmbeddableFactory { + const getEmbeddableFactory = await this.getFactory(); + return new FilterableContainer(initialInput, getEmbeddableFactory, parent); + }; } diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable_factory.ts b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable_factory.ts index f37a16ea86c43..4c941ee22abfa 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable_factory.ts +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_embeddable_factory.ts @@ -23,10 +23,11 @@ import { FilterableEmbeddableInput, FILTERABLE_EMBEDDABLE, } from './filterable_embeddable'; -import { EmbeddableFactory } from '../../embeddables'; +import { EmbeddableFactoryDefinition } from '../../embeddables'; import { IContainer } from '../../containers'; -export class FilterableEmbeddableFactory extends EmbeddableFactory { +export class FilterableEmbeddableFactory + implements EmbeddableFactoryDefinition { public readonly type = FILTERABLE_EMBEDDABLE; public async isEditable() { diff --git a/src/plugins/embeddable/public/mocks.ts b/src/plugins/embeddable/public/mocks.ts index ba2f78e42e10e..65676bf3e5a91 100644 --- a/src/plugins/embeddable/public/mocks.ts +++ b/src/plugins/embeddable/public/mocks.ts @@ -30,6 +30,7 @@ export type Start = jest.Mocked; const createSetupContract = (): Setup => { const setupContract: Setup = { registerEmbeddableFactory: jest.fn(), + setEmbeddableFactoryProvider: jest.fn(), }; return setupContract; }; diff --git a/src/plugins/embeddable/public/plugin.test.ts b/src/plugins/embeddable/public/plugin.test.ts index c334411004e2c..f61998dfb2181 100644 --- a/src/plugins/embeddable/public/plugin.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -18,6 +18,8 @@ */ import { coreMock } from '../../../core/public/mocks'; import { testPlugin } from './tests/test_plugin'; +import { EmbeddableFactoryProvider } from './types'; +import { defaultEmbeddableFactoryProvider } from './lib'; test('cannot register embeddable factory with the same ID', async () => { const coreSetup = coreMock.createSetup(); @@ -33,3 +35,26 @@ test('cannot register embeddable factory with the same ID', async () => { 'Embeddable factory [embeddableFactoryId = ID] already registered in Embeddables API.' ); }); + +test('can set custom embeddable factory provider', async () => { + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createStart(); + const { setup, doStart } = testPlugin(coreSetup, coreStart); + + const customProvider: EmbeddableFactoryProvider = def => ({ + ...defaultEmbeddableFactoryProvider(def), + getDisplayName: () => 'Intercepted!', + }); + + setup.setEmbeddableFactoryProvider(customProvider); + setup.registerEmbeddableFactory('test', { + type: 'test', + create: () => Promise.resolve(undefined), + getDisplayName: () => 'Test', + isEditable: () => Promise.resolve(true), + }); + + const start = doStart(); + const factory = start.getEmbeddableFactory('test'); + expect(() => factory!.getDisplayName()).toEqual('Intercepted!'); +}); diff --git a/src/plugins/embeddable/public/plugin.ts b/src/plugins/embeddable/public/plugin.ts index 381665c359ffd..31e7e5c949e27 100644 --- a/src/plugins/embeddable/public/plugin.ts +++ b/src/plugins/embeddable/public/plugin.ts @@ -18,9 +18,16 @@ */ import { UiActionsSetup } from 'src/plugins/ui_actions/public'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; -import { EmbeddableFactoryRegistry } from './types'; +import { EmbeddableFactoryRegistry, EmbeddableFactoryProvider } from './types'; import { bootstrap } from './bootstrap'; -import { EmbeddableFactory, EmbeddableInput, EmbeddableOutput } from './lib'; +import { + EmbeddableFactory, + EmbeddableInput, + EmbeddableOutput, + defaultEmbeddableFactoryProvider, + IEmbeddable, +} from './lib'; +import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition'; export interface EmbeddableSetupDependencies { uiActions: UiActionsSetup; @@ -29,21 +36,29 @@ export interface EmbeddableSetupDependencies { export interface EmbeddableSetup { registerEmbeddableFactory: ( id: string, - factory: EmbeddableFactory + factory: EmbeddableFactoryDefinition ) => void; + setEmbeddableFactoryProvider: (provider: EmbeddableFactoryProvider) => void; } + export interface EmbeddableStart { getEmbeddableFactory: < I extends EmbeddableInput = EmbeddableInput, - O extends EmbeddableOutput = EmbeddableOutput + O extends EmbeddableOutput = EmbeddableOutput, + E extends IEmbeddable = IEmbeddable >( embeddableFactoryId: string - ) => EmbeddableFactory | undefined; + ) => EmbeddableFactory | undefined; getEmbeddableFactories: () => IterableIterator; } export class EmbeddablePublicPlugin implements Plugin { + private readonly embeddableFactoryDefinitions: Map< + string, + EmbeddableFactoryDefinition + > = new Map(); private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map(); + private embeddableFactoryProvider?: EmbeddableFactoryProvider; constructor(initializerContext: PluginInitializerContext) {} @@ -52,10 +67,26 @@ export class EmbeddablePublicPlugin implements Plugin { + if (this.embeddableFactoryProvider) { + throw new Error( + 'Custom embeddable factory provider is already set, and can only be set once' + ); + } + this.embeddableFactoryProvider = provider; + }, }; } - public start(core: CoreStart) { + public start(core: CoreStart): EmbeddableStart { + this.embeddableFactoryDefinitions.forEach(def => { + this.embeddableFactories.set( + def.type, + this.embeddableFactoryProvider + ? this.embeddableFactoryProvider(def) + : defaultEmbeddableFactoryProvider(def) + ); + }); return { getEmbeddableFactory: this.getEmbeddableFactory, getEmbeddableFactories: () => this.embeddableFactories.values(), @@ -64,30 +95,42 @@ export class EmbeddablePublicPlugin implements Plugin { - if (this.embeddableFactories.has(embeddableFactoryId)) { + private registerEmbeddableFactory = ( + embeddableFactoryId: string, + factory: EmbeddableFactoryDefinition + ) => { + if (this.embeddableFactoryDefinitions.has(embeddableFactoryId)) { throw new Error( `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] already registered in Embeddables API.` ); } - - this.embeddableFactories.set(embeddableFactoryId, factory); + this.embeddableFactoryDefinitions.set(embeddableFactoryId, factory); }; private getEmbeddableFactory = < I extends EmbeddableInput = EmbeddableInput, - O extends EmbeddableOutput = EmbeddableOutput + O extends EmbeddableOutput = EmbeddableOutput, + E extends IEmbeddable = IEmbeddable >( embeddableFactoryId: string - ) => { - const factory = this.embeddableFactories.get(embeddableFactoryId); + ): EmbeddableFactory => { + let factory = this.embeddableFactories.get(embeddableFactoryId); if (!factory) { - throw new Error( - `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] does not exist.` - ); + // This is only needed for legacy plugins registering embeddable factories after this start method. + const def = this.embeddableFactoryDefinitions.get(embeddableFactoryId); + if (def) { + factory = this.embeddableFactoryProvider + ? this.embeddableFactoryProvider(def) + : defaultEmbeddableFactoryProvider(def); + this.embeddableFactories.set(def.type, factory); + } else { + throw new Error( + `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] does not exist.` + ); + } } - return factory as EmbeddableFactory; + return factory as EmbeddableFactory; }; } diff --git a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts index 6beef35bbe136..54f3ac2887f6c 100644 --- a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts +++ b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts @@ -36,13 +36,13 @@ import { esFilters } from '../../../../plugins/data/public'; test('ApplyFilterAction applies the filter to the root of the container tree', async () => { const { doStart, setup } = testPlugin(); - const api = doStart(); - const factory1 = new FilterableContainerFactory(api.getEmbeddableFactory); const factory2 = new FilterableEmbeddableFactory(); - - setup.registerEmbeddableFactory(factory1.type, factory1); + const factory1 = new FilterableContainerFactory(async () => await api.getEmbeddableFactory); setup.registerEmbeddableFactory(factory2.type, factory2); + setup.registerEmbeddableFactory(factory1.type, factory1); + + const api = doStart(); const applyFilterAction = createFilterAction(); @@ -63,7 +63,9 @@ test('ApplyFilterAction applies the filter to the root of the container tree', a FilterableContainer >(FILTERABLE_CONTAINER, { panels: {}, id: 'Node2' }); - if (isErrorEmbeddable(node1) || isErrorEmbeddable(node2)) throw new Error(); + if (isErrorEmbeddable(node1) || isErrorEmbeddable(node2)) { + throw new Error(); + } const embeddable = await node2.addNewEmbeddable< FilterableEmbeddableInput, @@ -94,9 +96,11 @@ test('ApplyFilterAction applies the filter to the root of the container tree', a test('ApplyFilterAction is incompatible if the root container does not accept a filter as input', async () => { const { doStart, coreStart, setup } = testPlugin(); - const api = doStart(); const inspector = inspectorPluginMock.createStartContract(); + const factory = new FilterableEmbeddableFactory(); + setup.registerEmbeddableFactory(factory.type, factory); + const api = doStart(); const applyFilterAction = createFilterAction(); const parent = new HelloWorldContainer( { id: 'root', panels: {} }, @@ -110,10 +114,6 @@ test('ApplyFilterAction is incompatible if the root container does not accept a SavedObjectFinder: () => null, } ); - - const factory = new FilterableEmbeddableFactory(); - setup.registerEmbeddableFactory(factory.type, factory); - const embeddable = await parent.addNewEmbeddable< FilterableContainerInput, EmbeddableOutput, @@ -130,12 +130,12 @@ test('ApplyFilterAction is incompatible if the root container does not accept a test('trying to execute on incompatible context throws an error ', async () => { const { doStart, coreStart, setup } = testPlugin(); - const api = doStart(); const inspector = inspectorPluginMock.createStartContract(); const factory = new FilterableEmbeddableFactory(); setup.registerEmbeddableFactory(factory.type, factory); + const api = doStart(); const applyFilterAction = createFilterAction(); const parent = new HelloWorldContainer( { id: 'root', panels: {} }, diff --git a/src/plugins/embeddable/public/tests/container.test.ts b/src/plugins/embeddable/public/tests/container.test.ts index 1ee52f4749135..87076399465d3 100644 --- a/src/plugins/embeddable/public/tests/container.test.ts +++ b/src/plugins/embeddable/public/tests/container.test.ts @@ -56,8 +56,6 @@ async function creatHelloWorldContainerAndEmbeddable( const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); const { setup, doStart, uiActions } = testPlugin(coreSetup, coreStart); - const start = doStart(); - const filterableFactory = new FilterableEmbeddableFactory(); const slowContactCardFactory = new SlowContactCardEmbeddableFactory({ execAction: uiActions.executeTriggerActions, @@ -68,6 +66,8 @@ async function creatHelloWorldContainerAndEmbeddable( setup.registerEmbeddableFactory(slowContactCardFactory.type, slowContactCardFactory); setup.registerEmbeddableFactory(helloWorldFactory.type, helloWorldFactory); + const start = doStart(); + const container = new HelloWorldContainer(containerInput, { getActions: uiActions.getTriggerCompatibleActions, getEmbeddableFactory: start.getEmbeddableFactory, @@ -563,6 +563,13 @@ test('Container changes made directly after adding a new embeddable are propagat const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); const { setup, doStart, uiActions } = testPlugin(coreSetup, coreStart); + + const factory = new SlowContactCardEmbeddableFactory({ + loadTickCount: 3, + execAction: uiActions.executeTriggerActions, + }); + setup.registerEmbeddableFactory(factory.type, factory); + const start = doStart(); const container = new HelloWorldContainer( @@ -582,12 +589,6 @@ test('Container changes made directly after adding a new embeddable are propagat } ); - const factory = new SlowContactCardEmbeddableFactory({ - loadTickCount: 3, - execAction: uiActions.executeTriggerActions, - }); - setup.registerEmbeddableFactory(factory.type, factory); - const subscription = Rx.merge(container.getOutput$(), container.getInput$()) .pipe(skip(2)) .subscribe(() => { @@ -759,12 +760,13 @@ test('untilEmbeddableLoaded resolves with undefined if child is subsequently rem coreMock.createSetup(), coreMock.createStart() ); - const start = doStart(); const factory = new SlowContactCardEmbeddableFactory({ loadTickCount: 3, execAction: uiActions.executeTriggerActions, }); setup.registerEmbeddableFactory(factory.type, factory); + + const start = doStart(); const container = new HelloWorldContainer( { id: 'hello', @@ -799,12 +801,12 @@ test('adding a panel then subsequently removing it before its loaded removes the coreMock.createSetup(), coreMock.createStart() ); - const start = doStart(); const factory = new SlowContactCardEmbeddableFactory({ loadTickCount: 1, execAction: uiActions.executeTriggerActions, }); setup.registerEmbeddableFactory(factory.type, factory); + const start = doStart(); const container = new HelloWorldContainer( { id: 'hello', diff --git a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx index 99d5a7c747d15..19e461b8bde7e 100644 --- a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx +++ b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx @@ -47,15 +47,14 @@ beforeEach(async () => { coreMock.createSetup(), coreMock.createStart() ); - api = doStart(); const contactCardFactory = new ContactCardEmbeddableFactory( - {}, uiActions.executeTriggerActions, {} as any ); setup.registerEmbeddableFactory(contactCardFactory.type, contactCardFactory); + api = doStart(); container = new HelloWorldContainer( { id: '123', panels: {} }, { diff --git a/src/plugins/embeddable/public/tests/explicit_input.test.ts b/src/plugins/embeddable/public/tests/explicit_input.test.ts index f0a7219531b59..0e03db3ec8358 100644 --- a/src/plugins/embeddable/public/tests/explicit_input.test.ts +++ b/src/plugins/embeddable/public/tests/explicit_input.test.ts @@ -41,7 +41,6 @@ const { setup, doStart, coreStart, uiActions } = testPlugin( coreMock.createSetup(), coreMock.createStart() ); -const start = doStart(); setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); const factory = new SlowContactCardEmbeddableFactory({ @@ -51,6 +50,8 @@ const factory = new SlowContactCardEmbeddableFactory({ setup.registerEmbeddableFactory(CONTACT_CARD_EMBEDDABLE, factory); setup.registerEmbeddableFactory(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory()); +const start = doStart(); + test('Explicit embeddable input mapped to undefined will default to inherited', async () => { const derivedFilter: Filter = { $state: { store: esFilters.FilterStateStore.APP_STATE }, diff --git a/src/plugins/embeddable/public/tests/get_embeddable_factories.test.ts b/src/plugins/embeddable/public/tests/get_embeddable_factories.test.ts index 1a222cd548de7..1989d6356cbd1 100644 --- a/src/plugins/embeddable/public/tests/get_embeddable_factories.test.ts +++ b/src/plugins/embeddable/public/tests/get_embeddable_factories.test.ts @@ -35,16 +35,16 @@ test('returns empty list if there are no embeddable factories', () => { test('returns existing embeddable factories', () => { const { setup, doStart } = testPlugin(); - const start = doStart(); - const { length } = [...start.getEmbeddableFactories()]; - const factory1 = new FilterableContainerFactory(start.getEmbeddableFactory); - const factory2 = new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any); + const factory1 = new FilterableContainerFactory(async () => await start.getEmbeddableFactory); + const factory2 = new ContactCardEmbeddableFactory((() => null) as any, {} as any); setup.registerEmbeddableFactory(factory1.type, factory1); setup.registerEmbeddableFactory(factory2.type, factory2); + const start = doStart(); + const list = [...start.getEmbeddableFactories()]; - expect(list.length - length).toBe(2); + expect(list.length).toBe(2); expect(!!list.find(({ type }) => factory1.type === type)).toBe(true); expect(!!list.find(({ type }) => factory2.type === type)).toBe(true); }); diff --git a/src/plugins/embeddable/public/types.ts b/src/plugins/embeddable/public/types.ts index 7a879c389f3c4..2d112b2359818 100644 --- a/src/plugins/embeddable/public/types.ts +++ b/src/plugins/embeddable/public/types.ts @@ -17,6 +17,22 @@ * under the License. */ -import { EmbeddableFactory } from './lib/embeddables'; +import { SavedObjectAttributes } from 'kibana/public'; +import { + EmbeddableFactory, + EmbeddableInput, + EmbeddableOutput, + IEmbeddable, + EmbeddableFactoryDefinition, +} from './lib/embeddables'; export type EmbeddableFactoryRegistry = Map; + +export type EmbeddableFactoryProvider = < + I extends EmbeddableInput = EmbeddableInput, + O extends EmbeddableOutput = EmbeddableOutput, + E extends IEmbeddable = IEmbeddable, + T extends SavedObjectAttributes = SavedObjectAttributes +>( + def: EmbeddableFactoryDefinition +) => EmbeddableFactory; diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx index f8625e4490e51..fd07416cadbc5 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx @@ -29,7 +29,6 @@ import { import { DASHBOARD_CONTAINER_TYPE, DashboardContainer, - DashboardContainerFactory, DashboardContainerInput, } from '../../../../../../../../src/plugins/dashboard/public'; @@ -70,8 +69,9 @@ export class DashboardContainerExample extends React.Component { this.mounted = true; const dashboardFactory = this.props.getEmbeddableFactory< DashboardContainerInput, - ContainerOutput - >(DASHBOARD_CONTAINER_TYPE) as DashboardContainerFactory; + ContainerOutput, + DashboardContainer + >(DASHBOARD_CONTAINER_TYPE); if (dashboardFactory) { this.container = await dashboardFactory.create(dashboardInput); if (this.mounted) { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index 2bde698e23562..215548658ea47 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -18,7 +18,7 @@ import { } from '../../../../../../../src/plugins/data/public'; import { ReactExpressionRendererType } from '../../../../../../../src/plugins/expressions/public'; import { - EmbeddableFactory as AbstractEmbeddableFactory, + EmbeddableFactoryDefinition, ErrorEmbeddable, EmbeddableInput, IContainer, @@ -36,20 +36,17 @@ interface StartServices { indexPatternService: IndexPatternsContract; } -export class EmbeddableFactory extends AbstractEmbeddableFactory { +export class EmbeddableFactory implements EmbeddableFactoryDefinition { type = DOC_TYPE; + savedObjectMetaData = { + name: i18n.translate('xpack.lens.lensSavedObjectLabel', { + defaultMessage: 'Lens Visualization', + }), + type: DOC_TYPE, + getIconForSavedObject: () => 'lensApp', + }; - constructor(private getStartServices: () => Promise) { - super({ - savedObjectMetaData: { - name: i18n.translate('xpack.lens.lensSavedObjectLabel', { - defaultMessage: 'Lens Visualization', - }), - type: DOC_TYPE, - getIconForSavedObject: () => 'lensApp', - }, - }); - } + constructor(private getStartServices: () => Promise) {} public async isEditable() { const { capabilities } = await this.getStartServices(); diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js index fcbae894dffa4..5372c210ada9a 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js @@ -8,10 +8,7 @@ import _ from 'lodash'; import chrome from 'ui/chrome'; import { capabilities } from 'ui/capabilities'; import { i18n } from '@kbn/i18n'; -import { - EmbeddableFactory, - ErrorEmbeddable, -} from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { ErrorEmbeddable } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { setup } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { MapEmbeddable } from './map_embeddable'; import { getIndexPatternService } from '../kibana_services'; @@ -27,23 +24,21 @@ import '../angular/services/gis_map_saved_object_loader'; import { bindSetupCoreAndPlugins, bindStartCoreAndPlugins } from '../plugin'; import { npSetup, npStart } from 'ui/new_platform'; -export class MapEmbeddableFactory extends EmbeddableFactory { +export class MapEmbeddableFactory { type = MAP_SAVED_OBJECT_TYPE; - + savedObjectMetaData = { + name: i18n.translate('xpack.maps.mapSavedObjectLabel', { + defaultMessage: 'Map', + }), + type: MAP_SAVED_OBJECT_TYPE, + getIconForSavedObject: () => APP_ICON, + }; constructor() { - super({ - savedObjectMetaData: { - name: i18n.translate('xpack.maps.mapSavedObjectLabel', { - defaultMessage: 'Map', - }), - type: MAP_SAVED_OBJECT_TYPE, - getIconForSavedObject: () => APP_ICON, - }, - }); // Init required services. Necessary while in legacy bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins); bindStartCoreAndPlugins(npStart.core, npStart.plugins); } + isEditable() { return capabilities.get().maps.save; } diff --git a/x-pack/plugins/advanced_ui_actions/public/custom_time_range_action.test.ts b/x-pack/plugins/advanced_ui_actions/public/custom_time_range_action.test.ts index 91b829a020048..052bb838c0a37 100644 --- a/x-pack/plugins/advanced_ui_actions/public/custom_time_range_action.test.ts +++ b/x-pack/plugins/advanced_ui_actions/public/custom_time_range_action.test.ts @@ -316,7 +316,9 @@ test(`badge is compatible with embeddable that inherits from parent`, async () = test('Attempting to execute on incompatible embeddable throws an error', async () => { const embeddableFactories = new Map(); - embeddableFactories.set(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory()); + embeddableFactories.set(HELLO_WORLD_EMBEDDABLE, { + ...new HelloWorldEmbeddableFactory(), + } as EmbeddableFactory); const container = new HelloWorldContainer( { panels: { diff --git a/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts b/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts index 311d3357476b9..9ce1c6e7ce0d9 100644 --- a/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts +++ b/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts @@ -8,6 +8,7 @@ import { EmbeddableInput, IContainer, EmbeddableFactory, + ErrorEmbeddable, } from '../../../../../src/plugins/embeddable/public'; import { TimeRange } from '../../../../../src/plugins/data/public'; import { TIME_RANGE_EMBEDDABLE, TimeRangeEmbeddable } from './time_range_embeddable'; @@ -16,13 +17,38 @@ interface EmbeddableTimeRangeInput extends EmbeddableInput { timeRange: TimeRange; } -export class TimeRangeEmbeddableFactory extends EmbeddableFactory { +export class TimeRangeEmbeddableFactory implements EmbeddableFactory { public readonly type = TIME_RANGE_EMBEDDABLE; + public readonly isContainerType = false; public async isEditable() { return true; } + public getDefaultInput() { + return {}; + } + + public getExplicitInput() { + return Promise.resolve({}); + } + + public canCreateNew() { + return true; + } + + public createFromSavedObject( + savedObjectId: string, + input: Partial, + parent?: IContainer + ) { + return Promise.resolve( + new ErrorEmbeddable(`Creation from saved object not supported by type ${this.type}`, { + id: '', + }) + ); + } + public async create(initialInput: EmbeddableTimeRangeInput, parent?: IContainer) { return new TimeRangeEmbeddable(initialInput, parent); } diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts index c8e038869efcd..601f8a6bdc2c1 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts @@ -6,13 +6,13 @@ import { i18n } from '@kbn/i18n'; import { - EmbeddableFactory, IContainer, EmbeddableInput, + EmbeddableFactoryDefinition, } from '../../../../../../src/plugins/embeddable/public'; import { ResolverEmbeddable } from './embeddable'; -export class ResolverEmbeddableFactory extends EmbeddableFactory { +export class ResolverEmbeddableFactory implements EmbeddableFactoryDefinition { public readonly type = 'resolver'; public async isEditable() { From ce18bb19557dabbac528e52c1035eb91714c9ad1 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Wed, 25 Mar 2020 10:07:19 -0400 Subject: [PATCH 02/21] typescript map embeddable --- .../maps/public/actions/map_actions.d.ts | 44 ++++++++ .../maps/public/actions/ui_actions.d.ts | 13 +++ .../public/angular/get_initial_layers.d.ts | 7 ++ .../connected_components/gis_map/index.d.ts | 9 ++ .../{map_embeddable.js => map_embeddable.tsx} | 100 +++++++++++++++--- ...e_factory.js => map_embeddable_factory.ts} | 37 ++++--- .../merge_input_with_saved_map.d.ts | 9 ++ .../plugins/maps/public/kibana_services.d.ts | 23 ++++ x-pack/legacy/plugins/maps/public/plugin.ts | 1 - .../maps/public/selectors/map_selectors.d.ts | 16 +++ .../maps/public/selectors/ui_selectors.d.ts | 9 ++ .../reducers/non_serializable_instances.d.ts | 12 +++ .../plugins/maps/public/reducers/store.d.ts | 11 ++ 13 files changed, 259 insertions(+), 32 deletions(-) create mode 100644 x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts create mode 100644 x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts create mode 100644 x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts rename x-pack/legacy/plugins/maps/public/embeddable/{map_embeddable.js => map_embeddable.tsx} (75%) rename x-pack/legacy/plugins/maps/public/embeddable/{map_embeddable_factory.js => map_embeddable_factory.ts} (83%) create mode 100644 x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts create mode 100644 x-pack/legacy/plugins/maps/public/kibana_services.d.ts create mode 100644 x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts create mode 100644 x-pack/legacy/plugins/maps/public/selectors/ui_selectors.d.ts create mode 100644 x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts create mode 100644 x-pack/plugins/maps/public/reducers/store.d.ts diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts index 3a61d5affd861..73681c6577fbe 100644 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts +++ b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts @@ -5,6 +5,8 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ +import { Filter, Query } from 'src/plugins/data/public'; +import { AnyAction } from 'redux'; import { LAYER_TYPE } from '../../common/constants'; import { DataMeta, MapFilters } from '../../common/data_request_descriptor_types'; @@ -24,3 +26,45 @@ export function updateSourceProp( value: unknown, newLayerType?: LAYER_TYPE ): void; + +export interface MapCenter { + lat: string; + lon: string; + zoom: unknown; +} + +export function setGotoWithCenter(config: MapCenter): AnyAction; + +export function replaceLayerList(layerList: unknown): AnyAction; + +export interface QueryGroup { + filters: Filter[]; + query?: Query; + timeFilters: unknown; + refresh: unknown; +} + +export function setQuery(query: QueryGroup): AnyAction; + +export interface RefreshConfig { + isPaused: boolean; + interval: unknown; +} + +export function setRefreshConfig(config: RefreshConfig): AnyAction; + +export function disableScrollZoom(): AnyAction; + +export function disableInteractive(disable: boolean): AnyAction; + +export function disableTooltipControl(disable: boolean): AnyAction; + +export function hideToolbarOverlay(hide: boolean): AnyAction; + +export function hideLayerControl(hide: boolean): AnyAction; + +export function hideViewControl(hide: boolean): AnyAction; + +export function setHiddenLayers(layer: unknown): AnyAction; + +export function addLayerWithoutDataSync(layer: unknown): AnyAction; diff --git a/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts new file mode 100644 index 0000000000000..ead09b5d10ca1 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AnyAction } from 'redux'; + +export function setOpenTOCDetails(details: unknown): AnyAction; + +export function setIsLayerTOCOpen(open: boolean): AnyAction; + +export function setReadOnly(readOnly: boolean): AnyAction; diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts new file mode 100644 index 0000000000000..6fc24e601cd70 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export function getInitialLayers(layers?: unknown): unknown[]; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts new file mode 100644 index 0000000000000..8079396162953 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +export const GisMap: React.FC<{ addFilters: unknown; renderTooltipContent: unknown }>; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx similarity index 75% rename from x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js rename to x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index 9af1a135794c0..25d059613f36b 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -10,16 +10,29 @@ import { Provider } from 'react-redux'; import { render, unmountComponentAtNode } from 'react-dom'; import 'mapbox-gl/dist/mapbox-gl.css'; -import { Embeddable } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { APPLY_FILTER_TRIGGER } from '../../../../../../src/plugins/ui_actions/public'; -import { esFilters } from '../../../../../../src/plugins/data/public'; - import { I18nContext } from 'ui/i18n'; +import { npStart } from 'ui/new_platform'; +import { Subscription } from 'rxjs'; +import { Unsubscribe } from 'redux'; +import { + Embeddable, + IContainer, + EmbeddableInput, + EmbeddableOutput, +} from '../../../../../../src/plugins/embeddable/public'; +import { APPLY_FILTER_TRIGGER } from '../../../../../../src/plugins/ui_actions/public'; +import { + esFilters, + IIndexPattern, + TimeRange, + Filter, + Query, + RefreshInterval, +} from '../../../../../../src/plugins/data/public'; import { GisMap } from '../connected_components/gis_map'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { createMapStore } from '../../../../../plugins/maps/public/reducers/store'; -import { npStart } from 'ui/new_platform'; +import { createMapStore, MapStore } from '../../../../../plugins/maps/public/reducers/store'; import { setGotoWithCenter, replaceLayerList, @@ -32,6 +45,7 @@ import { hideLayerControl, hideViewControl, setHiddenLayers, + MapCenter, } from '../actions/map_actions'; import { setReadOnly, setIsLayerTOCOpen, setOpenTOCDetails } from '../actions/ui_actions'; import { getIsLayerTOCOpen, getOpenTOCDetails } from '../selectors/ui_selectors'; @@ -43,10 +57,58 @@ import { import { getMapCenter, getMapZoom, getHiddenLayerIds } from '../selectors/map_selectors'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; -export class MapEmbeddable extends Embeddable { +interface MapConfig { + editUrl?: string; + indexPatterns: IIndexPattern[]; + editable: boolean; + title?: string; + layerList: unknown; +} + +export interface MapInput extends EmbeddableInput { + timeRange?: TimeRange; + filters: Filter[]; + query?: Query; + refresh: unknown; + refreshConfig: RefreshInterval; + isLayerTOCOpen: boolean; + openTOCDetails: unknown; + disableTooltipControl: boolean; + disableInteractive: boolean; + hideToolbarOverlay: boolean; + hideLayerControl: boolean; + hideViewControl: boolean; + mapCenter: MapCenter; + hiddenLayers: unknown; + hideFilterActions: boolean; +} + +export interface MapOutput extends EmbeddableOutput { + indexPatterns: IIndexPattern[]; +} + +export class MapEmbeddable extends Embeddable { type = MAP_SAVED_OBJECT_TYPE; - constructor(config, initialInput, parent, renderTooltipContent, eventHandlers) { + private _renderTooltipContent?: unknown; + private _eventHandlers?: unknown; + private _layerList: unknown; + private _store: MapStore; + private _subscription: Subscription; + private _prevTimeRange?: TimeRange; + private _prevQuery?: Query; + private _prevRefreshConfig?: RefreshInterval; + private _prevFilters?: Filter[]; + private _domNode?: HTMLElement; + private _unsubscribeFromStore?: Unsubscribe; + + constructor( + config: MapConfig, + initialInput: MapInput, + parent?: IContainer, + renderTooltipContent?: unknown, + eventHandlers?: unknown + ) { super( initialInput, { @@ -70,7 +132,7 @@ export class MapEmbeddable extends Embeddable { return getInspectorAdapters(this._store.getState()); } - onContainerStateChanged(containerState) { + onContainerStateChanged(containerState: MapInput) { if ( !_.isEqual(containerState.timeRange, this._prevTimeRange) || !_.isEqual(containerState.query, this._prevQuery) || @@ -84,7 +146,12 @@ export class MapEmbeddable extends Embeddable { } } - _dispatchSetQuery({ query, timeRange, filters, refresh }) { + _dispatchSetQuery({ + query, + timeRange, + filters, + refresh, + }: Pick) { this._prevTimeRange = timeRange; this._prevQuery = query; this._prevFilters = filters; @@ -98,7 +165,7 @@ export class MapEmbeddable extends Embeddable { ); } - _dispatchSetRefreshConfig({ refreshConfig }) { + _dispatchSetRefreshConfig({ refreshConfig }: Pick) { this._prevRefreshConfig = refreshConfig; this._store.dispatch( setRefreshConfig({ @@ -113,7 +180,7 @@ export class MapEmbeddable extends Embeddable { * @param {HTMLElement} domNode * @param {ContainerState} containerState */ - render(domNode) { + render(domNode: HTMLElement) { this._store.dispatch(setEventHandlers(this._eventHandlers)); this._store.dispatch(setReadOnly(true)); this._store.dispatch(disableScrollZoom()); @@ -133,7 +200,6 @@ export class MapEmbeddable extends Embeddable { if (_.has(this.input, 'disableTooltipControl') && this.input.disableTooltipControl) { this._store.dispatch(disableTooltipControl(this.input.disableTooltipControl)); } - if (_.has(this.input, 'hideToolbarOverlay') && this.input.hideToolbarOverlay) { this._store.dispatch(hideToolbarOverlay(this.input.hideToolbarOverlay)); } @@ -182,12 +248,12 @@ export class MapEmbeddable extends Embeddable { }); } - async setLayerList(layerList) { + async setLayerList(layerList: unknown) { this._layerList = layerList; return await this._store.dispatch(replaceLayerList(this._layerList)); } - addFilters = filters => { + addFilters = (filters: Filter[]) => { npStart.plugins.uiActions.executeTriggerActions(APPLY_FILTER_TRIGGER, { embeddable: this, filters, @@ -213,7 +279,7 @@ export class MapEmbeddable extends Embeddable { this._dispatchSetQuery({ query: this._prevQuery, timeRange: this._prevTimeRange, - filters: this._prevFilters, + filters: this._prevFilters ?? [], refresh: true, }); } @@ -233,7 +299,7 @@ export class MapEmbeddable extends Embeddable { mapCenter: { lat: center.lat, lon: center.lon, - zoom: zoom, + zoom, }, }); } diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts similarity index 83% rename from x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js rename to x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts index fcbae894dffa4..d0d519d0d7e80 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -8,12 +8,16 @@ import _ from 'lodash'; import chrome from 'ui/chrome'; import { capabilities } from 'ui/capabilities'; import { i18n } from '@kbn/i18n'; +import { npSetup, npStart } from 'ui/new_platform'; +import { SavedObjectLoader } from 'src/plugins/saved_objects/public'; +import { IIndexPattern } from 'src/plugins/data/public'; import { EmbeddableFactory, ErrorEmbeddable, + IContainer, } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { setup } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; -import { MapEmbeddable } from './map_embeddable'; +import { MapEmbeddable, MapInput } from './map_embeddable'; import { getIndexPatternService } from '../kibana_services'; import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants'; @@ -25,7 +29,6 @@ import { getInitialLayers } from '../angular/get_initial_layers'; import { mergeInputWithSavedMap } from './merge_input_with_saved_map'; import '../angular/services/gis_map_saved_object_loader'; import { bindSetupCoreAndPlugins, bindStartCoreAndPlugins } from '../plugin'; -import { npSetup, npStart } from 'ui/new_platform'; export class MapEmbeddableFactory extends EmbeddableFactory { type = MAP_SAVED_OBJECT_TYPE; @@ -44,8 +47,9 @@ export class MapEmbeddableFactory extends EmbeddableFactory { bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins); bindStartCoreAndPlugins(npStart.core, npStart.plugins); } - isEditable() { - return capabilities.get().maps.save; + + async isEditable() { + return capabilities.get().maps.save as boolean; } // Not supported yet for maps types. @@ -59,12 +63,12 @@ export class MapEmbeddableFactory extends EmbeddableFactory { }); } - async _getIndexPatterns(layerList) { + async _getIndexPatterns(layerList: unknown[]): Promise { // Need to extract layerList from store to get queryable index pattern ids const store = createMapStore(); let queryableIndexPatternIds; try { - layerList.forEach(layerDescriptor => { + layerList.forEach((layerDescriptor: unknown) => { store.dispatch(addLayerWithoutDataSync(layerDescriptor)); }); queryableIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState()); @@ -86,16 +90,16 @@ export class MapEmbeddableFactory extends EmbeddableFactory { } }); const indexPatterns = await Promise.all(promises); - return _.compact(indexPatterns); + return _.compact(indexPatterns) as IIndexPattern[]; } - async _fetchSavedMap(savedObjectId) { + async _fetchSavedMap(savedObjectId: string) { const $injector = await chrome.dangerouslyGetActiveInjector(); - const savedObjectLoader = $injector.get('gisMapSavedObjectLoader'); + const savedObjectLoader = $injector.get('gisMapSavedObjectLoader'); return await savedObjectLoader.get(savedObjectId); } - async createFromSavedObject(savedObjectId, input, parent) { + async createFromSavedObject(savedObjectId: string, input: MapInput, parent?: IContainer) { const savedMap = await this._fetchSavedMap(savedObjectId); const layerList = getInitialLayers(savedMap.layerListJSON); const indexPatterns = await this._getIndexPatterns(layerList); @@ -106,7 +110,7 @@ export class MapEmbeddableFactory extends EmbeddableFactory { title: savedMap.title, editUrl: chrome.addBasePath(createMapPath(savedObjectId)), indexPatterns, - editable: this.isEditable(), + editable: await this.isEditable(), }, input, parent @@ -125,7 +129,13 @@ export class MapEmbeddableFactory extends EmbeddableFactory { return embeddable; } - async createFromState(state, input, parent, renderTooltipContent, eventHandlers) { + async createFromState( + state: { title?: string; layerList?: unknown[] }, + input: MapInput, + parent: IContainer, + renderTooltipContent: unknown, + eventHandlers: unknown + ) { const layerList = state && state.layerList ? state.layerList : getInitialLayers(); const indexPatterns = await this._getIndexPatterns(layerList); @@ -133,7 +143,6 @@ export class MapEmbeddableFactory extends EmbeddableFactory { { layerList, title: state && state.title ? state.title : '', - editUrl: null, indexPatterns, editable: false, }, @@ -144,7 +153,7 @@ export class MapEmbeddableFactory extends EmbeddableFactory { ); } - async create(input) { + async create(input: MapInput) { window.location.href = chrome.addBasePath(createMapPath('')); return new ErrorEmbeddable( 'Maps can only be created with createFromSavedObject or createFromState', diff --git a/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts b/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts new file mode 100644 index 0000000000000..d7eff790b82a1 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MapInput } from './map_embeddable'; + +export function mergeInputWithSavedMap(input: MapInput, savedmap: unknown): Partial; diff --git a/x-pack/legacy/plugins/maps/public/kibana_services.d.ts b/x-pack/legacy/plugins/maps/public/kibana_services.d.ts new file mode 100644 index 0000000000000..89b1fee1aa842 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/kibana_services.d.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IIndexPattern } from 'src/plugins/data/public'; + +export function getIndexPatternService(): { + get: (id: string) => IIndexPattern | undefined; +}; + +export function setLicenseId(args: unknown): void; +export function setInspector(args: unknown): void; +export function setFileUpload(args: unknown): void; +export function setIndexPatternSelect(args: unknown): void; +export function setHttp(args: unknown): void; +export function setTimeFilter(args: unknown): void; +export function setUiSettings(args: unknown): void; +export function setInjectedVarFunc(args: unknown): void; +export function setToasts(args: unknown): void; +export function setIndexPatternService(args: unknown): void; +export function setAutocompleteService(args: unknown): void; diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts index 1f8f83e44a769..86c2bfe7118a1 100644 --- a/x-pack/legacy/plugins/maps/public/plugin.ts +++ b/x-pack/legacy/plugins/maps/public/plugin.ts @@ -24,7 +24,6 @@ import { setToasts, setIndexPatternService, setAutocompleteService, - // @ts-ignore } from './kibana_services'; // @ts-ignore import { setInjectedVarFunc as npSetInjectedVarFunc } from '../../../../plugins/maps/public/kibana_services'; // eslint-disable-line @kbn/eslint/no-restricted-paths diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts new file mode 100644 index 0000000000000..86c819693a35c --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AnyAction } from 'redux'; +import { MapCenter } from '../actions/map_actions'; + +export function getHiddenLayerIds(config: unknown): AnyAction; + +export function getMapZoom(config: unknown): unknown; + +export function getMapCenter(config: unknown): MapCenter; + +export function getQueryableUniqueIndexPatternIds(state: unknown): string[]; diff --git a/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.d.ts b/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.d.ts new file mode 100644 index 0000000000000..7d66e55602238 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.d.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export function getOpenTOCDetails(state: unknown): unknown; + +export function getIsLayerTOCOpen(state: unknown): boolean; diff --git a/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts b/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts new file mode 100644 index 0000000000000..042448a3ce650 --- /dev/null +++ b/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Adapters } from 'src/plugins/inspector/public'; +import { AnyAction } from 'redux'; + +export function setEventHandlers(handlers: unknown): AnyAction; + +export function getInspectorAdapters(args: unknown): Adapters | undefined; diff --git a/x-pack/plugins/maps/public/reducers/store.d.ts b/x-pack/plugins/maps/public/reducers/store.d.ts new file mode 100644 index 0000000000000..ebed396e20399 --- /dev/null +++ b/x-pack/plugins/maps/public/reducers/store.d.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Store } from 'redux'; + +export type MapStore = Store; + +export function createMapStore(): MapStore; From 538a7486a28b5488f135888e665e6e566f524675 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Wed, 25 Mar 2020 12:10:16 -0400 Subject: [PATCH 03/21] More updates --- .../embeddable/search_embeddable_factory.ts | 6 +-- .../public/embeddable/visualize_embeddable.ts | 2 +- .../actions/replace_panel_action.test.tsx | 2 +- .../embeddable/dashboard_container.test.tsx | 2 +- .../embeddable/grid/dashboard_grid.test.tsx | 15 ++++--- .../viewport/dashboard_viewport.test.tsx | 12 +++--- .../public/tests/dashboard_container.test.tsx | 2 +- .../tests/dashboard_container.test.tsx.rej | 10 ----- .../lib/actions/edit_panel_action.test.tsx | 1 - .../embeddable_child_panel.test.tsx | 1 - .../lib/panel/embeddable_panel.test.tsx | 2 +- .../functions/common/saved_visualization.ts | 2 +- .../input_type_to_expression/visualization.ts | 5 ++- .../maps/public/actions/map_actions.d.ts | 6 +-- .../plugins/maps/public/embeddable/README.md | 21 +++++----- .../plugins/maps/public/embeddable/index.ts | 8 ++++ .../maps/public/embeddable/map_embeddable.tsx | 33 +++++++++------ .../embeddable/map_embeddable_factory.ts | 35 ++++------------ x-pack/legacy/plugins/maps/public/index.ts | 3 ++ .../maps/public/selectors/map_selectors.d.ts | 2 +- .../embeddables/embedded_map_helpers.test.tsx | 2 +- .../embeddables/embedded_map_helpers.tsx | 41 +++++++++++-------- .../location_map/embeddables/embedded_map.tsx | 19 +++++---- .../location_map/embeddables/types.ts | 31 -------------- 24 files changed, 120 insertions(+), 143 deletions(-) delete mode 100644 src/plugins/dashboard/public/tests/dashboard_container.test.tsx.rej create mode 100644 x-pack/legacy/plugins/maps/public/embeddable/index.ts delete mode 100644 x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts index 0f190fa586056..6f63ecf9ba3fd 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts @@ -72,11 +72,11 @@ export class SearchEmbeddableFactory }); } - public async createFromSavedObject( + public createFromSavedObject = async ( savedObjectId: string, input: Partial & { id: string; timeRange: TimeRange }, parent?: Container - ): Promise { + ): Promise => { if (!this.$injector) { this.$injector = await this.getInjector(); } @@ -110,7 +110,7 @@ export class SearchEmbeddableFactory console.error(e); // eslint-disable-line no-console return new ErrorEmbeddable(e, input, parent); } - } + }; public async create(input: SearchInput) { return new ErrorEmbeddable('Saved searches can only be created from a saved object', input); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts index 42d19fbeb934a..0c34a38e7dc3e 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts @@ -116,7 +116,7 @@ export class VisualizeEmbeddable extends Embeddable) { - const embeddableFactories = new Map(); - embeddableFactories.set( + const { setup, doStart } = embeddablePluginMock.createInstance(); + setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, - new ContactCardEmbeddableFactory((() => {}) as any, {} as any) + new ContactCardEmbeddableFactory((() => null) as any, {} as any) ); - const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); + const start = doStart(); + + const getEmbeddableFactory = start.getEmbeddableFactory; const initialInput = getSampleDashboardInput({ panels: { '1': { @@ -60,7 +63,7 @@ function prepare(props?: Partial) { application: {} as any, embeddable: { getTriggerCompatibleActions: (() => []) as any, - getEmbeddableFactories: (() => []) as any, + getEmbeddableFactories: start.getEmbeddableFactories, getEmbeddableFactory, } as any, notifications: {} as any, diff --git a/src/plugins/dashboard/public/embeddable/viewport/dashboard_viewport.test.tsx b/src/plugins/dashboard/public/embeddable/viewport/dashboard_viewport.test.tsx index f9ba10a8fda42..be4d2e3851f11 100644 --- a/src/plugins/dashboard/public/embeddable/viewport/dashboard_viewport.test.tsx +++ b/src/plugins/dashboard/public/embeddable/viewport/dashboard_viewport.test.tsx @@ -24,7 +24,6 @@ import { skip } from 'rxjs/operators'; import { mount } from 'enzyme'; import { I18nProvider } from '@kbn/i18n/react'; import { nextTick } from 'test_utils/enzyme_helpers'; -import { EmbeddableFactory } from '../../embeddable_plugin'; import { DashboardViewport, DashboardViewportProps } from './dashboard_viewport'; import { DashboardContainer, DashboardContainerOptions } from '../dashboard_container'; import { getSampleDashboardInput } from '../../test_helpers'; @@ -33,6 +32,8 @@ import { ContactCardEmbeddableFactory, } from '../../embeddable_plugin_test_samples'; import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public'; +// eslint-disable-next-line +import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks'; let dashboardContainer: DashboardContainer | undefined; @@ -41,18 +42,19 @@ const ExitFullScreenButton = () =>
function getProps( props?: Partial ): { props: DashboardViewportProps; options: DashboardContainerOptions } { - const embeddableFactories = new Map(); - embeddableFactories.set( + const { setup, doStart } = embeddablePluginMock.createInstance(); + setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, new ContactCardEmbeddableFactory((() => null) as any, {} as any) ); + const start = doStart(); const options: DashboardContainerOptions = { application: {} as any, embeddable: { getTriggerCompatibleActions: (() => []) as any, - getEmbeddableFactories: (() => []) as any, - getEmbeddableFactory: (id: string) => embeddableFactories.get(id), + getEmbeddableFactories: start.getEmbeddableFactories, + getEmbeddableFactory: start.getEmbeddableFactory, } as any, notifications: {} as any, overlays: {} as any, diff --git a/src/plugins/dashboard/public/tests/dashboard_container.test.tsx b/src/plugins/dashboard/public/tests/dashboard_container.test.tsx index a81d80b440e04..1c72ad34e5446 100644 --- a/src/plugins/dashboard/public/tests/dashboard_container.test.tsx +++ b/src/plugins/dashboard/public/tests/dashboard_container.test.tsx @@ -52,7 +52,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { uiActionsSetup.attachAction(CONTEXT_MENU_TRIGGER, editModeAction); setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, - new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any) + new ContactCardEmbeddableFactory((() => null) as any, {} as any) ); const start = doStart(); diff --git a/src/plugins/dashboard/public/tests/dashboard_container.test.tsx.rej b/src/plugins/dashboard/public/tests/dashboard_container.test.tsx.rej deleted file mode 100644 index a038407f52f0d..0000000000000 --- a/src/plugins/dashboard/public/tests/dashboard_container.test.tsx.rej +++ /dev/null @@ -1,10 +0,0 @@ -diff a/src/plugins/dashboard/public/tests/dashboard_container.test.tsx b/src/plugins/dashboard/public/tests/dashboard_container.test.tsx (rejected hunks) -@@ -52,7 +52,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { - uiActionsSetup.addTriggerAction(CONTEXT_MENU_TRIGGER, editModeAction); - setup.registerEmbeddableFactory( - CONTACT_CARD_EMBEDDABLE, -- new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any) -+ new ContactCardEmbeddableFactory((() => null) as any, {} as any) - ); - - const start = doStart(); diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx index 151282f62d57c..ce733bba6dda5 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx @@ -21,7 +21,6 @@ import { EditPanelAction } from './edit_panel_action'; import { Embeddable, EmbeddableInput } from '../embeddables'; import { ViewMode } from '../types'; import { ContactCardEmbeddable } from '../test_samples'; -import { EmbeddableStart } from '../../plugin'; import { embeddablePluginMock } from '../../mocks'; const { doStart } = embeddablePluginMock.createInstance(); diff --git a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx index d4f138feea2ca..9e47da5cea032 100644 --- a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx @@ -20,7 +20,6 @@ import React from 'react'; import { nextTick } from 'test_utils/enzyme_helpers'; import { EmbeddableChildPanel } from './embeddable_child_panel'; -import { EmbeddableFactory } from '../embeddables'; import { CONTACT_CARD_EMBEDDABLE } from '../test_samples/embeddables/contact_card/contact_card_embeddable_factory'; import { SlowContactCardEmbeddableFactory } from '../test_samples/embeddables/contact_card/slow_contact_card_embeddable_factory'; import { HelloWorldContainer } from '../test_samples/embeddables/hello_world_container'; diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx index 4e235d3cd5e3c..649677dc67c7d 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx @@ -27,7 +27,7 @@ import { I18nProvider } from '@kbn/i18n/react'; import { CONTEXT_MENU_TRIGGER } from '../triggers'; import { Action, UiActionsStart, ActionType } from 'src/plugins/ui_actions/public'; import { Trigger, ViewMode } from '../types'; -import { EmbeddableFactory, isErrorEmbeddable } from '../embeddables'; +import { isErrorEmbeddable } from '../embeddables'; import { EmbeddablePanel } from './embeddable_panel'; import { createEditModeAction } from '../test_samples/actions'; import { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts index 9777eaebb36ed..891fb6d3c1a19 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts @@ -22,7 +22,7 @@ interface Arguments { hideLegend: boolean | null; } -type Output = EmbeddableExpression; +type Output = EmbeddableExpression>; const defaultTimeRange = { from: 'now-15m', diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts index be0dd6a79292f..bea759efcef51 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts @@ -6,7 +6,10 @@ import { VisualizeInput } from 'src/legacy/core_plugins/visualizations/public'; -export function toExpression(input: VisualizeInput): string { +export function toExpression( + // Expressions work "by reference" only. + input: Omit & { id: string } +): string { const expressionParts = [] as string[]; expressionParts.push('savedVisualization'); diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts index 73681c6577fbe..76fc6cc7095a8 100644 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts +++ b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts @@ -28,9 +28,9 @@ export function updateSourceProp( ): void; export interface MapCenter { - lat: string; - lon: string; - zoom: unknown; + lat: number; + lon: number; + zoom: number; } export function setGotoWithCenter(config: MapCenter): AnyAction; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/README.md b/x-pack/legacy/plugins/maps/public/embeddable/README.md index 1de327702fb87..8740739a3e8ef 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/README.md +++ b/x-pack/legacy/plugins/maps/public/embeddable/README.md @@ -30,17 +30,15 @@ ### Creating a Map embeddable from state ``` const factory = new MapEmbeddableFactory(); -const state = { +const input = { layerList: [], // where layerList is same as saved object layerListJSON property (unstringified) title: 'my map', -} -const input = { hideFilterActions: true, isLayerTOCOpen: false, openTOCDetails: ['tfi3f', 'edh66'], mapCenter: { lat: 0.0, lon: 0.0, zoom: 7 } } -const mapEmbeddable = await factory.createFromState(state, input, parent); +const mapEmbeddable = await factory.create(input, parent); ``` #### Customize tooltip @@ -62,7 +60,8 @@ const renderTooltipContent = ({ addFilters, closeTooltip, features, isLocked, lo return
Custom tooltip content
; } -const mapEmbeddable = await factory.createFromState(state, input, parent, renderTooltipContent); +const mapEmbeddable = await factory.create(input, parent) +mapEmbeddable.setRenderTooltipContent(renderTooltipContent); ``` @@ -80,7 +79,9 @@ const eventHandlers = { }, } -const mapEmbeddable = await factory.createFromState(state, input, parent, renderTooltipContent, eventHandlers); +const mapEmbeddable = await factory.create(input, parent); +mapEmbeddable.setRenderTooltipContent(renderTooltipContent); +mapEmbeddable.setEventHandlers(eventHandlers); ``` @@ -90,8 +91,8 @@ Geojson sources will not update unless you modify `__featureCollection` property ``` const factory = new MapEmbeddableFactory(); -const state = { - layerList: [ +const input = { + layerList: [ { 'id': 'gaxya', 'label': 'My geospatial data', @@ -131,14 +132,12 @@ const state = { } ], title: 'my map', -} -const input = { hideFilterActions: true, isLayerTOCOpen: false, openTOCDetails: ['tfi3f', 'edh66'], mapCenter: { lat: 0.0, lon: 0.0, zoom: 7 } } -const mapEmbeddable = await factory.createFromState(state, input, parent); +const mapEmbeddable = await factory.create(input, parent); mapEmbeddable.setLayerList([ { diff --git a/x-pack/legacy/plugins/maps/public/embeddable/index.ts b/x-pack/legacy/plugins/maps/public/embeddable/index.ts new file mode 100644 index 0000000000000..6349964348b11 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/embeddable/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './map_embeddable_factory'; +export * from './map_embeddable'; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index 25d059613f36b..94ffc8abcd293 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -62,25 +62,26 @@ interface MapConfig { indexPatterns: IIndexPattern[]; editable: boolean; title?: string; - layerList: unknown; + layerList: unknown[]; } export interface MapInput extends EmbeddableInput { timeRange?: TimeRange; filters: Filter[]; query?: Query; - refresh: unknown; + refresh?: unknown; refreshConfig: RefreshInterval; isLayerTOCOpen: boolean; - openTOCDetails: unknown; - disableTooltipControl: boolean; - disableInteractive: boolean; - hideToolbarOverlay: boolean; - hideLayerControl: boolean; - hideViewControl: boolean; - mapCenter: MapCenter; - hiddenLayers: unknown; - hideFilterActions: boolean; + openTOCDetails?: unknown; + disableTooltipControl?: boolean; + disableInteractive?: boolean; + hideToolbarOverlay?: boolean; + hideLayerControl?: boolean; + hideViewControl?: boolean; + mapCenter?: MapCenter; + hiddenLayers?: unknown; + hideFilterActions?: boolean; + layerList: unknown[]; } export interface MapOutput extends EmbeddableOutput { @@ -128,6 +129,14 @@ export class MapEmbeddable extends Embeddable { this._subscription = this.getInput$().subscribe(input => this.onContainerStateChanged(input)); } + setRenderTooltipContent = (renderTooltipContent: unknown) => { + this._renderTooltipContent = renderTooltipContent; + }; + + setEventHandlers = (eventHandlers: unknown) => { + this._eventHandlers = eventHandlers; + }; + getInspectorAdapters() { return getInspectorAdapters(this._store.getState()); } @@ -288,7 +297,7 @@ export class MapEmbeddable extends Embeddable { const center = getMapCenter(this._store.getState()); const zoom = getMapZoom(this._store.getState()); - const mapCenter = this.input.mapCenter || {}; + const mapCenter = this.input.mapCenter || undefined; if ( !mapCenter || mapCenter.lat !== center.lat || diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts index d1d73ef254649..0ce5196ef57d2 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -12,8 +12,7 @@ import { npSetup, npStart } from 'ui/new_platform'; import { SavedObjectLoader } from 'src/plugins/saved_objects/public'; import { IIndexPattern } from 'src/plugins/data/public'; import { - EmbeddableFactory, - ErrorEmbeddable, + EmbeddableFactoryDefinition, IContainer, } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { setup } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; @@ -30,7 +29,7 @@ import { mergeInputWithSavedMap } from './merge_input_with_saved_map'; import '../angular/services/gis_map_saved_object_loader'; import { bindSetupCoreAndPlugins, bindStartCoreAndPlugins } from '../plugin'; -export class MapEmbeddableFactory { +export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { type = MAP_SAVED_OBJECT_TYPE; savedObjectMetaData = { name: i18n.translate('xpack.maps.mapSavedObjectLabel', { @@ -96,7 +95,7 @@ export class MapEmbeddableFactory { return await savedObjectLoader.get(savedObjectId); } - async createFromSavedObject(savedObjectId: string, input: MapInput, parent?: IContainer) { + createFromSavedObject = async (savedObjectId: string, input: MapInput, parent?: IContainer) => { const savedMap = await this._fetchSavedMap(savedObjectId); const layerList = getInitialLayers(savedMap.layerListJSON); const indexPatterns = await this._getIndexPatterns(layerList); @@ -124,39 +123,23 @@ export class MapEmbeddableFactory { } return embeddable; - } + }; - async createFromState( - state: { title?: string; layerList?: unknown[] }, - input: MapInput, - parent: IContainer, - renderTooltipContent: unknown, - eventHandlers: unknown - ) { - const layerList = state && state.layerList ? state.layerList : getInitialLayers(); + create = async (input: MapInput, parent?: IContainer) => { + const layerList = input.layerList ?? getInitialLayers(); const indexPatterns = await this._getIndexPatterns(layerList); return new MapEmbeddable( { layerList, - title: state && state.title ? state.title : '', + title: input.title ?? '', indexPatterns, editable: false, }, input, - parent, - renderTooltipContent, - eventHandlers - ); - } - - async create(input: MapInput) { - window.location.href = chrome.addBasePath(createMapPath('')); - return new ErrorEmbeddable( - 'Maps can only be created with createFromSavedObject or createFromState', - input + parent ); - } + }; } setup.registerEmbeddableFactory(MAP_SAVED_OBJECT_TYPE, new MapEmbeddableFactory()); diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts index 27cd64103eec9..744726e1e3d0f 100644 --- a/x-pack/legacy/plugins/maps/public/index.ts +++ b/x-pack/legacy/plugins/maps/public/index.ts @@ -25,3 +25,6 @@ import { MapsPlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => { return new MapsPlugin(); }; + +export { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; +export { MapEmbeddable, MapInput } from './embeddable'; diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts index 86c819693a35c..bd7ad8d12e3d9 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts @@ -9,7 +9,7 @@ import { MapCenter } from '../actions/map_actions'; export function getHiddenLayerIds(config: unknown): AnyAction; -export function getMapZoom(config: unknown): unknown; +export function getMapZoom(config: unknown): number; export function getMapCenter(config: unknown): MapCenter; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx index 0ffb13cd66028..9b55a053db844 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx @@ -20,7 +20,7 @@ jest.mock('ui/new_platform'); const { npStart } = createUiNewPlatformMock(); npStart.plugins.embeddable.getEmbeddableFactory = jest.fn().mockImplementation(() => ({ - createFromState: () => ({ + create: () => ({ reload: jest.fn(), }), })); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index 888df8447a728..344d916638082 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -8,14 +8,17 @@ import uuid from 'uuid'; import React from 'react'; import { OutPortal, PortalNode } from 'react-reverse-portal'; import minimatch from 'minimatch'; -import { ViewMode } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { IndexPatternMapping, MapEmbeddable, RenderTooltipContentParams, SetQuery } from './types'; +import { IndexPatternMapping, RenderTooltipContentParams, SetQuery } from './types'; import { getLayerList } from './map_config'; -// @ts-ignore Missing type defs as maps moves to Typescript -import { MAP_SAVED_OBJECT_TYPE } from '../../../../maps/common/constants'; +import { MAP_SAVED_OBJECT_TYPE, MapInput, MapEmbeddable } from '../../../../maps/public'; import * as i18n from './translations'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; -import { EmbeddableStart } from '../../../../../../../src/plugins/embeddable/public'; +import { + EmbeddableStart, + isErrorEmbeddable, + EmbeddableOutput, + ViewMode, +} from '../../../../../../../src/plugins/embeddable/public'; import { IndexPatternSavedObject } from '../../hooks/types'; /** @@ -42,13 +45,17 @@ export const createEmbeddable = async ( portalNode: PortalNode, embeddableApi: EmbeddableStart ): Promise => { - const factory = embeddableApi.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); + const factory = embeddableApi.getEmbeddableFactory( + MAP_SAVED_OBJECT_TYPE + ); + + if (!factory) { + throw new Error('Map embeddable factory undefined'); + } - const state = { + const input: MapInput = { layerList: getLayerList(indexPatterns), title: i18n.MAP_TITLE, - }; - const input = { id: uuid.v4(), filters, hidePanelTitles: true, @@ -87,13 +94,15 @@ export const createEmbeddable = async ( return ; }; - // @ts-ignore method added in https://github.com/elastic/kibana/pull/43878 - const embeddableObject = await factory.createFromState( - state, - input, - undefined, - renderTooltipContent - ); + const embeddableObject = await factory.create(input); + + if (!embeddableObject) { + throw new Error('Map embeddable is undefined'); + } + + if (!isErrorEmbeddable(embeddableObject)) { + embeddableObject.setRenderTooltipContent(renderTooltipContent); + } // Wire up to app refresh action setQuery({ diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index 11f6565734782..ea45bf441be8c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -10,11 +10,10 @@ import styled from 'styled-components'; import { start } from '../../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import * as i18n from './translations'; -// @ts-ignore -import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/common/constants'; +import { MAP_SAVED_OBJECT_TYPE, MapInput, MapEmbeddable } from '../../../../../../maps/public'; + import { Location } from '../../../../../common/runtime_types'; -import { MapEmbeddable } from './types'; import { getLayerList } from './map_config'; import { UptimeThemeContext } from '../../../../contexts'; @@ -47,7 +46,7 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp const { colors } = useContext(UptimeThemeContext); const [embeddable, setEmbeddable] = useState(); const embeddableRoot: React.RefObject = useRef(null); - const factory = start.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); + const factory = start.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); const input = { id: uuid.v4(), @@ -57,7 +56,7 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp value: 0, pause: false, }, - viewMode: 'view', + viewMode: ViewMode.VIEW, isLayerTOCOpen: false, hideFilterActions: true, // Zoom Lat/Lon values are set to make sure map is in center in the panel @@ -76,12 +75,14 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp useEffect(() => { async function setupEmbeddable() { - const mapState = { + if (!factory) { + throw new Error('Map embeddable not found.'); + } + const embeddableObject = await factory.create({ + ...input, layerList: getLayerList(upPoints, downPoints, colors), title: i18n.MAP_TITLE, - }; - // @ts-ignore - const embeddableObject = await factory.createFromState(mapState, input, undefined); + }); setEmbeddable(embeddableObject); } diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts deleted file mode 100644 index 03cb33c3459d2..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Query } from 'src/plugins/data/common'; -import { TimeRange } from 'src/plugins/data/public'; -import { - EmbeddableInput, - EmbeddableOutput, - IEmbeddable, -} from '../../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; - -import { Filter } from '../../../../../../../../../src/plugins/data/public'; - -export interface MapEmbeddableInput extends EmbeddableInput { - filters: Filter[]; - query: Query; - refreshConfig: { - isPaused: boolean; - interval: number; - }; - timeRange?: TimeRange; -} - -export interface CustomProps { - setLayerList: Function; -} - -export type MapEmbeddable = IEmbeddable & CustomProps; From a809660cd780a4868167507e747ea2ac1808ab2e Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Mar 2020 09:52:02 -0400 Subject: [PATCH 04/21] Address code review comments and update some usages in SIEM and uptime to the new types --- .../maps/public/actions/map_actions.d.ts | 24 ++++----- .../maps/public/actions/ui_actions.d.ts | 2 +- .../public/angular/get_initial_layers.d.ts | 2 +- .../connected_components/gis_map/index.d.ts | 6 ++- .../plugins/maps/public/embeddable/index.ts | 8 +++ .../maps/public/embeddable/map_embeddable.tsx | 54 +++++++++---------- .../embeddable/map_embeddable_factory.ts | 14 +++-- .../merge_input_with_saved_map.d.ts | 7 ++- x-pack/legacy/plugins/maps/public/index.ts | 2 + .../maps/public/selectors/map_selectors.d.ts | 4 +- .../maps/public/selectors/ui_selectors.d.ts | 2 +- .../components/embeddables/embedded_map.tsx | 3 +- .../embeddables/embedded_map_helpers.tsx | 6 +-- .../public/components/embeddables/types.ts | 19 ------- .../location_map/embeddables/embedded_map.tsx | 10 ++-- .../location_map/embeddables/types.ts | 31 ----------- x-pack/plugins/maps/public/index.ts | 2 + 17 files changed, 85 insertions(+), 111 deletions(-) create mode 100644 x-pack/legacy/plugins/maps/public/embeddable/index.ts delete mode 100644 x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts index 73681c6577fbe..0d13d21f9abdb 100644 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts +++ b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts @@ -28,14 +28,14 @@ export function updateSourceProp( ): void; export interface MapCenter { - lat: string; - lon: string; - zoom: unknown; + lat: number; + lon: number; + zoom: number; } export function setGotoWithCenter(config: MapCenter): AnyAction; -export function replaceLayerList(layerList: unknown): AnyAction; +export function replaceLayerList(layerList: unknown[]): AnyAction; export interface QueryGroup { filters: Filter[]; @@ -48,23 +48,23 @@ export function setQuery(query: QueryGroup): AnyAction; export interface RefreshConfig { isPaused: boolean; - interval: unknown; + interval: number; } export function setRefreshConfig(config: RefreshConfig): AnyAction; export function disableScrollZoom(): AnyAction; -export function disableInteractive(disable: boolean): AnyAction; +export function disableInteractive(): AnyAction; -export function disableTooltipControl(disable: boolean): AnyAction; +export function disableTooltipControl(): AnyAction; -export function hideToolbarOverlay(hide: boolean): AnyAction; +export function hideToolbarOverlay(): AnyAction; -export function hideLayerControl(hide: boolean): AnyAction; +export function hideLayerControl(): AnyAction; -export function hideViewControl(hide: boolean): AnyAction; +export function hideViewControl(): AnyAction; -export function setHiddenLayers(layer: unknown): AnyAction; +export function setHiddenLayers(hiddenLayerIds: string[]): AnyAction; -export function addLayerWithoutDataSync(layer: unknown): AnyAction; +export function addLayerWithoutDataSync(layerDescriptor: unknown): AnyAction; diff --git a/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts index ead09b5d10ca1..233918847de08 100644 --- a/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts +++ b/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts @@ -6,7 +6,7 @@ import { AnyAction } from 'redux'; -export function setOpenTOCDetails(details: unknown): AnyAction; +export function setOpenTOCDetails(layerIds?: string[]): AnyAction; export function setIsLayerTOCOpen(open: boolean): AnyAction; diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts index 6fc24e601cd70..920888404b97d 100644 --- a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts +++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export function getInitialLayers(layers?: unknown): unknown[]; +export function getInitialLayers(layerListJSON?: string, initialLayers?: unknown[]): unknown[]; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts index 8079396162953..56b24cb6d2af0 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts +++ b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts @@ -5,5 +5,9 @@ */ import React from 'react'; +import { Filter } from 'src/plugins/data/public'; -export const GisMap: React.FC<{ addFilters: unknown; renderTooltipContent: unknown }>; +export const GisMap: React.ComponentType<{ + addFilters: ((filters: Filter[]) => void) | null; + renderTooltipContent?: (params: unknown) => React.ComponentType; +}>; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/index.ts b/x-pack/legacy/plugins/maps/public/embeddable/index.ts new file mode 100644 index 0000000000000..a410a6699a01f --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/embeddable/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './map_embeddable'; +export * from './map_embeddable_factory'; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index 25d059613f36b..f29674c8bab06 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -62,37 +62,37 @@ interface MapConfig { indexPatterns: IIndexPattern[]; editable: boolean; title?: string; - layerList: unknown; + layerList: unknown[]; } -export interface MapInput extends EmbeddableInput { +export interface MapEmbeddableInput extends EmbeddableInput { timeRange?: TimeRange; filters: Filter[]; query?: Query; - refresh: unknown; + refresh?: unknown; refreshConfig: RefreshInterval; isLayerTOCOpen: boolean; - openTOCDetails: unknown; - disableTooltipControl: boolean; - disableInteractive: boolean; - hideToolbarOverlay: boolean; - hideLayerControl: boolean; - hideViewControl: boolean; - mapCenter: MapCenter; - hiddenLayers: unknown; - hideFilterActions: boolean; + openTOCDetails?: string[]; + disableTooltipControl?: boolean; + disableInteractive?: boolean; + hideToolbarOverlay?: boolean; + hideLayerControl?: boolean; + hideViewControl?: boolean; + mapCenter?: MapCenter; + hiddenLayers?: string[]; + hideFilterActions?: boolean; } export interface MapOutput extends EmbeddableOutput { indexPatterns: IIndexPattern[]; } -export class MapEmbeddable extends Embeddable { +export class MapEmbeddable extends Embeddable { type = MAP_SAVED_OBJECT_TYPE; - private _renderTooltipContent?: unknown; + private _renderTooltipContent?: (params: unknown) => React.ComponentType; private _eventHandlers?: unknown; - private _layerList: unknown; + private _layerList: unknown[]; private _store: MapStore; private _subscription: Subscription; private _prevTimeRange?: TimeRange; @@ -104,9 +104,9 @@ export class MapEmbeddable extends Embeddable { constructor( config: MapConfig, - initialInput: MapInput, + initialInput: MapEmbeddableInput, parent?: IContainer, - renderTooltipContent?: unknown, + renderTooltipContent?: (params: unknown) => React.ComponentType, eventHandlers?: unknown ) { super( @@ -132,7 +132,7 @@ export class MapEmbeddable extends Embeddable { return getInspectorAdapters(this._store.getState()); } - onContainerStateChanged(containerState: MapInput) { + onContainerStateChanged(containerState: MapEmbeddableInput) { if ( !_.isEqual(containerState.timeRange, this._prevTimeRange) || !_.isEqual(containerState.query, this._prevQuery) || @@ -151,7 +151,7 @@ export class MapEmbeddable extends Embeddable { timeRange, filters, refresh, - }: Pick) { + }: Pick) { this._prevTimeRange = timeRange; this._prevQuery = query; this._prevFilters = filters; @@ -165,7 +165,7 @@ export class MapEmbeddable extends Embeddable { ); } - _dispatchSetRefreshConfig({ refreshConfig }: Pick) { + _dispatchSetRefreshConfig({ refreshConfig }: Pick) { this._prevRefreshConfig = refreshConfig; this._store.dispatch( setRefreshConfig({ @@ -194,22 +194,22 @@ export class MapEmbeddable extends Embeddable { } if (_.has(this.input, 'disableInteractive') && this.input.disableInteractive) { - this._store.dispatch(disableInteractive(this.input.disableInteractive)); + this._store.dispatch(disableInteractive()); } if (_.has(this.input, 'disableTooltipControl') && this.input.disableTooltipControl) { - this._store.dispatch(disableTooltipControl(this.input.disableTooltipControl)); + this._store.dispatch(disableTooltipControl()); } if (_.has(this.input, 'hideToolbarOverlay') && this.input.hideToolbarOverlay) { - this._store.dispatch(hideToolbarOverlay(this.input.hideToolbarOverlay)); + this._store.dispatch(hideToolbarOverlay()); } if (_.has(this.input, 'hideLayerControl') && this.input.hideLayerControl) { - this._store.dispatch(hideLayerControl(this.input.hideLayerControl)); + this._store.dispatch(hideLayerControl()); } if (_.has(this.input, 'hideViewControl') && this.input.hideViewControl) { - this._store.dispatch(hideViewControl(this.input.hideViewControl)); + this._store.dispatch(hideViewControl()); } if (this.input.mapCenter) { @@ -248,7 +248,7 @@ export class MapEmbeddable extends Embeddable { }); } - async setLayerList(layerList: unknown) { + async setLayerList(layerList: unknown[]) { this._layerList = layerList; return await this._store.dispatch(replaceLayerList(this._layerList)); } @@ -288,7 +288,7 @@ export class MapEmbeddable extends Embeddable { const center = getMapCenter(this._store.getState()); const zoom = getMapZoom(this._store.getState()); - const mapCenter = this.input.mapCenter || {}; + const mapCenter = this.input.mapCenter || undefined; if ( !mapCenter || mapCenter.lat !== center.lat || diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts index d0d519d0d7e80..c65552851617f 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -17,7 +17,7 @@ import { IContainer, } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { setup } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; -import { MapEmbeddable, MapInput } from './map_embeddable'; +import { MapEmbeddable, MapEmbeddableInput } from './map_embeddable'; import { getIndexPatternService } from '../kibana_services'; import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants'; @@ -99,7 +99,11 @@ export class MapEmbeddableFactory extends EmbeddableFactory { return await savedObjectLoader.get(savedObjectId); } - async createFromSavedObject(savedObjectId: string, input: MapInput, parent?: IContainer) { + async createFromSavedObject( + savedObjectId: string, + input: MapEmbeddableInput, + parent?: IContainer + ) { const savedMap = await this._fetchSavedMap(savedObjectId); const layerList = getInitialLayers(savedMap.layerListJSON); const indexPatterns = await this._getIndexPatterns(layerList); @@ -131,9 +135,9 @@ export class MapEmbeddableFactory extends EmbeddableFactory { async createFromState( state: { title?: string; layerList?: unknown[] }, - input: MapInput, + input: MapEmbeddableInput, parent: IContainer, - renderTooltipContent: unknown, + renderTooltipContent: (params: unknown) => React.ComponentType, eventHandlers: unknown ) { const layerList = state && state.layerList ? state.layerList : getInitialLayers(); @@ -153,7 +157,7 @@ export class MapEmbeddableFactory extends EmbeddableFactory { ); } - async create(input: MapInput) { + async create(input: MapEmbeddableInput) { window.location.href = chrome.addBasePath(createMapPath('')); return new ErrorEmbeddable( 'Maps can only be created with createFromSavedObject or createFromState', diff --git a/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts b/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts index d7eff790b82a1..4ce4df02f6a39 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts @@ -4,6 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { MapInput } from './map_embeddable'; +import { MapEmbeddableInput } from './map_embeddable'; -export function mergeInputWithSavedMap(input: MapInput, savedmap: unknown): Partial; +export function mergeInputWithSavedMap( + input: MapEmbeddableInput, + savedmap: unknown +): Partial; diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts index 27cd64103eec9..fa33a1c1852d8 100644 --- a/x-pack/legacy/plugins/maps/public/index.ts +++ b/x-pack/legacy/plugins/maps/public/index.ts @@ -25,3 +25,5 @@ import { MapsPlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => { return new MapsPlugin(); }; + +export { MapEmbeddable, MapEmbeddableInput } from './embeddable'; diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts index 86c819693a35c..c22c60853b75b 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts @@ -7,9 +7,9 @@ import { AnyAction } from 'redux'; import { MapCenter } from '../actions/map_actions'; -export function getHiddenLayerIds(config: unknown): AnyAction; +export function getHiddenLayerIds(config: unknown): string[]; -export function getMapZoom(config: unknown): unknown; +export function getMapZoom(config: unknown): number; export function getMapCenter(config: unknown): MapCenter; diff --git a/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.d.ts b/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.d.ts index 7d66e55602238..812e2082241bd 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.d.ts +++ b/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.d.ts @@ -4,6 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export function getOpenTOCDetails(state: unknown): unknown; +export function getOpenTOCDetails(state: unknown): string[]; export function getIsLayerTOCOpen(state: unknown): boolean; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx index d0b1d8ffcb5ae..a3c4a655a4937 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx @@ -22,7 +22,8 @@ import { createEmbeddable, findMatchingIndexPatterns } from './embedded_map_help import { IndexPatternsMissingPrompt } from './index_patterns_missing_prompt'; import { MapToolTip } from './map_tool_tip/map_tool_tip'; import * as i18n from './translations'; -import { MapEmbeddable, SetQuery } from './types'; +import { SetQuery } from './types'; +import { MapEmbeddable } from '../../../../../plugins/maps/public'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { useKibana, useUiSetting$ } from '../../lib/kibana'; import { getSavedObjectFinder } from '../../../../../../../src/plugins/saved_objects/public'; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index 888df8447a728..bf2f95a071d1f 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -9,10 +9,10 @@ import React from 'react'; import { OutPortal, PortalNode } from 'react-reverse-portal'; import minimatch from 'minimatch'; import { ViewMode } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { IndexPatternMapping, MapEmbeddable, RenderTooltipContentParams, SetQuery } from './types'; +import { IndexPatternMapping, RenderTooltipContentParams, SetQuery } from './types'; import { getLayerList } from './map_config'; -// @ts-ignore Missing type defs as maps moves to Typescript -import { MAP_SAVED_OBJECT_TYPE } from '../../../../maps/common/constants'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../plugins/maps/public'; +import { MapEmbeddable } from '../../../../maps/public'; import * as i18n from './translations'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { EmbeddableStart } from '../../../../../../../src/plugins/embeddable/public'; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts index cc253beb08eae..cc1cbc5e0f6a0 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts @@ -4,26 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimeRange } from 'src/plugins/data/public'; -import { - EmbeddableInput, - EmbeddableOutput, - IEmbeddable, -} from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { inputsModel } from '../../store/inputs'; -import { Query, Filter } from '../../../../../../../src/plugins/data/public'; - -export interface MapEmbeddableInput extends EmbeddableInput { - filters: Filter[]; - query: Query; - refreshConfig: { - isPaused: boolean; - interval: number; - }; - timeRange?: TimeRange; -} - -export type MapEmbeddable = IEmbeddable; export interface IndexPatternMapping { title: string; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index 11f6565734782..eba17a0bb21a8 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -8,13 +8,13 @@ import React, { useEffect, useState, useContext, useRef } from 'react'; import uuid from 'uuid'; import styled from 'styled-components'; +import { ViewMode } from 'src/plugins/embeddable/public'; import { start } from '../../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import * as i18n from './translations'; -// @ts-ignore -import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/common/constants'; +import { MapEmbeddable, MapEmbeddableInput } from '../../../../../../maps/public'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../../../plugins/maps/public'; import { Location } from '../../../../../common/runtime_types'; -import { MapEmbeddable } from './types'; import { getLayerList } from './map_config'; import { UptimeThemeContext } from '../../../../contexts'; @@ -49,7 +49,7 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp const embeddableRoot: React.RefObject = useRef(null); const factory = start.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); - const input = { + const input: MapEmbeddableInput = { id: uuid.v4(), filters: [], hidePanelTitles: true, @@ -57,7 +57,7 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp value: 0, pause: false, }, - viewMode: 'view', + viewMode: ViewMode.VIEW, isLayerTOCOpen: false, hideFilterActions: true, // Zoom Lat/Lon values are set to make sure map is in center in the panel diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts deleted file mode 100644 index 03cb33c3459d2..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Query } from 'src/plugins/data/common'; -import { TimeRange } from 'src/plugins/data/public'; -import { - EmbeddableInput, - EmbeddableOutput, - IEmbeddable, -} from '../../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; - -import { Filter } from '../../../../../../../../../src/plugins/data/public'; - -export interface MapEmbeddableInput extends EmbeddableInput { - filters: Filter[]; - query: Query; - refreshConfig: { - isPaused: boolean; - interval: number; - }; - timeRange?: TimeRange; -} - -export interface CustomProps { - setLayerList: Function; -} - -export type MapEmbeddable = IEmbeddable & CustomProps; diff --git a/x-pack/plugins/maps/public/index.ts b/x-pack/plugins/maps/public/index.ts index c465700a4f9c5..e3feb47691877 100644 --- a/x-pack/plugins/maps/public/index.ts +++ b/x-pack/plugins/maps/public/index.ts @@ -10,3 +10,5 @@ import { MapsPlugin, MapsPluginSetup, MapsPluginStart } from './plugin'; export const plugin: PluginInitializer = () => { return new MapsPlugin(); }; + +export { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; From ee9bc9e5421e8a077a0f6c4003bfda2a2b75e6f8 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Mar 2020 10:40:24 -0400 Subject: [PATCH 05/21] More clean up - carry over some of the SIEM types to maps for render tool tip --- .../connected_components/gis_map/index.d.ts | 3 +- .../plugins/maps/public/embeddable/index.ts | 1 + .../maps/public/embeddable/map_embeddable.tsx | 3 +- .../plugins/maps/public/embeddable/types.ts | 34 +++++++++++++++++++ x-pack/legacy/plugins/maps/public/index.ts | 2 +- .../embeddables/embedded_map_helpers.tsx | 4 +-- .../public/components/embeddables/types.ts | 11 +----- .../location_map/embeddables/embedded_map.tsx | 2 +- 8 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 x-pack/legacy/plugins/maps/public/embeddable/types.ts diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts index 56b24cb6d2af0..35cdc5d2e762c 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts +++ b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts @@ -6,8 +6,9 @@ import React from 'react'; import { Filter } from 'src/plugins/data/public'; +import { RenderTooltipContentParams } from '../../embeddable/types'; export const GisMap: React.ComponentType<{ addFilters: ((filters: Filter[]) => void) | null; - renderTooltipContent?: (params: unknown) => React.ComponentType; + renderTooltipContent?: (params: RenderTooltipContentParams) => React.ComponentType; }>; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/index.ts b/x-pack/legacy/plugins/maps/public/embeddable/index.ts index a410a6699a01f..b6ab237214894 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/index.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/index.ts @@ -6,3 +6,4 @@ export * from './map_embeddable'; export * from './map_embeddable_factory'; +export * from './types'; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index f29674c8bab06..8d151ec43098c 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -56,6 +56,7 @@ import { } from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; import { getMapCenter, getMapZoom, getHiddenLayerIds } from '../selectors/map_selectors'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; +import { RenderTooltipContentParams } from './types'; interface MapConfig { editUrl?: string; @@ -90,7 +91,7 @@ export interface MapOutput extends EmbeddableOutput { export class MapEmbeddable extends Embeddable { type = MAP_SAVED_OBJECT_TYPE; - private _renderTooltipContent?: (params: unknown) => React.ComponentType; + private _renderTooltipContent?: (params: RenderTooltipContentParams) => React.ComponentType; private _eventHandlers?: unknown; private _layerList: unknown[]; private _store: MapStore; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/types.ts b/x-pack/legacy/plugins/maps/public/embeddable/types.ts new file mode 100644 index 0000000000000..32fd589340928 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/embeddable/types.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export interface MapFeature { + id: number; + layerId: string; +} + +export interface LoadFeatureProps { + layerId: string; + featureId: number; +} + +export interface FeatureProperty { + _propertyKey: string; + _rawValue: string | string[]; +} + +export interface FeatureGeometry { + coordinates: [number]; + type: string; +} + +export interface RenderTooltipContentParams { + addFilters(filter: object): void; + closeTooltip(): void; + features: MapFeature[]; + isLocked: boolean; + getLayerName(layerId: string): Promise; + loadFeatureProperties({ layerId, featureId }: LoadFeatureProps): Promise; + loadFeatureGeometry({ layerId, featureId }: LoadFeatureProps): FeatureGeometry; +} diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts index fa33a1c1852d8..347d8e28d0d89 100644 --- a/x-pack/legacy/plugins/maps/public/index.ts +++ b/x-pack/legacy/plugins/maps/public/index.ts @@ -26,4 +26,4 @@ export const plugin = (initializerContext: PluginInitializerContext) => { return new MapsPlugin(); }; -export { MapEmbeddable, MapEmbeddableInput } from './embeddable'; +export { MapEmbeddable, MapEmbeddableInput, RenderTooltipContentParams } from './embeddable'; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index bf2f95a071d1f..4b32fd8299ef7 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -9,10 +9,10 @@ import React from 'react'; import { OutPortal, PortalNode } from 'react-reverse-portal'; import minimatch from 'minimatch'; import { ViewMode } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { IndexPatternMapping, RenderTooltipContentParams, SetQuery } from './types'; +import { IndexPatternMapping, SetQuery } from './types'; import { getLayerList } from './map_config'; import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../plugins/maps/public'; -import { MapEmbeddable } from '../../../../maps/public'; +import { MapEmbeddable, RenderTooltipContentParams } from '../../../../maps/public'; import * as i18n from './translations'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { EmbeddableStart } from '../../../../../../../src/plugins/embeddable/public'; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts index cc1cbc5e0f6a0..216fe9105327c 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { RenderTooltipContentParams } from '../../../../maps/public'; import { inputsModel } from '../../store/inputs'; export interface IndexPatternMapping { @@ -54,14 +55,4 @@ export interface FeatureGeometry { type: string; } -export interface RenderTooltipContentParams { - addFilters(filter: object): void; - closeTooltip(): void; - features: MapFeature[]; - isLocked: boolean; - getLayerName(layerId: string): Promise; - loadFeatureProperties({ layerId, featureId }: LoadFeatureProps): Promise; - loadFeatureGeometry({ layerId, featureId }: LoadFeatureProps): FeatureGeometry; -} - export type MapToolTipProps = Partial; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index eba17a0bb21a8..cfed52f4e5d27 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useState, useContext, useRef } from 'react'; import uuid from 'uuid'; import styled from 'styled-components'; -import { ViewMode } from 'src/plugins/embeddable/public'; +import { ViewMode } from '../../../../../../../../../src/plugins/embeddable/public'; import { start } from '../../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import * as i18n from './translations'; import { MapEmbeddable, MapEmbeddableInput } from '../../../../../../maps/public'; From 476afad934f71f6afc8c5118e68c0b685a9fbbe4 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Mar 2020 10:44:22 -0400 Subject: [PATCH 06/21] fixes --- .../maps/public/embeddable/map_embeddable.tsx | 4 ++-- .../components/embeddables/embedded_map.tsx | 9 +++++++-- .../embeddables/embedded_map_helpers.tsx | 13 ++++++++----- .../location_map/embeddables/embedded_map.tsx | 15 +++++++++++---- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index 64642bc3d604d..aaf3754a5981f 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -81,7 +81,7 @@ export interface MapEmbeddableInput extends EmbeddableInput { mapCenter?: MapCenter; hiddenLayers?: string[]; hideFilterActions?: boolean; - layerList: unknown[]; + layerList?: unknown[]; } export interface MapOutput extends EmbeddableOutput { @@ -129,7 +129,7 @@ export class MapEmbeddable extends Embeddable { this._subscription = this.getInput$().subscribe(input => this.onContainerStateChanged(input)); } - setRenderTooltipContent = (renderTooltipContent: unknown) => { + setRenderTooltipContent = (renderTooltipContent: (params: unknown) => React.ComponentType) => { this._renderTooltipContent = renderTooltipContent; }; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx index a3c4a655a4937..f05c26836473c 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx @@ -9,7 +9,10 @@ import React, { useEffect, useState } from 'react'; import { createPortalNode, InPortal } from 'react-reverse-portal'; import styled, { css } from 'styled-components'; -import { EmbeddablePanel } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { + EmbeddablePanel, + ErrorEmbeddable, +} from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { getIndexPatternTitleIdMapping } from '../../hooks/api/helpers'; @@ -84,7 +87,9 @@ export const EmbeddedMapComponent = ({ setQuery, startDate, }: EmbeddedMapProps) => { - const [embeddable, setEmbeddable] = React.useState(null); + const [embeddable, setEmbeddable] = React.useState( + undefined + ); const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState(false); const [isIndexError, setIsIndexError] = useState(false); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index d4c16fc9730bd..af58501ad5b11 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -19,6 +19,7 @@ import { isErrorEmbeddable, EmbeddableOutput, ViewMode, + ErrorEmbeddable, } from '../../../../../../../src/plugins/embeddable/public'; import { IndexPatternSavedObject } from '../../hooks/types'; @@ -45,16 +46,18 @@ export const createEmbeddable = async ( setQuery: SetQuery, portalNode: PortalNode, embeddableApi: EmbeddableStart -): Promise => { - const factory = embeddableApi.getEmbeddableFactory( - MAP_SAVED_OBJECT_TYPE - ); +): Promise => { + const factory = embeddableApi.getEmbeddableFactory< + MapEmbeddableInput, + EmbeddableOutput, + MapEmbeddable + >(MAP_SAVED_OBJECT_TYPE); if (!factory) { throw new Error('Map embeddable factory undefined'); } - const input: MapInput = { + const input: MapEmbeddableInput = { layerList: getLayerList(indexPatterns), title: i18n.MAP_TITLE, id: uuid.v4(), diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index f79acaf06b941..bd0a14451226a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -8,7 +8,12 @@ import React, { useEffect, useState, useContext, useRef } from 'react'; import uuid from 'uuid'; import styled from 'styled-components'; -import { ViewMode } from 'src/plugins/embeddable/public'; +import { + ViewMode, + EmbeddableOutput, + ErrorEmbeddable, + isErrorEmbeddable, +} from 'src/plugins/embeddable/public'; import { start } from '../../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import * as i18n from './translations'; import { MapEmbeddable, MapEmbeddableInput } from '../../../../../../maps/public'; @@ -45,9 +50,11 @@ const EmbeddedPanel = styled.div` export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProps) => { const { colors } = useContext(UptimeThemeContext); - const [embeddable, setEmbeddable] = useState(); + const [embeddable, setEmbeddable] = useState(); const embeddableRoot: React.RefObject = useRef(null); - const factory = start.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); + const factory = start.getEmbeddableFactory( + MAP_SAVED_OBJECT_TYPE + ); const input: MapEmbeddableInput = { id: uuid.v4(), @@ -95,7 +102,7 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp // update map layers based on points useEffect(() => { - if (embeddable) { + if (embeddable && !isErrorEmbeddable(embeddable)) { embeddable.setLayerList(getLayerList(upPoints, downPoints, colors)); } }, [upPoints, downPoints, embeddable, colors]); From fb69ea3db2ee11cc3dae4bf6454c089f00516ba7 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Mar 2020 11:14:28 -0400 Subject: [PATCH 07/21] fixes --- .../np_ready/public/embeddable/visualize_embeddable.ts | 5 ++--- .../public/embeddable/visualize_embeddable_factory.tsx | 1 - .../maps/public/connected_components/gis_map/index.d.ts | 4 ++-- .../plugins/maps/public/embeddable/map_embeddable.tsx | 8 ++++---- x-pack/legacy/plugins/maps/public/embeddable/types.ts | 2 ++ .../components/embeddables/embedded_map_helpers.tsx | 7 +++++-- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts index 0c34a38e7dc3e..551fe12a43559 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts @@ -65,7 +65,6 @@ export interface VisualizeInput extends EmbeddableInput { colors?: { [key: string]: string }; }; visObject: SerializedVis; - visType: string; } export interface VisualizeOutput extends EmbeddableOutput { @@ -80,7 +79,7 @@ function getOutput( input: VisualizeInput, { savedVisualizations, editable }: VisualizeEmbeddableConfiguration ): VisualizeOutput { - const vis = new Vis(input.visType, input.visObject); + const vis = new Vis(input.visObject.type, input.visObject); const indexPattern = vis.data.indexPattern; const indexPatterns = indexPattern ? [indexPattern] : []; const editUrl = input.visObject.id @@ -117,7 +116,7 @@ export class VisualizeEmbeddable extends Embeddable void) | null; - renderTooltipContent?: (params: RenderTooltipContentParams) => React.ComponentType; + renderTooltipContent?: RenderToolTipContent; }>; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index ad0a511064035..207a857f98a18 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -56,7 +56,7 @@ import { } from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; import { getMapCenter, getMapZoom, getHiddenLayerIds } from '../selectors/map_selectors'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; -import { RenderTooltipContentParams } from './types'; +import { RenderToolTipContent } from './types'; interface MapConfig { editUrl?: string; @@ -92,7 +92,7 @@ export interface MapOutput extends EmbeddableOutput { export class MapEmbeddable extends Embeddable { type = MAP_SAVED_OBJECT_TYPE; - private _renderTooltipContent?: (params: RenderTooltipContentParams) => React.ComponentType; + private _renderTooltipContent?: RenderToolTipContent; private _eventHandlers?: unknown; private _layerList: unknown[]; private _store: MapStore; @@ -108,7 +108,7 @@ export class MapEmbeddable extends Embeddable { config: MapConfig, initialInput: MapEmbeddableInput, parent?: IContainer, - renderTooltipContent?: (params: unknown) => React.ComponentType, + renderTooltipContent?: RenderToolTipContent, eventHandlers?: unknown ) { super( @@ -130,7 +130,7 @@ export class MapEmbeddable extends Embeddable { this._subscription = this.getInput$().subscribe(input => this.onContainerStateChanged(input)); } - setRenderTooltipContent = (renderTooltipContent: (params: unknown) => React.ComponentType) => { + setRenderTooltipContent = (renderTooltipContent: RenderToolTipContent) => { this._renderTooltipContent = renderTooltipContent; }; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/types.ts b/x-pack/legacy/plugins/maps/public/embeddable/types.ts index 32fd589340928..da18daa8115d6 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/types.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/types.ts @@ -32,3 +32,5 @@ export interface RenderTooltipContentParams { loadFeatureProperties({ layerId, featureId }: LoadFeatureProps): Promise; loadFeatureGeometry({ layerId, featureId }: LoadFeatureProps): FeatureGeometry; } + +export type RenderToolTipContent = (params: RenderTooltipContentParams) => JSX.Element; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index 6646b7844beb3..c04af5c0ea43d 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -8,11 +8,14 @@ import uuid from 'uuid'; import React from 'react'; import { OutPortal, PortalNode } from 'react-reverse-portal'; import minimatch from 'minimatch'; -import { ViewMode } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { IndexPatternMapping, SetQuery } from './types'; import { getLayerList } from './map_config'; import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../plugins/maps/public'; -import { MapEmbeddable, RenderTooltipContentParams } from '../../../../maps/public'; +import { + MapEmbeddable, + RenderTooltipContentParams, + MapEmbeddableInput, +} from '../../../../maps/public'; import * as i18n from './translations'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { From d08da59a6c37fc20391c3159e74c20933d967ccb Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Mar 2020 13:50:40 -0400 Subject: [PATCH 08/21] Address more review comments --- .../expression_types/embeddable_types.ts | 3 +- .../connected_components/gis_map/index.d.ts | 4 +-- .../plugins/maps/public/embeddable/index.ts | 1 - .../maps/public/embeddable/map_embeddable.tsx | 19 ++++++----- .../embeddable/map_embeddable_factory.ts | 9 +++-- .../plugins/maps/public/embeddable/types.ts | 34 ------------------- x-pack/legacy/plugins/maps/public/index.ts | 3 +- .../layers/tooltips/tooltip_property.ts | 29 +++++++++++++++- .../maps/public/selectors/map_selectors.d.ts | 6 ++-- .../embeddables/map_tool_tip/map_tool_tip.tsx | 2 +- .../reducers/non_serializable_instances.d.ts | 17 +++++++++- 11 files changed, 70 insertions(+), 57 deletions(-) delete mode 100644 x-pack/legacy/plugins/maps/public/embeddable/types.ts diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts index 538aa9f74e2a6..6bc4b5ec1c840 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore -import { MAP_SAVED_OBJECT_TYPE } from '../../../maps/common/constants'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../plugins/maps/public'; import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/visualizations/public'; import { LENS_EMBEDDABLE_TYPE } from '../../../../../plugins/lens/common/constants'; import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/constants'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts index 35cdc5d2e762c..00a9400109dc1 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts +++ b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts @@ -6,9 +6,9 @@ import React from 'react'; import { Filter } from 'src/plugins/data/public'; -import { RenderTooltipContentParams } from '../../embeddable/types'; +import { RenderToolTipContent } from '../../layers/tooltips/tooltip_property'; export const GisMap: React.ComponentType<{ addFilters: ((filters: Filter[]) => void) | null; - renderTooltipContent?: (params: RenderTooltipContentParams) => React.ComponentType; + renderTooltipContent?: RenderToolTipContent; }>; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/index.ts b/x-pack/legacy/plugins/maps/public/embeddable/index.ts index b6ab237214894..a410a6699a01f 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/index.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/index.ts @@ -6,4 +6,3 @@ export * from './map_embeddable'; export * from './map_embeddable_factory'; -export * from './types'; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index 8d151ec43098c..69f55815d16a0 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -52,13 +52,14 @@ import { getIsLayerTOCOpen, getOpenTOCDetails } from '../selectors/ui_selectors' import { getInspectorAdapters, setEventHandlers, + EventHandlers, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; import { getMapCenter, getMapZoom, getHiddenLayerIds } from '../selectors/map_selectors'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; -import { RenderTooltipContentParams } from './types'; +import { RenderToolTipContent } from '../layers/tooltips/tooltip_property'; -interface MapConfig { +interface MapEmbeddableConfig { editUrl?: string; indexPatterns: IIndexPattern[]; editable: boolean; @@ -84,15 +85,15 @@ export interface MapEmbeddableInput extends EmbeddableInput { hideFilterActions?: boolean; } -export interface MapOutput extends EmbeddableOutput { +export interface MapEmbeddableOutput extends EmbeddableOutput { indexPatterns: IIndexPattern[]; } -export class MapEmbeddable extends Embeddable { +export class MapEmbeddable extends Embeddable { type = MAP_SAVED_OBJECT_TYPE; - private _renderTooltipContent?: (params: RenderTooltipContentParams) => React.ComponentType; - private _eventHandlers?: unknown; + private _renderTooltipContent?: RenderToolTipContent; + private _eventHandlers?: EventHandlers; private _layerList: unknown[]; private _store: MapStore; private _subscription: Subscription; @@ -104,11 +105,11 @@ export class MapEmbeddable extends Embeddable { private _unsubscribeFromStore?: Unsubscribe; constructor( - config: MapConfig, + config: MapEmbeddableConfig, initialInput: MapEmbeddableInput, parent?: IContainer, - renderTooltipContent?: (params: unknown) => React.ComponentType, - eventHandlers?: unknown + renderTooltipContent?: RenderToolTipContent, + eventHandlers?: EventHandlers ) { super( initialInput, diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts index c65552851617f..ddb937dd98926 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -29,6 +29,11 @@ import { getInitialLayers } from '../angular/get_initial_layers'; import { mergeInputWithSavedMap } from './merge_input_with_saved_map'; import '../angular/services/gis_map_saved_object_loader'; import { bindSetupCoreAndPlugins, bindStartCoreAndPlugins } from '../plugin'; +import { RenderToolTipContent } from '../layers/tooltips/tooltip_property'; +import { + EventHandlers, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; export class MapEmbeddableFactory extends EmbeddableFactory { type = MAP_SAVED_OBJECT_TYPE; @@ -137,8 +142,8 @@ export class MapEmbeddableFactory extends EmbeddableFactory { state: { title?: string; layerList?: unknown[] }, input: MapEmbeddableInput, parent: IContainer, - renderTooltipContent: (params: unknown) => React.ComponentType, - eventHandlers: unknown + renderTooltipContent: RenderToolTipContent, + eventHandlers: EventHandlers ) { const layerList = state && state.layerList ? state.layerList : getInitialLayers(); const indexPatterns = await this._getIndexPatterns(layerList); diff --git a/x-pack/legacy/plugins/maps/public/embeddable/types.ts b/x-pack/legacy/plugins/maps/public/embeddable/types.ts deleted file mode 100644 index 32fd589340928..0000000000000 --- a/x-pack/legacy/plugins/maps/public/embeddable/types.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -export interface MapFeature { - id: number; - layerId: string; -} - -export interface LoadFeatureProps { - layerId: string; - featureId: number; -} - -export interface FeatureProperty { - _propertyKey: string; - _rawValue: string | string[]; -} - -export interface FeatureGeometry { - coordinates: [number]; - type: string; -} - -export interface RenderTooltipContentParams { - addFilters(filter: object): void; - closeTooltip(): void; - features: MapFeature[]; - isLocked: boolean; - getLayerName(layerId: string): Promise; - loadFeatureProperties({ layerId, featureId }: LoadFeatureProps): Promise; - loadFeatureGeometry({ layerId, featureId }: LoadFeatureProps): FeatureGeometry; -} diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts index 347d8e28d0d89..2d13f005f1a70 100644 --- a/x-pack/legacy/plugins/maps/public/index.ts +++ b/x-pack/legacy/plugins/maps/public/index.ts @@ -26,4 +26,5 @@ export const plugin = (initializerContext: PluginInitializerContext) => { return new MapsPlugin(); }; -export { MapEmbeddable, MapEmbeddableInput, RenderTooltipContentParams } from './embeddable'; +export { RenderTooltipContentParams, ITooltipProperty } from './layers/tooltips/tooltip_property'; +export { MapEmbeddable, MapEmbeddableInput } from './embeddable'; diff --git a/x-pack/legacy/plugins/maps/public/layers/tooltips/tooltip_property.ts b/x-pack/legacy/plugins/maps/public/layers/tooltips/tooltip_property.ts index 3428cb9589267..c77af11d0ae24 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tooltips/tooltip_property.ts +++ b/x-pack/legacy/plugins/maps/public/layers/tooltips/tooltip_property.ts @@ -16,10 +16,37 @@ export interface ITooltipProperty { getESFilters(): Promise; } +export interface MapFeature { + id: number; + layerId: string; +} + +export interface LoadFeatureProps { + layerId: string; + featureId: number; +} + +export interface FeatureGeometry { + coordinates: [number]; + type: string; +} + +export interface RenderTooltipContentParams { + addFilters(filter: object): void; + closeTooltip(): void; + features: MapFeature[]; + isLocked: boolean; + getLayerName(layerId: string): Promise; + loadFeatureProperties({ layerId, featureId }: LoadFeatureProps): Promise; + loadFeatureGeometry({ layerId, featureId }: LoadFeatureProps): FeatureGeometry; +} + +export type RenderToolTipContent = (params: RenderTooltipContentParams) => JSX.Element; + export class TooltipProperty implements ITooltipProperty { private readonly _propertyKey: string; - private readonly _propertyName: string; private readonly _rawValue: string | undefined; + private readonly _propertyName: string; constructor(propertyKey: string, propertyName: string, rawValue: string | undefined) { this._propertyKey = propertyKey; diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts index c22c60853b75b..237a04027e21b 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts @@ -7,10 +7,10 @@ import { AnyAction } from 'redux'; import { MapCenter } from '../actions/map_actions'; -export function getHiddenLayerIds(config: unknown): string[]; +export function getHiddenLayerIds(state: unknown): string[]; -export function getMapZoom(config: unknown): number; +export function getMapZoom(state: unknown): number; -export function getMapCenter(config: unknown): MapCenter; +export function getMapCenter(state: unknown): MapCenter; export function getQueryableUniqueIndexPatternIds(state: unknown): string[]; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx index 15c423a3b3dc1..fc55e3437dc21 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx @@ -64,7 +64,7 @@ export const MapToolTipComponent = ({ getLayerName(layerId), ]); - setFeatureProps(featureProperties); + setFeatureProps((featureProperties as unknown) as FeatureProperty[]); setFeatureGeometry(featureGeo); setLayerName(layerNameString); } catch (e) { diff --git a/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts b/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts index 042448a3ce650..6d216eb60c45d 100644 --- a/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts +++ b/x-pack/plugins/maps/public/reducers/non_serializable_instances.d.ts @@ -7,6 +7,21 @@ import { Adapters } from 'src/plugins/inspector/public'; import { AnyAction } from 'redux'; -export function setEventHandlers(handlers: unknown): AnyAction; +interface EventHandlers { + /** + * Take action on data load. + */ + onDataLoad: (layerId: string, dataId: string) => void; + /** + * Take action on data load end. + */ + onDataLoadEnd: (layerId: string, dataId: string, resultMeta: object) => void; + /** + * Take action on data load error. + */ + onDataLoadError: (layerId: string, dataId: string, errorMessage: string) => void; +} + +export function setEventHandlers(eventHandlers?: EventHandlers): AnyAction; export function getInspectorAdapters(args: unknown): Adapters | undefined; From f874b9a34f9545ae67526a011acdcb54c506d31a Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Mar 2020 14:39:47 -0400 Subject: [PATCH 09/21] fixes --- .../public/list_container/list_container_factory.ts | 4 ++-- .../searchable_list_container_factory.ts | 4 ++-- .../public/todo/todo_embeddable_factory.tsx | 4 ++-- .../np_ready/embeddable/search_embeddable_factory.ts | 4 ++-- .../embeddable/embeddable_factory.ts | 10 +++++----- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/embeddable_examples/public/list_container/list_container_factory.ts b/examples/embeddable_examples/public/list_container/list_container_factory.ts index 510946e94c6a5..1fde254110c62 100644 --- a/examples/embeddable_examples/public/list_container/list_container_factory.ts +++ b/examples/embeddable_examples/public/list_container/list_container_factory.ts @@ -39,10 +39,10 @@ export class ListContainerFactory implements EmbeddableFactoryDefinition { return true; } - public async create(initialInput: ContainerInput) { + public create = async (initialInput: ContainerInput) => { const { getEmbeddableFactory } = await this.getStartServices(); return new ListContainer(initialInput, getEmbeddableFactory); - } + }; public getDisplayName() { return i18n.translate('embeddableExamples.searchableListContainer.displayName', { diff --git a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts index 74a568b3ebbf8..382bb65e769ef 100644 --- a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts +++ b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts @@ -42,10 +42,10 @@ export class SearchableListContainerFactory implements EmbeddableFactoryDefiniti return true; } - public async create(initialInput: SearchableContainerInput) { + public create = async (initialInput: SearchableContainerInput) => { const { getEmbeddableFactory } = await this.getStartServices(); return new SearchableListContainer(initialInput, getEmbeddableFactory); - } + }; public getDisplayName() { return i18n.translate('embeddableExamples.searchableListContainer.displayName', { diff --git a/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx index 90386ba9ec2fe..bc577ca36d793 100644 --- a/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx +++ b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx @@ -67,7 +67,7 @@ export class TodoEmbeddableFactory * used to collect specific embeddable input that the container will not provide, like * in this case, the task string. */ - public async getExplicitInput() { + public getExplicitInput = async () => { const { openModal } = await this.getStartServices(); return new Promise<{ task: string }>(resolve => { const onSave = (task: string) => resolve({ task }); @@ -82,7 +82,7 @@ export class TodoEmbeddableFactory ) ); }); - } + }; public getDisplayName() { return i18n.translate('embeddableExamples.todo.displayName', { diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts index 6f63ecf9ba3fd..ad61984a52536 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts @@ -62,9 +62,9 @@ export class SearchEmbeddableFactory return false; } - public async isEditable() { + public isEditable = async () => { return (await this.getStartServices()).isEditable(); - } + }; public getDisplayName() { return i18n.translate('kbn.embeddable.search.displayName', { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index 215548658ea47..2a85e6b794e18 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -48,10 +48,10 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { constructor(private getStartServices: () => Promise) {} - public async isEditable() { + public isEditable = async () => { const { capabilities } = await this.getStartServices(); return capabilities.visualize.save as boolean; - } + }; canCreateNew() { return false; @@ -63,11 +63,11 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { }); } - async createFromSavedObject( + createFromSavedObject = async ( savedObjectId: string, input: Partial & { id: string }, parent?: IContainer - ) { + ) => { const { savedObjectsClient, coreHttp, @@ -108,7 +108,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { input, parent ); - } + }; async create(input: EmbeddableInput) { return new ErrorEmbeddable('Lens can only be created from a saved object', input); From c5f2fcddec6d74c8c6e151d64e5214b7ba894675 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Mar 2020 16:38:57 -0400 Subject: [PATCH 10/21] fixes --- .../maps/public/embeddable/map_embeddable_factory.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts index c1df902d45250..291484d2c5fc2 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -13,7 +13,6 @@ import { SavedObjectLoader } from 'src/plugins/saved_objects/public'; import { IIndexPattern } from 'src/plugins/data/public'; import { EmbeddableFactoryDefinition, - ErrorEmbeddable, IContainer, } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { setup } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; @@ -29,11 +28,6 @@ import { getInitialLayers } from '../angular/get_initial_layers'; import { mergeInputWithSavedMap } from './merge_input_with_saved_map'; import '../angular/services/gis_map_saved_object_loader'; import { bindSetupCoreAndPlugins, bindStartCoreAndPlugins } from '../plugin'; -import { RenderToolTipContent } from '../layers/tooltips/tooltip_property'; -import { - EventHandlers, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { type = MAP_SAVED_OBJECT_TYPE; From bef63572dfdffbff5cf4018a430cfb8947928678 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Mar 2020 18:15:25 -0400 Subject: [PATCH 11/21] fix jest test --- src/plugins/embeddable/public/plugin.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/embeddable/public/plugin.test.ts b/src/plugins/embeddable/public/plugin.test.ts index f61998dfb2181..6d1b20e8083cf 100644 --- a/src/plugins/embeddable/public/plugin.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -19,7 +19,7 @@ import { coreMock } from '../../../core/public/mocks'; import { testPlugin } from './tests/test_plugin'; import { EmbeddableFactoryProvider } from './types'; -import { defaultEmbeddableFactoryProvider } from './lib'; +import { defaultEmbeddableFactoryProvider, EmbeddableFactory } from './lib'; test('cannot register embeddable factory with the same ID', async () => { const coreSetup = coreMock.createSetup(); @@ -56,5 +56,5 @@ test('can set custom embeddable factory provider', async () => { const start = doStart(); const factory = start.getEmbeddableFactory('test'); - expect(() => factory!.getDisplayName()).toEqual('Intercepted!'); + expect(factory!.getDisplayName()).toEqual('Intercepted!'); }); From ffa87cedd4605dadddc91cfb62ed418bdd6188c0 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 26 Mar 2020 20:55:04 -0400 Subject: [PATCH 12/21] Fix visualize embeddable --- .../public/visualize/np_ready/legacy_app.js | 16 ++-- .../create_vis_embeddable_from_object.ts | 69 ++++++++++++++++ .../public/embeddable/visualize_embeddable.ts | 47 ++++------- .../visualize_embeddable_factory.tsx | 82 ++++--------------- src/plugins/embeddable/public/plugin.test.ts | 2 +- .../functions/common/saved_visualization.ts | 2 +- .../input_type_to_expression/visualization.ts | 5 +- .../public/custom_time_range_action.test.ts | 20 ----- .../time_range_embeddable_factory.ts | 31 +------ 9 files changed, 114 insertions(+), 160 deletions(-) create mode 100644 src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/create_vis_embeddable_from_object.ts diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js index d60c46608459f..84ae0731d0f65 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js @@ -41,6 +41,7 @@ import { getEditBreadcrumbs, } from './breadcrumbs'; import { createSavedSearchesLoader } from '../../../../../../plugins/discover/public'; +import { createVisEmbeddableFromObject } from '../../../../visualizations/public/np_ready/public/embeddable/create_vis_embeddable_from_object'; const getResolvedResults = deps => { const { core, data, visualizations } = deps; @@ -51,21 +52,16 @@ const getResolvedResults = deps => { results.savedVis = savedVis; return visualizations .convertToSerializedVis(savedVis) - .then(serializedVis => ({ - vis: visualizations.createVis(serializedVis.type, serializedVis), - serializedVis, - })) - .then(({ vis, serializedVis }) => { + .then(serializedVis => visualizations.createVis(serializedVis.type, serializedVis)) + .then(vis => { if (vis.type.setup) { return vis.type.setup(vis).catch(() => vis); } - return { vis, serializedVis }; + return vis; }) - .then(({ vis, serializedVis }) => { + .then(vis => { results.vis = vis; - return deps.embeddable.getEmbeddableFactory('visualization').create({ - visType: savedVis.type, - visObject: serializedVis, + return createVisEmbeddableFromObject({ timeRange: data.query.timefilter.timefilter.getTime(), filters: data.query.filterManager.getFilters(), }); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/create_vis_embeddable_from_object.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/create_vis_embeddable_from_object.ts new file mode 100644 index 0000000000000..b9ffd9cdb3ba8 --- /dev/null +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/create_vis_embeddable_from_object.ts @@ -0,0 +1,69 @@ +/* + * 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 { Vis } from '../types'; +import { VisualizeInput, VisualizeEmbeddable } from './visualize_embeddable'; +import { IContainer, ErrorEmbeddable } from '../../../../../../../plugins/embeddable/public'; +import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; +import { + getSavedVisualizationsLoader, + getUISettings, + getHttp, + getTimeFilter, + getCapabilities, +} from '../services'; + +export const createVisEmbeddableFromObject = async ( + vis: Vis, + input: Partial & { id: string }, + parent?: IContainer +): Promise => { + const savedVisualizations = getSavedVisualizationsLoader(); + + try { + const visId = vis.id as string; + + const editUrl = visId + ? getHttp().basePath.prepend(`/app/kibana${savedVisualizations.urlFor(visId)}`) + : ''; + const isLabsEnabled = getUISettings().get('visualize:enableLabs'); + + if (!isLabsEnabled && vis.type.stage === 'experimental') { + return new DisabledLabEmbeddable(vis.title, input); + } + + const indexPattern = vis.data.indexPattern; + const indexPatterns = indexPattern ? [indexPattern] : []; + const editable = getCapabilities().visualize.save as boolean; + return new VisualizeEmbeddable( + getTimeFilter(), + { + vis, + indexPatterns, + editUrl, + editable, + }, + input, + parent + ); + } catch (e) { + console.error(e); // eslint-disable-line no-console + return new ErrorEmbeddable(e, input, parent); + } +}; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts index 72f50b2aa145e..383d03956a1c4 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts @@ -20,8 +20,6 @@ import _, { get } from 'lodash'; import { Subscription } from 'rxjs'; import * as Rx from 'rxjs'; -import { SavedObjectLoader } from '../../../../../../../plugins/saved_objects/public'; -import { getHttp } from '../services'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; import { IIndexPattern, @@ -47,24 +45,23 @@ import { buildPipeline } from '../legacy/build_pipeline'; import { Vis } from '../vis'; import { getExpressions, getUiActions } from '../services'; import { VIS_EVENT_TO_TRIGGER } from './events'; -import { SerializedVis } from '../types'; const getKeys = (o: T): Array => Object.keys(o) as Array; export interface VisualizeEmbeddableConfiguration { + vis: Vis; + indexPatterns?: IIndexPattern[]; + editUrl: string; editable: boolean; - savedVisualizations: SavedObjectLoader; } export interface VisualizeInput extends EmbeddableInput { timeRange?: TimeRange; query?: Query; filters?: Filter[]; - // This should probably be called "vis overrides". "vis" is a bad name. vis?: { colors?: { [key: string]: string }; }; - visObject: SerializedVis; } export interface VisualizeOutput extends EmbeddableOutput { @@ -75,25 +72,6 @@ export interface VisualizeOutput extends EmbeddableOutput { type ExpressionLoader = InstanceType; -function getOutput( - input: VisualizeInput, - { savedVisualizations, editable }: VisualizeEmbeddableConfiguration -): VisualizeOutput { - const vis = new Vis(input.visObject.type, input.visObject); - const indexPattern = vis.data.indexPattern; - const indexPatterns = indexPattern ? [indexPattern] : []; - const editUrl = input.visObject.id - ? getHttp().basePath.prepend(`/app/kibana${savedVisualizations.urlFor(input.visObject.id)}`) - : ''; - return { - defaultTitle: vis.title, - editUrl, - editable, - indexPatterns, - visTypeName: vis.type.name, - }; -} - export class VisualizeEmbeddable extends Embeddable { private handler?: ExpressionLoader; private timefilter: TimefilterContract; @@ -112,13 +90,23 @@ export class VisualizeEmbeddable extends Embeddable => { - const savedVisualizations = getSavedVisualizationsLoader(); - - try { - const type = getTypes().get(input.visObject.type); - - const isLabsEnabled = getUISettings().get('visualize:enableLabs'); - - if (!isLabsEnabled && type.stage === 'experimental') { - return new DisabledLabEmbeddable(input.visObject.title, input); - } - - const editable = await this.isEditable(); - return new VisualizeEmbeddable( - getTimeFilter(), - { - editable, - savedVisualizations, - }, - input, - parent - ); - } catch (e) { - console.error(e); // eslint-disable-line no-console - return new ErrorEmbeddable(e, input, parent); - } - }; - - public createFromSavedObject = async ( + public async createFromSavedObject( savedObjectId: string, input: Partial & { id: string }, - parent?: Container - ): Promise => { + parent?: IContainer + ): Promise { const savedVisualizations = getSavedVisualizationsLoader(); try { const savedObject = await savedVisualizations.get(savedObjectId); - const embeddable = await this.create( - { - ...input, - visObject: await convertToSerializedVis(savedObject), - }, - parent - ); - if (embeddable === undefined) { - return new ErrorEmbeddable( - i18n.translate('visualizations.errorCreatingVisualizeEmbeddable', { - defaultMessage: 'An error was encountered attempting to create a visualization.', - }), - input - ); - } else { - return embeddable; - } + const vis = new Vis(savedObject.visState.type, await convertToSerializedVis(savedObject)); + return createVisEmbeddableFromObject(vis, input, parent); } catch (e) { console.error(e); // eslint-disable-line no-console return new ErrorEmbeddable(e, input, parent); } - }; + } - public create = async (input: VisualizeInput, parent?: IContainer) => { - if (input === undefined) { - // 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. - showNewVisModal({ - editorParams: ['addToDashboard'], - }); - return undefined; - } else { - return this.createFromInput(input, parent); - } - }; + public async create() { + // 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. + showNewVisModal({ + editorParams: ['addToDashboard'], + }); + return undefined; + } } diff --git a/src/plugins/embeddable/public/plugin.test.ts b/src/plugins/embeddable/public/plugin.test.ts index 6d1b20e8083cf..5fcae2cc22380 100644 --- a/src/plugins/embeddable/public/plugin.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -19,7 +19,7 @@ import { coreMock } from '../../../core/public/mocks'; import { testPlugin } from './tests/test_plugin'; import { EmbeddableFactoryProvider } from './types'; -import { defaultEmbeddableFactoryProvider, EmbeddableFactory } from './lib'; +import { defaultEmbeddableFactoryProvider } from './lib'; test('cannot register embeddable factory with the same ID', async () => { const coreSetup = coreMock.createSetup(); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts index 891fb6d3c1a19..9777eaebb36ed 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts @@ -22,7 +22,7 @@ interface Arguments { hideLegend: boolean | null; } -type Output = EmbeddableExpression>; +type Output = EmbeddableExpression; const defaultTimeRange = { from: 'now-15m', diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts index bea759efcef51..be0dd6a79292f 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts @@ -6,10 +6,7 @@ import { VisualizeInput } from 'src/legacy/core_plugins/visualizations/public'; -export function toExpression( - // Expressions work "by reference" only. - input: Omit & { id: string } -): string { +export function toExpression(input: VisualizeInput): string { const expressionParts = [] as string[]; expressionParts.push('savedVisualization'); diff --git a/x-pack/plugins/advanced_ui_actions/public/custom_time_range_action.test.ts b/x-pack/plugins/advanced_ui_actions/public/custom_time_range_action.test.ts index 052bb838c0a37..10860fe471a3b 100644 --- a/x-pack/plugins/advanced_ui_actions/public/custom_time_range_action.test.ts +++ b/x-pack/plugins/advanced_ui_actions/public/custom_time_range_action.test.ts @@ -10,9 +10,7 @@ import { skip } from 'rxjs/operators'; import * as Rx from 'rxjs'; import { mount } from 'enzyme'; -import { EmbeddableFactory } from '../../../../src/plugins/embeddable/public'; import { TimeRangeEmbeddable, TimeRangeContainer, TIME_RANGE_EMBEDDABLE } from './test_helpers'; -import { TimeRangeEmbeddableFactory } from './test_helpers/time_range_embeddable_factory'; import { CustomTimeRangeAction } from './custom_time_range_action'; /* eslint-disable */ import { @@ -21,7 +19,6 @@ import { /* eslint-enable */ import { - HelloWorldEmbeddableFactory, HelloWorldEmbeddable, HELLO_WORLD_EMBEDDABLE, } from '../../../../examples/embeddable_examples/public'; @@ -38,9 +35,6 @@ const createOpenModalMock = () => { }; test('Custom time range action prevents embeddable from using container time', async done => { - const embeddableFactories = new Map(); - embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory()); - const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -105,9 +99,6 @@ test('Custom time range action prevents embeddable from using container time', a }); test('Removing custom time range action resets embeddable back to container time', async done => { - const embeddableFactories = new Map(); - embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory()); - const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -182,9 +173,6 @@ test('Removing custom time range action resets embeddable back to container time }); test('Cancelling custom time range action leaves state alone', async done => { - const embeddableFactories = new Map(); - embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory()); - const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -244,8 +232,6 @@ test('Cancelling custom time range action leaves state alone', async done => { }); test(`badge is compatible with embeddable that inherits from parent`, async () => { - const embeddableFactories = new Map(); - embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory()); const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -279,8 +265,6 @@ test(`badge is compatible with embeddable that inherits from parent`, async () = // TODO: uncomment when https://github.com/elastic/kibana/issues/43271 is fixed. // test('Embeddable that does not use time range in a container that has time range is incompatible', async () => { -// const embeddableFactories = new Map(); -// embeddableFactories.set(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory()); // const container = new TimeRangeContainer( // { // timeRange: { from: 'now-15m', to: 'now' }, @@ -315,10 +299,6 @@ test(`badge is compatible with embeddable that inherits from parent`, async () = // }); test('Attempting to execute on incompatible embeddable throws an error', async () => { - const embeddableFactories = new Map(); - embeddableFactories.set(HELLO_WORLD_EMBEDDABLE, { - ...new HelloWorldEmbeddableFactory(), - } as EmbeddableFactory); const container = new HelloWorldContainer( { panels: { diff --git a/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts b/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts index 9ce1c6e7ce0d9..e8d9451f9f2a6 100644 --- a/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts +++ b/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts @@ -7,8 +7,7 @@ import { EmbeddableInput, IContainer, - EmbeddableFactory, - ErrorEmbeddable, + EmbeddableFactoryDefinition, } from '../../../../../src/plugins/embeddable/public'; import { TimeRange } from '../../../../../src/plugins/data/public'; import { TIME_RANGE_EMBEDDABLE, TimeRangeEmbeddable } from './time_range_embeddable'; @@ -17,38 +16,14 @@ interface EmbeddableTimeRangeInput extends EmbeddableInput { timeRange: TimeRange; } -export class TimeRangeEmbeddableFactory implements EmbeddableFactory { +export class TimeRangeEmbeddableFactory + implements EmbeddableFactoryDefinition { public readonly type = TIME_RANGE_EMBEDDABLE; - public readonly isContainerType = false; public async isEditable() { return true; } - public getDefaultInput() { - return {}; - } - - public getExplicitInput() { - return Promise.resolve({}); - } - - public canCreateNew() { - return true; - } - - public createFromSavedObject( - savedObjectId: string, - input: Partial, - parent?: IContainer - ) { - return Promise.resolve( - new ErrorEmbeddable(`Creation from saved object not supported by type ${this.type}`, { - id: '', - }) - ); - } - public async create(initialInput: EmbeddableTimeRangeInput, parent?: IContainer) { return new TimeRangeEmbeddable(initialInput, parent); } From 51c531e8358045903a72bd94349d690a9d576787 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Fri, 27 Mar 2020 10:20:49 -0400 Subject: [PATCH 13/21] fixes after master merge --- .../kibana/public/visualize/kibana_services.ts | 1 + .../kibana/public/visualize/np_ready/legacy_app.js | 3 +-- src/legacy/core_plugins/kibana/public/visualize/plugin.ts | 1 + .../embeddable/create_vis_embeddable_from_object.ts | 2 +- src/plugins/visualizations/public/embeddable/index.ts | 1 + .../public/embeddable/visualize_embeddable_factory.tsx | 4 ++-- src/plugins/visualizations/public/mocks.ts | 3 +++ src/plugins/visualizations/public/plugin.ts | 8 +++++++- 8 files changed, 17 insertions(+), 6 deletions(-) rename src/plugins/visualizations/public/{vis_types => }/embeddable/create_vis_embeddable_from_object.ts (95%) diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts index f29f07ba4b20b..39ab265bf34ea 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts @@ -62,6 +62,7 @@ export interface VisualizeKibanaServices { I18nContext: I18nStart['Context']; setActiveUrl: (newUrl: string) => void; DefaultVisualizationEditor: typeof DefaultEditorController; + createVisEmbeddableFromObject: VisualizationsStart['__LEGACY']['createVisEmbeddableFromObject']; } let services: VisualizeKibanaServices | null = null; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js index 84ae0731d0f65..e72eb8972ba1b 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js @@ -41,10 +41,9 @@ import { getEditBreadcrumbs, } from './breadcrumbs'; import { createSavedSearchesLoader } from '../../../../../../plugins/discover/public'; -import { createVisEmbeddableFromObject } from '../../../../visualizations/public/np_ready/public/embeddable/create_vis_embeddable_from_object'; const getResolvedResults = deps => { - const { core, data, visualizations } = deps; + const { core, data, visualizations, createVisEmbeddableFromObject } = deps; const results = {}; diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts index 59b814c98dd08..367318e0c4d55 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts @@ -157,6 +157,7 @@ export class VisualizePlugin implements Plugin { I18nContext: coreStart.i18n.Context, setActiveUrl, DefaultVisualizationEditor: DefaultEditorController, + createVisEmbeddableFromObject: visualizations.__LEGACY.createVisEmbeddableFromObject, }; setServices(deps); diff --git a/src/plugins/visualizations/public/vis_types/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts similarity index 95% rename from src/plugins/visualizations/public/vis_types/embeddable/create_vis_embeddable_from_object.ts rename to src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts index b9ffd9cdb3ba8..bf2d174f594b2 100644 --- a/src/plugins/visualizations/public/vis_types/embeddable/create_vis_embeddable_from_object.ts +++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts @@ -19,7 +19,7 @@ import { Vis } from '../types'; import { VisualizeInput, VisualizeEmbeddable } from './visualize_embeddable'; -import { IContainer, ErrorEmbeddable } from '../../../../../../../plugins/embeddable/public'; +import { IContainer, ErrorEmbeddable } from '../../../../plugins/embeddable/public'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { getSavedVisualizationsLoader, diff --git a/src/plugins/visualizations/public/embeddable/index.ts b/src/plugins/visualizations/public/embeddable/index.ts index 78f9827ffde3e..3753c4dbbb9ed 100644 --- a/src/plugins/visualizations/public/embeddable/index.ts +++ b/src/plugins/visualizations/public/embeddable/index.ts @@ -21,3 +21,4 @@ export { VisualizeEmbeddable, VisualizeInput } from './visualize_embeddable'; export { VisualizeEmbeddableFactory } from './visualize_embeddable_factory'; export { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; export { VIS_EVENT_TO_TRIGGER } from './events'; +export { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object'; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index e2b6a481ab419..4b7d01ae3b246 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -25,10 +25,11 @@ import { EmbeddableOutput, ErrorEmbeddable, IContainer, -} from '../../../../src/plugins/embeddable/public'; +} from '../../../../plugins/embeddable/public'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; +import { Vis } from '../vis'; import { getCapabilities, getTypes, @@ -37,7 +38,6 @@ import { } from '../services'; import { showNewVisModal } from '../wizard'; import { convertToSerializedVis } from '../saved_visualizations/_saved_vis'; -import { Vis } from '../types'; import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object'; interface VisualizationAttributes extends SavedObjectAttributes { diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index f4983a4313c4d..2aa346423297a 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -43,6 +43,9 @@ const createStartContract = (): VisualizationsStart => ({ createVis: jest.fn(), convertFromSerializedVis: jest.fn(), convertToSerializedVis: jest.fn(), + __LEGACY: { + createVisEmbeddableFromObject: jest.fn(), + }, }); const createInstance = async () => { diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index d3e7b759a4416..216defcee9016 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -37,7 +37,11 @@ import { setChrome, setOverlays, } from './services'; -import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeEmbeddableFactory } from './embeddable'; +import { + VISUALIZE_EMBEDDABLE_TYPE, + VisualizeEmbeddableFactory, + createVisEmbeddableFromObject, +} from './embeddable'; import { ExpressionsSetup, ExpressionsStart } from '../../../plugins/expressions/public'; import { EmbeddableSetup } from '../../../plugins/embeddable/public'; import { visualization as visualizationFunction } from './expressions/visualization_function'; @@ -69,6 +73,7 @@ export interface VisualizationsStart extends TypesStart { convertToSerializedVis: typeof convertToSerializedVis; convertFromSerializedVis: typeof convertFromSerializedVis; showNewVisModal: typeof showNewVisModal; + __LEGACY: { createVisEmbeddableFromObject: typeof createVisEmbeddableFromObject }; } export interface VisualizationsSetupDeps { @@ -163,6 +168,7 @@ export class VisualizationsPlugin convertToSerializedVis, convertFromSerializedVis, savedVisualizationsLoader, + __LEGACY: { createVisEmbeddableFromObject }, }; } From 01db8f66b705442983f7e994b5bfc028e0a5fa9d Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Fri, 27 Mar 2020 12:41:47 -0400 Subject: [PATCH 14/21] Fixes --- .../public/visualize/np_ready/legacy_app.js | 2 +- .../embeddables/create_embeddable_factory.ts | 1 + .../add_panel/add_panel_flyout.tsx | 13 +++--- src/plugins/embeddable/public/plugin.ts | 41 ++++++++++++------- .../embeddables/embedded_map_helpers.test.tsx | 1 + .../public/custom_time_range_badge.test.ts | 11 ----- 6 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js index 3ec0894abfe1d..7c9ab32ab2f72 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js @@ -60,7 +60,7 @@ const getResolvedResults = deps => { }) .then(vis => { results.vis = vis; - return createVisEmbeddableFromObject({ + return createVisEmbeddableFromObject(vis, { timeRange: data.query.timefilter.timefilter.getTime(), filters: data.query.filterManager.getFilters(), }); diff --git a/src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts b/src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts index c5577f1da5b98..7e6d0a7ffefed 100644 --- a/src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts +++ b/src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts @@ -44,6 +44,7 @@ export const defaultEmbeddableFactoryProvider = < type: def.type, isEditable: def.isEditable, getDisplayName: def.getDisplayName, + savedObjectMetaData: def.savedObjectMetaData, }; return factory; }; diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx index 95eeb63710c32..06c47bd1bcad8 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx @@ -121,15 +121,16 @@ export class AddPanelFlyout extends React.Component { public render() { const SavedObjectFinder = this.props.SavedObjectFinder; + const metaData = [...this.props.getAllFactories()] + .filter( + embeddableFactory => + Boolean(embeddableFactory.savedObjectMetaData) && !embeddableFactory.isContainerType + ) + .map(({ savedObjectMetaData }) => savedObjectMetaData as any); const savedObjectsFinder = ( - Boolean(embeddableFactory.savedObjectMetaData) && !embeddableFactory.isContainerType - ) - .map(({ savedObjectMetaData }) => savedObjectMetaData as any)} + savedObjectMetaData={metaData} showFilter={true} noItemsMessage={i18n.translate('embeddableApi.addPanel.noMatchingObjectsMessage', { defaultMessage: 'No matching objects found.', diff --git a/src/plugins/embeddable/public/plugin.ts b/src/plugins/embeddable/public/plugin.ts index 31e7e5c949e27..657194bb0c0c3 100644 --- a/src/plugins/embeddable/public/plugin.ts +++ b/src/plugins/embeddable/public/plugin.ts @@ -89,7 +89,10 @@ export class EmbeddablePublicPlugin implements Plugin this.embeddableFactories.values(), + getEmbeddableFactories: () => { + this.ensureFactoriesExist(); + return this.embeddableFactories.values(); + }, }; } @@ -114,23 +117,33 @@ export class EmbeddablePublicPlugin implements Plugin( embeddableFactoryId: string ): EmbeddableFactory => { - let factory = this.embeddableFactories.get(embeddableFactoryId); + this.ensureFactoryExists(embeddableFactoryId); + const factory = this.embeddableFactories.get(embeddableFactoryId); if (!factory) { - // This is only needed for legacy plugins registering embeddable factories after this start method. - const def = this.embeddableFactoryDefinitions.get(embeddableFactoryId); - if (def) { - factory = this.embeddableFactoryProvider - ? this.embeddableFactoryProvider(def) - : defaultEmbeddableFactoryProvider(def); - this.embeddableFactories.set(def.type, factory); - } else { - throw new Error( - `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] does not exist.` - ); - } + throw new Error( + `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] does not exist.` + ); } return factory as EmbeddableFactory; }; + + // These two functions are only to support legacy plugins registering factories after the start lifecycle. + private ensureFactoriesExist() { + this.embeddableFactoryDefinitions.forEach(def => this.ensureFactoryExists(def.type)); + } + + private ensureFactoryExists(type: string) { + if (!this.embeddableFactories.get(type)) { + const def = this.embeddableFactoryDefinitions.get(type); + if (!def) return; + this.embeddableFactories.set( + type, + this.embeddableFactoryProvider + ? this.embeddableFactoryProvider(def) + : defaultEmbeddableFactoryProvider(def) + ); + } + } } diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx index 9b55a053db844..e20a2ec9e17be 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx @@ -22,6 +22,7 @@ const { npStart } = createUiNewPlatformMock(); npStart.plugins.embeddable.getEmbeddableFactory = jest.fn().mockImplementation(() => ({ create: () => ({ reload: jest.fn(), + setRenderTooltipContent: jest.fn(), }), })); diff --git a/x-pack/plugins/advanced_ui_actions/public/custom_time_range_badge.test.ts b/x-pack/plugins/advanced_ui_actions/public/custom_time_range_badge.test.ts index d2b9fa9ac1655..3bf763470f002 100644 --- a/x-pack/plugins/advanced_ui_actions/public/custom_time_range_badge.test.ts +++ b/x-pack/plugins/advanced_ui_actions/public/custom_time_range_badge.test.ts @@ -9,17 +9,12 @@ import { findTestSubject } from '@elastic/eui/lib/test'; import { skip } from 'rxjs/operators'; import * as Rx from 'rxjs'; import { mount } from 'enzyme'; -import { EmbeddableFactory } from '../../../../src/plugins/embeddable/public'; import { TimeRangeEmbeddable, TimeRangeContainer, TIME_RANGE_EMBEDDABLE } from './test_helpers'; -import { TimeRangeEmbeddableFactory } from './test_helpers/time_range_embeddable_factory'; import { CustomTimeRangeBadge } from './custom_time_range_badge'; import { ReactElement } from 'react'; import { nextTick } from 'test_utils/enzyme_helpers'; test('Removing custom time range from badge resets embeddable back to container time', async done => { - const embeddableFactories = new Map(); - embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory()); - const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -79,8 +74,6 @@ test('Removing custom time range from badge resets embeddable back to container }); test(`badge is not compatible with embeddable that inherits from parent`, async () => { - const embeddableFactories = new Map(); - embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory()); const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -113,8 +106,6 @@ test(`badge is not compatible with embeddable that inherits from parent`, async }); test(`badge is compatible with embeddable that has custom time range`, async () => { - const embeddableFactories = new Map(); - embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory()); const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -148,8 +139,6 @@ test(`badge is compatible with embeddable that has custom time range`, async () }); test('Attempting to execute on incompatible embeddable throws an error', async () => { - const embeddableFactories = new Map(); - embeddableFactories.set(TIME_RANGE_EMBEDDABLE, new TimeRangeEmbeddableFactory()); const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, From aa040da0c38bd537d5d4c42a923b955cdedfb971 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Mon, 30 Mar 2020 10:51:20 -0400 Subject: [PATCH 15/21] Prefix variable with name "custom" to make it more obvious --- src/plugins/embeddable/public/plugin.test.ts | 2 +- src/plugins/embeddable/public/plugin.ts | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/plugins/embeddable/public/plugin.test.ts b/src/plugins/embeddable/public/plugin.test.ts index 5fcae2cc22380..4722ce946800b 100644 --- a/src/plugins/embeddable/public/plugin.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -46,7 +46,7 @@ test('can set custom embeddable factory provider', async () => { getDisplayName: () => 'Intercepted!', }); - setup.setEmbeddableFactoryProvider(customProvider); + setup.setCustomEmbeddableFactoryProvider(customProvider); setup.registerEmbeddableFactory('test', { type: 'test', create: () => Promise.resolve(undefined), diff --git a/src/plugins/embeddable/public/plugin.ts b/src/plugins/embeddable/public/plugin.ts index 657194bb0c0c3..a483f90f76dde 100644 --- a/src/plugins/embeddable/public/plugin.ts +++ b/src/plugins/embeddable/public/plugin.ts @@ -38,7 +38,7 @@ export interface EmbeddableSetup { id: string, factory: EmbeddableFactoryDefinition ) => void; - setEmbeddableFactoryProvider: (provider: EmbeddableFactoryProvider) => void; + setCustomEmbeddableFactoryProvider: (customProvider: EmbeddableFactoryProvider) => void; } export interface EmbeddableStart { @@ -58,7 +58,7 @@ export class EmbeddablePublicPlugin implements Plugin = new Map(); private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map(); - private embeddableFactoryProvider?: EmbeddableFactoryProvider; + private customEmbeddableFactoryProvider?: EmbeddableFactoryProvider; constructor(initializerContext: PluginInitializerContext) {} @@ -67,13 +67,13 @@ export class EmbeddablePublicPlugin implements Plugin { - if (this.embeddableFactoryProvider) { + setCustomEmbeddableFactoryProvider: (provider: EmbeddableFactoryProvider) => { + if (this.customEmbeddableFactoryProvider) { throw new Error( 'Custom embeddable factory provider is already set, and can only be set once' ); } - this.embeddableFactoryProvider = provider; + this.customEmbeddableFactoryProvider = provider; }, }; } @@ -82,8 +82,8 @@ export class EmbeddablePublicPlugin implements Plugin { this.embeddableFactories.set( def.type, - this.embeddableFactoryProvider - ? this.embeddableFactoryProvider(def) + this.customEmbeddableFactoryProvider + ? this.customEmbeddableFactoryProvider(def) : defaultEmbeddableFactoryProvider(def) ); }); @@ -140,8 +140,8 @@ export class EmbeddablePublicPlugin implements Plugin Date: Mon, 30 Mar 2020 10:57:05 -0400 Subject: [PATCH 16/21] Remove layerList from input state --- .../legacy/plugins/maps/public/embeddable/map_embeddable.tsx | 1 - .../public/components/embeddables/embedded_map_helpers.tsx | 2 +- .../functional/location_map/embeddables/embedded_map.tsx | 5 ++++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index 8c60e907b6d14..a427cee18d198 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -83,7 +83,6 @@ export interface MapEmbeddableInput extends EmbeddableInput { mapCenter?: MapCenter; hiddenLayers?: string[]; hideFilterActions?: boolean; - layerList?: unknown[]; } export interface MapEmbeddableOutput extends EmbeddableOutput { diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index c04af5c0ea43d..0c7a1212ba280 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -62,7 +62,6 @@ export const createEmbeddable = async ( } const input: MapEmbeddableInput = { - layerList: getLayerList(indexPatterns), title: i18n.MAP_TITLE, id: uuid.v4(), filters, @@ -110,6 +109,7 @@ export const createEmbeddable = async ( if (!isErrorEmbeddable(embeddableObject)) { embeddableObject.setRenderTooltipContent(renderTooltipContent); + embeddableObject.setLayerList(getLayerList(indexPatterns)); } // Wire up to app refresh action diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index 167942056413c..e20b9f2bdf56f 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -88,10 +88,13 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp } const embeddableObject = await factory.create({ ...input, - layerList: getLayerList(upPoints, downPoints, colors), title: i18n.MAP_TITLE, }); + if (embeddableObject && !isErrorEmbeddable(embeddableObject)) { + embeddableObject.setLayerList(getLayerList(upPoints, downPoints, colors)); + } + setEmbeddable(embeddableObject); } setupEmbeddable(); From 61bf75031efae7e1d2ed432849421f14d36444c3 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Mon, 30 Mar 2020 13:18:19 -0400 Subject: [PATCH 17/21] fixes --- src/plugins/embeddable/public/mocks.ts | 2 +- .../plugins/maps/public/embeddable/map_embeddable_factory.ts | 2 +- .../public/components/embeddables/embedded_map_helpers.test.tsx | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/embeddable/public/mocks.ts b/src/plugins/embeddable/public/mocks.ts index 65676bf3e5a91..2ee05d8316ace 100644 --- a/src/plugins/embeddable/public/mocks.ts +++ b/src/plugins/embeddable/public/mocks.ts @@ -30,7 +30,7 @@ export type Start = jest.Mocked; const createSetupContract = (): Setup => { const setupContract: Setup = { registerEmbeddableFactory: jest.fn(), - setEmbeddableFactoryProvider: jest.fn(), + setCustomEmbeddableFactoryProvider: jest.fn(), }; return setupContract; }; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts index 291484d2c5fc2..2ff07db6976cb 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -130,7 +130,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { }; create = async (input: MapEmbeddableInput, parent?: IContainer) => { - const layerList = input.layerList ?? getInitialLayers(); + const layerList = getInitialLayers(); const indexPatterns = await this._getIndexPatterns(layerList); return new MapEmbeddable( diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx index e20a2ec9e17be..f4e6ee5f878a6 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx @@ -23,6 +23,7 @@ npStart.plugins.embeddable.getEmbeddableFactory = jest.fn().mockImplementation(( create: () => ({ reload: jest.fn(), setRenderTooltipContent: jest.fn(), + setLayerList: jest.fn(), }), })); From 00f58ff3a28ad0fe3759fc93ad6f802c457ec623 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Tue, 31 Mar 2020 10:44:52 -0400 Subject: [PATCH 18/21] Update src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx Co-Authored-By: Vadim Dalecky --- .../dashboard/public/embeddable/dashboard_container_factory.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx index 16cf0f5c3818b..eb628c141e8f0 100644 --- a/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx @@ -56,7 +56,7 @@ export class DashboardContainerFactory return !!capabilities.createNew && !!capabilities.showWriteControls; }; - public getDisplayName() { + public readonly getDisplayName = () => { return i18n.translate('dashboard.factory.displayName', { defaultMessage: 'dashboard', }); From 57d7f81bde581e3e88ea768c7a4604db319b2a70 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Tue, 31 Mar 2020 10:47:28 -0400 Subject: [PATCH 19/21] review updates --- ...=> default_embeddable_factory_provider.ts} | 14 +++--- .../public/lib/embeddables/embeddable.tsx | 2 + .../public/lib/embeddables/index.ts | 2 +- src/plugins/embeddable/public/plugin.test.ts | 50 +++++++++++++++++++ 4 files changed, 61 insertions(+), 7 deletions(-) rename src/plugins/embeddable/public/lib/embeddables/{create_embeddable_factory.ts => default_embeddable_factory_provider.ts} (82%) diff --git a/src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts b/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts similarity index 82% rename from src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts rename to src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts index 7e6d0a7ffefed..570a78fc41ea9 100644 --- a/src/plugins/embeddable/public/lib/embeddables/create_embeddable_factory.ts +++ b/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts @@ -32,18 +32,20 @@ export const defaultEmbeddableFactoryProvider = < ): EmbeddableFactory => { const factory: EmbeddableFactory = { isContainerType: def.isContainerType ?? false, - canCreateNew: def.canCreateNew ?? (() => true), - getDefaultInput: def.getDefaultInput ?? (() => ({})), - getExplicitInput: def.getExplicitInput ?? (() => Promise.resolve({})), + canCreateNew: def.canCreateNew ? def.canCreateNew.bind(def) : () => true, + getDefaultInput: def.getDefaultInput ? def.getDefaultInput.bind(def) : () => ({}), + getExplicitInput: def.getExplicitInput + ? def.getExplicitInput.bind(def) + : () => Promise.resolve({}), createFromSavedObject: def.createFromSavedObject ?? ((savedObjectId: string, input: Partial, parent?: IContainer) => { throw new Error(`Creation from saved object not supported by type ${def.type}`); }), - create: def.create, + create: def.create.bind(def), type: def.type, - isEditable: def.isEditable, - getDisplayName: def.getDisplayName, + isEditable: def.isEditable.bind(def), + getDisplayName: def.getDisplayName.bind(def), savedObjectMetaData: def.savedObjectMetaData, }; return factory; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx index eb10c16806640..a135484ff61be 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx @@ -158,6 +158,8 @@ export abstract class Embeddable< */ public destroy(): void { this.destoyed = true; + this.input$.complete(); + this.output$.complete(); if (this.parentSubscription) { this.parentSubscription.unsubscribe(); } diff --git a/src/plugins/embeddable/public/lib/embeddables/index.ts b/src/plugins/embeddable/public/lib/embeddables/index.ts index d65f7c81d25ab..4d6ab37a50c05 100644 --- a/src/plugins/embeddable/public/lib/embeddables/index.ts +++ b/src/plugins/embeddable/public/lib/embeddables/index.ts @@ -20,7 +20,7 @@ export { EmbeddableOutput, EmbeddableInput, IEmbeddable } from './i_embeddable'; export { Embeddable } from './embeddable'; export * from './embeddable_factory'; export * from './embeddable_factory_definition'; -export * from './create_embeddable_factory'; +export * from './default_embeddable_factory_provider'; export { ErrorEmbeddable, isErrorEmbeddable } from './error_embeddable'; export { withEmbeddableSubscription } from './with_subscription'; export { EmbeddableFactoryRenderer } from './embeddable_factory_renderer'; diff --git a/src/plugins/embeddable/public/plugin.test.ts b/src/plugins/embeddable/public/plugin.test.ts index 4722ce946800b..804f3e2e8a7b4 100644 --- a/src/plugins/embeddable/public/plugin.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -20,6 +20,7 @@ import { coreMock } from '../../../core/public/mocks'; import { testPlugin } from './tests/test_plugin'; import { EmbeddableFactoryProvider } from './types'; import { defaultEmbeddableFactoryProvider } from './lib'; +import { HelloWorldEmbeddable } from '../../../../examples/embeddable_examples/public'; test('cannot register embeddable factory with the same ID', async () => { const coreSetup = coreMock.createSetup(); @@ -58,3 +59,52 @@ test('can set custom embeddable factory provider', async () => { const factory = start.getEmbeddableFactory('test'); expect(factory!.getDisplayName()).toEqual('Intercepted!'); }); + +test('custom embeddable factory provider test for intercepting embeddable creation and destruction', async () => { + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createStart(); + const { setup, doStart } = testPlugin(coreSetup, coreStart); + + let updateCount = 0; + const customProvider: EmbeddableFactoryProvider = def => { + return { + ...defaultEmbeddableFactoryProvider(def), + create: async (input, parent) => { + const embeddable = await defaultEmbeddableFactoryProvider(def).create(input, parent); + if (embeddable) { + const subscription = embeddable.getInput$().subscribe( + () => { + updateCount++; + }, + () => {}, + () => { + subscription.unsubscribe(); + updateCount = 0; + } + ); + } + return embeddable; + }, + }; + }; + + setup.setCustomEmbeddableFactoryProvider(customProvider); + setup.registerEmbeddableFactory('test', { + type: 'test', + create: (input, parent) => Promise.resolve(new HelloWorldEmbeddable(input, parent)), + getDisplayName: () => 'Test', + isEditable: () => Promise.resolve(true), + }); + + const start = doStart(); + const factory = start.getEmbeddableFactory('test'); + + const embeddable = await factory?.create({ id: '123' }); + embeddable!.updateInput({ title: 'boo' }); + // initial subscription, plus the second update. + expect(updateCount).toEqual(2); + + embeddable!.destroy(); + await new Promise(resolve => process.nextTick(resolve)); + expect(updateCount).toEqual(0); +}); From fccdb4addbf7dbb2eaedf4097042197a985c4704 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Tue, 31 Mar 2020 11:30:37 -0400 Subject: [PATCH 20/21] fixes --- .../dashboard/public/embeddable/dashboard_container_factory.tsx | 2 +- .../plugins/siem/public/components/embeddables/embedded_map.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx index eb628c141e8f0..9ff48cb45adfd 100644 --- a/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx @@ -60,7 +60,7 @@ export class DashboardContainerFactory return i18n.translate('dashboard.factory.displayName', { defaultMessage: 'dashboard', }); - } + }; public getDefaultInput(): Partial { return { diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx index 763e911480cd1..cbb4006bbf933 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx @@ -13,7 +13,7 @@ import { npStart } from 'ui/new_platform'; import { EmbeddablePanel, ErrorEmbeddable, -} from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +} from '../../../../../../../src/plugins/embeddable/public'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { getIndexPatternTitleIdMapping } from '../../hooks/api/helpers'; import { useIndexPatterns } from '../../hooks/use_index_patterns'; From 06bdd5a3f064feae834b89e5c919e447c238095f Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Tue, 31 Mar 2020 11:35:03 -0400 Subject: [PATCH 21/21] update maps readme --- .../plugins/maps/public/embeddable/README.md | 46 ++----------------- 1 file changed, 4 insertions(+), 42 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/embeddable/README.md b/x-pack/legacy/plugins/maps/public/embeddable/README.md index 8740739a3e8ef..8ce3794e2ed2c 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/README.md +++ b/x-pack/legacy/plugins/maps/public/embeddable/README.md @@ -31,14 +31,14 @@ ``` const factory = new MapEmbeddableFactory(); const input = { - layerList: [], // where layerList is same as saved object layerListJSON property (unstringified) - title: 'my map', hideFilterActions: true, isLayerTOCOpen: false, openTOCDetails: ['tfi3f', 'edh66'], mapCenter: { lat: 0.0, lon: 0.0, zoom: 7 } } const mapEmbeddable = await factory.create(input, parent); +// where layerList is same as saved object layerListJSON property (unstringified)) +mapEmbeddable.setLayerList([]); ``` #### Customize tooltip @@ -61,6 +61,7 @@ const renderTooltipContent = ({ addFilters, closeTooltip, features, isLocked, lo } const mapEmbeddable = await factory.create(input, parent) +mapEmbeddable.setLayerList(layerList); mapEmbeddable.setRenderTooltipContent(renderTooltipContent); ``` @@ -80,6 +81,7 @@ const eventHandlers = { } const mapEmbeddable = await factory.create(input, parent); +mapEmbeddable.setLayerList(layerList); mapEmbeddable.setRenderTooltipContent(renderTooltipContent); mapEmbeddable.setEventHandlers(eventHandlers); ``` @@ -92,46 +94,6 @@ Geojson sources will not update unless you modify `__featureCollection` property ``` const factory = new MapEmbeddableFactory(); const input = { - layerList: [ - { - 'id': 'gaxya', - 'label': 'My geospatial data', - 'minZoom': 0, - 'maxZoom': 24, - 'alpha': 1, - 'sourceDescriptor': { - 'id': 'b7486', - 'type': 'GEOJSON_FILE', - '__featureCollection': { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [0, 0], [10, 10], [10, 0], [0, 0] - ] - ] - }, - "properties": { - "name": "null island", - "another_prop": "something else interesting" - } - } - ] - } - }, - 'visible': true, - 'style': { - 'type': 'VECTOR', - 'properties': {} - }, - 'type': 'VECTOR' - } - ], - title: 'my map', hideFilterActions: true, isLayerTOCOpen: false, openTOCDetails: ['tfi3f', 'edh66'],