[Embeddable] Move registries (#182621)

Moves the React Embeddable registry, and saved object to panel registries onto the embeddable start / setup contracts
This commit is contained in:
Devon Thomson 2024-05-10 12:42:34 -04:00 committed by GitHub
parent afaba520d0
commit 6584663bf6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 240 additions and 154 deletions

View file

@ -13,11 +13,7 @@ import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataViewFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
import {
EmbeddableSetup,
EmbeddableStart,
registerReactEmbeddableFactory,
} from '@kbn/embeddable-plugin/public';
import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
import { setupApp } from './app/setup_app';
@ -51,36 +47,47 @@ export interface StartDeps {
export class EmbeddableExamplesPlugin implements Plugin<void, void, SetupDeps, StartDeps> {
public setup(core: CoreSetup<StartDeps>, { embeddable, developerExamples }: SetupDeps) {
setupApp(core, developerExamples);
}
public start(core: CoreStart, deps: StartDeps) {
registerCreateFieldListAction(deps.uiActions);
registerReactEmbeddableFactory(FIELD_LIST_ID, async () => {
const startServicesPromise = core.getStartServices();
embeddable.registerReactEmbeddableFactory(FIELD_LIST_ID, async () => {
const { getFieldListFactory } = await import(
'./react_embeddables/field_list/field_list_react_embeddable'
);
return getFieldListFactory(core, deps);
const [coreStart, deps] = await startServicesPromise;
return getFieldListFactory(coreStart, deps);
});
registerFieldListPanelPlacementSetting(deps.dashboard);
registerCreateEuiMarkdownAction(deps.uiActions);
registerReactEmbeddableFactory(EUI_MARKDOWN_ID, async () => {
embeddable.registerReactEmbeddableFactory(EUI_MARKDOWN_ID, async () => {
const { markdownEmbeddableFactory } = await import(
'./react_embeddables/eui_markdown/eui_markdown_react_embeddable'
);
return markdownEmbeddableFactory;
});
registerAddSearchPanelAction(deps.uiActions);
registerSearchEmbeddable(deps);
registerCreateDataTableAction(deps.uiActions);
registerReactEmbeddableFactory(DATA_TABLE_ID, async () => {
embeddable.registerReactEmbeddableFactory(DATA_TABLE_ID, async () => {
const { getDataTableFactory } = await import(
'./react_embeddables/data_table/data_table_react_embeddable'
);
return getDataTableFactory(core, deps);
const [coreStart, deps] = await startServicesPromise;
return getDataTableFactory(coreStart, deps);
});
registerSearchEmbeddable(
embeddable,
new Promise((resolve) => startServicesPromise.then(([_, startDeps]) => resolve(startDeps)))
);
}
public start(core: CoreStart, deps: StartDeps) {
registerCreateFieldListAction(deps.uiActions);
registerFieldListPanelPlacementSetting(deps.dashboard);
registerCreateEuiMarkdownAction(deps.uiActions);
registerAddSearchPanelAction(deps.uiActions);
registerCreateDataTableAction(deps.uiActions);
}
public stop() {}

View file

@ -6,14 +6,14 @@
* Side Public License, v 1.
*/
import { registerReactEmbeddableSavedObject } from '@kbn/embeddable-plugin/public';
import { EmbeddableSetup } from '@kbn/embeddable-plugin/public';
const MY_EMBEDDABLE_TYPE = 'myEmbeddableType';
const MY_SAVED_OBJECT_TYPE = 'mySavedObjectType';
const APP_ICON = 'logoKibana';
export const registerMyEmbeddableSavedObject = () =>
registerReactEmbeddableSavedObject({
export const registerMyEmbeddableSavedObject = (embeddableSetup: EmbeddableSetup) =>
embeddableSetup.registerReactEmbeddableSavedObject({
onAdd: (container, savedObject) => {
container.addNewPanel({
panelType: MY_EMBEDDABLE_TYPE,

View file

@ -6,13 +6,13 @@
* Side Public License, v 1.
*/
import { registerReactEmbeddableFactory } from '@kbn/embeddable-plugin/public';
import { EmbeddableSetup } from '@kbn/embeddable-plugin/public';
import { SEARCH_EMBEDDABLE_ID } from './constants';
import { Services } from './types';
export function registerSearchEmbeddable(services: Services) {
registerReactEmbeddableFactory(SEARCH_EMBEDDABLE_ID, async () => {
export function registerSearchEmbeddable(embeddable: EmbeddableSetup, services: Promise<Services>) {
embeddable.registerReactEmbeddableFactory(SEARCH_EMBEDDABLE_ID, async () => {
const { getSearchEmbeddableFactory } = await import('./search_react_embeddable');
return getSearchEmbeddableFactory(services);
return getSearchEmbeddableFactory(await services);
});
}

View file

@ -7,17 +7,17 @@
*/
import { AppMountParameters, CoreSetup, Plugin } from '@kbn/core/public';
import type { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DashboardStart } from '@kbn/dashboard-plugin/public';
import { registerReactEmbeddableFactory } from '@kbn/embeddable-plugin/public';
import img from './portable_dashboard_image.png';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
import { EmbeddableSetup } from '@kbn/embeddable-plugin/public';
import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';
import { FILTER_DEBUGGER_EMBEDDABLE_ID, PLUGIN_ID } from './constants';
import img from './portable_dashboard_image.png';
interface SetupDeps {
developerExamples: DeveloperExamplesSetup;
embeddable: EmbeddableSetup;
}
export interface StartDeps {
@ -27,7 +27,7 @@ export interface StartDeps {
}
export class PortableDashboardsExamplePlugin implements Plugin<void, void, SetupDeps, StartDeps> {
public setup(core: CoreSetup<StartDeps>, { developerExamples }: SetupDeps) {
public setup(core: CoreSetup<StartDeps>, { developerExamples, embeddable }: SetupDeps) {
core.application.register({
id: PLUGIN_ID,
title: 'Portable dashboard examples',
@ -45,14 +45,14 @@ export class PortableDashboardsExamplePlugin implements Plugin<void, void, Setup
description: `Showcases different ways to embed a dashboard into your app`,
image: img,
});
}
public async start() {
registerReactEmbeddableFactory(FILTER_DEBUGGER_EMBEDDABLE_ID, async () => {
embeddable.registerReactEmbeddableFactory(FILTER_DEBUGGER_EMBEDDABLE_ID, async () => {
const { factory } = await import('./filter_debugger_embeddable');
return factory;
});
}
public async start() {}
public stop() {}
}

View file

@ -8,12 +8,7 @@
import { EuiLoadingChart } from '@elastic/eui';
import { css } from '@emotion/react';
import {
EmbeddablePanel,
reactEmbeddableRegistryHasKey,
ReactEmbeddableRenderer,
ViewMode,
} from '@kbn/embeddable-plugin/public';
import { EmbeddablePanel, ReactEmbeddableRenderer, ViewMode } from '@kbn/embeddable-plugin/public';
import { PhaseEvent } from '@kbn/presentation-publishing';
import classNames from 'classnames';
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
@ -101,6 +96,9 @@ export const Item = React.forwardRef<HTMLDivElement, Props>(
: undefined;
const renderedEmbeddable = useMemo(() => {
const {
embeddable: { reactEmbeddableRegistryHasKey },
} = pluginServices.getServices();
const references = getReferencesForPanelId(id, container.savedObjectReferences);
const panelProps = {
@ -134,7 +132,7 @@ export const Item = React.forwardRef<HTMLDivElement, Props>(
{...panelProps}
/>
);
}, [id, container, type, index, useMargins, onPanelStatusChange, panel.explicitInput]);
}, [id, container, useMargins, type, index, onPanelStatusChange, panel.explicitInput]);
return (
<div

View file

@ -10,7 +10,6 @@ import {
isReferenceOrValueEmbeddable,
PanelIncompatibleError,
PanelNotFoundError,
reactEmbeddableRegistryHasKey,
} from '@kbn/embeddable-plugin/public';
import { apiHasSerializableState } from '@kbn/presentation-containers';
import { apiPublishesPanelTitle, getPanelTitle } from '@kbn/presentation-publishing';
@ -77,13 +76,17 @@ const duplicateReactEmbeddableInput = async (
};
export async function duplicateDashboardPanel(this: DashboardContainer, idToDuplicate: string) {
const {
notifications: { toasts },
embeddable: { reactEmbeddableRegistryHasKey },
} = pluginServices.getServices();
const panelToClone = this.getInput().panels[idToDuplicate] as DashboardPanelState;
const duplicatedPanelState = reactEmbeddableRegistryHasKey(panelToClone.type)
? await duplicateReactEmbeddableInput(this, panelToClone, idToDuplicate)
: await duplicateLegacyInput(this, panelToClone, idToDuplicate);
pluginServices.getServices().notifications.toasts.addSuccess({
toasts.addSuccess({
title: dashboardClonePanelActionStrings.getSuccessMessage(),
'data-test-subj': 'addObjectToContainerSuccess',
});

View file

@ -9,11 +9,7 @@
import { Reference } from '@kbn/content-management-utils';
import type { PersistableControlGroupInput } from '@kbn/controls-plugin/common';
import { reportPerformanceMetricEvent } from '@kbn/ebt-tools';
import {
EmbeddableInput,
isReferenceOrValueEmbeddable,
reactEmbeddableRegistryHasKey,
} from '@kbn/embeddable-plugin/public';
import { EmbeddableInput, isReferenceOrValueEmbeddable } from '@kbn/embeddable-plugin/public';
import { apiHasSerializableState } from '@kbn/presentation-containers';
import { showSaveModal } from '@kbn/saved-objects-plugin/public';
import { cloneDeep } from 'lodash';
@ -35,6 +31,9 @@ import { DashboardSaveModal } from './overlays/save_modal';
const serializeAllPanelState = async (
dashboard: DashboardContainer
): Promise<{ panels: DashboardContainerInput['panels']; references: Reference[] }> => {
const {
embeddable: { reactEmbeddableRegistryHasKey },
} = pluginServices.getServices();
const references: Reference[] = [];
const panels = cloneDeep(dashboard.getInput().panels);
for (const [uuid, panel] of Object.entries(panels)) {

View file

@ -26,7 +26,6 @@ import {
embeddableInputToSubject,
isExplicitInputWithAttributes,
PanelNotFoundError,
reactEmbeddableRegistryHasKey,
ViewMode,
type EmbeddableFactory,
type EmbeddableInput,
@ -482,7 +481,7 @@ export class DashboardContainer
) {
const {
notifications: { toasts },
embeddable: { getEmbeddableFactory },
embeddable: { getEmbeddableFactory, reactEmbeddableRegistryHasKey },
} = pluginServices.getServices();
const onSuccess = (id?: string, title?: string) => {
@ -569,6 +568,9 @@ export class DashboardContainer
}
public getDashboardPanelFromId = async (panelId: string) => {
const {
embeddable: { reactEmbeddableRegistryHasKey },
} = pluginServices.getServices();
const panel = this.getInput().panels[panelId];
if (reactEmbeddableRegistryHasKey(panel.type)) {
const child = this.children$.value[panelId];
@ -731,6 +733,9 @@ export class DashboardContainer
};
public async getPanelTitles(): Promise<string[]> {
const {
embeddable: { reactEmbeddableRegistryHasKey },
} = pluginServices.getServices();
const titles: string[] = [];
for (const [id, panel] of Object.entries(this.getInput().panels)) {
const title = await (async () => {
@ -814,6 +819,9 @@ export class DashboardContainer
};
public removePanel(id: string) {
const {
embeddable: { reactEmbeddableRegistryHasKey },
} = pluginServices.getServices();
const type = this.getInput().panels[id]?.type;
this.removeEmbeddable(id);
if (reactEmbeddableRegistryHasKey(type)) {

View file

@ -6,10 +6,10 @@
* Side Public License, v 1.
*/
import { reactEmbeddableRegistryHasKey } from '@kbn/embeddable-plugin/public';
import { compareFilters, COMPARE_ALL_OPTIONS, isFilterPinned } from '@kbn/es-query';
import fastIsEqual from 'fast-deep-equal';
import { DashboardContainerInput } from '../../../../common';
import { pluginServices } from '../../../services/plugin_services';
import { DashboardContainer } from '../../embeddable/dashboard_container';
import { DashboardContainerInputWithoutId } from '../../types';
import { areTimesEqual, getPanelLayoutsAreEqual } from './dashboard_diffing_utils';
@ -74,6 +74,9 @@ export const unsavedChangesDiffingFunctions: DashboardDiffFunctions = {
const explicitInputComparePromises = Object.values(currentValue ?? {}).map(
(panel) =>
new Promise<boolean>((resolve, reject) => {
const {
embeddable: { reactEmbeddableRegistryHasKey },
} = pluginServices.getServices();
const embeddableId = panel.explicitInput.id;
if (!embeddableId || reactEmbeddableRegistryHasKey(panel.type)) {
// if this is a new style embeddable, it will handle its own diffing.

View file

@ -9,11 +9,11 @@
import { ControlGroupInput } from '@kbn/controls-plugin/common';
import {
EmbeddableFactoryNotFoundError,
reactEmbeddableRegistryHasKey,
runEmbeddableFactoryMigrations,
} from '@kbn/embeddable-plugin/public';
import { DashboardContainerInput, DashboardPanelState } from '../../../../common';
import { type DashboardEmbeddableService } from '../../embeddable/types';
import { pluginServices } from '../../plugin_services';
import { SavedDashboardInput } from '../types';
/**
@ -26,6 +26,9 @@ export const migrateDashboardInput = (
dashboardInput: SavedDashboardInput,
embeddable: DashboardEmbeddableService
) => {
const {
embeddable: { reactEmbeddableRegistryHasKey },
} = pluginServices.getServices();
let anyMigrationRun = false;
if (!dashboardInput) return dashboardInput;
if (dashboardInput.controlGroupInput) {

View file

@ -16,6 +16,7 @@ export const embeddableServiceFactory: EmbeddableServiceFactory = () => {
const pluginMock = embeddablePluginMock.createStartContract();
return {
reactEmbeddableRegistryHasKey: pluginMock.reactEmbeddableRegistryHasKey,
getEmbeddableFactories: pluginMock.getEmbeddableFactories,
getEmbeddableFactory: pluginMock.getEmbeddableFactory,
getStateTransfer: pluginMock.getStateTransfer,

View file

@ -21,6 +21,7 @@ export const embeddableServiceFactory: EmbeddableServiceFactory = ({ startPlugin
const { embeddable } = startPlugins;
return pick(embeddable, [
'reactEmbeddableRegistryHasKey',
'getEmbeddableFactory',
'getEmbeddableFactories',
'getStateTransfer',

View file

@ -10,6 +10,7 @@ import type { EmbeddableStart } from '@kbn/embeddable-plugin/public';
export type DashboardEmbeddableService = Pick<
EmbeddableStart,
| 'reactEmbeddableRegistryHasKey'
| 'getEmbeddableFactories'
| 'getEmbeddableFactory'
| 'getAllMigrations'

View file

@ -46,7 +46,6 @@ export {
PANEL_BADGE_TRIGGER,
PANEL_HOVER_TRIGGER,
PANEL_NOTIFICATION_TRIGGER,
registerReactEmbeddableSavedObject,
runEmbeddableFactoryMigrations,
SELECT_RANGE_TRIGGER,
shouldFetch$,
@ -96,16 +95,12 @@ export type { EnhancementRegistryDefinition } from './types';
export {
ReactEmbeddableRenderer,
reactEmbeddableRegistryHasKey,
registerReactEmbeddableFactory,
type DefaultEmbeddableApi,
type ReactEmbeddableFactory,
type ReactEmbeddableRegistration,
startTrackingEmbeddableUnsavedChanges,
} from './react_embeddable_system';
export { registerSavedObjectToPanelMethod } from './registry/saved_object_to_panel_methods';
export function plugin(initializerContext: PluginInitializerContext) {
return new EmbeddablePublicPlugin(initializerContext);
}

View file

@ -32,8 +32,14 @@ import {
SelfStyledEmbeddable,
} from '.';
import { setKibanaServices } from './kibana_services';
import { registerReactEmbeddableSavedObject } from './lib';
import { SelfStyledOptions } from './lib/self_styled_embeddable/types';
import { EmbeddablePublicPlugin } from './plugin';
import {
reactEmbeddableRegistryHasKey,
registerReactEmbeddableFactory,
} from './react_embeddable_system';
import { registerSavedObjectToPanelMethod } from './registry/saved_object_to_panel_methods';
export { mockAttributeService } from './lib/attribute_service/attribute_service.mock';
export type Setup = jest.Mocked<EmbeddableSetup>;
@ -93,6 +99,13 @@ export function mockFilterableEmbeddable<OriginalEmbeddableType>(
const createSetupContract = (): Setup => {
const setupContract: Setup = {
registerSavedObjectToPanelMethod: jest
.fn()
.mockImplementation(registerSavedObjectToPanelMethod),
registerReactEmbeddableSavedObject: jest
.fn()
.mockImplementation(registerReactEmbeddableSavedObject),
registerReactEmbeddableFactory: jest.fn().mockImplementation(registerReactEmbeddableFactory),
registerEmbeddableFactory: jest.fn(),
registerEnhancement: jest.fn(),
setCustomEmbeddableFactoryProvider: jest.fn(),
@ -102,6 +115,7 @@ const createSetupContract = (): Setup => {
const createStartContract = (): Start => {
const startContract: Start = {
reactEmbeddableRegistryHasKey: jest.fn().mockImplementation(reactEmbeddableRegistryHasKey),
getEmbeddableFactories: jest.fn(),
getEmbeddableFactory: jest.fn(),
telemetry: jest.fn(),

View file

@ -39,6 +39,7 @@ import {
defaultEmbeddableFactoryProvider,
IEmbeddable,
SavedObjectEmbeddableInput,
registerReactEmbeddableSavedObject,
} from './lib';
import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition';
import { EmbeddableStateTransfer } from './lib/state_transfer';
@ -53,6 +54,13 @@ import {
} from '../common/lib';
import { getAllMigrations } from '../common/lib/get_all_migrations';
import { setKibanaServices } from './kibana_services';
import {
DefaultEmbeddableApi,
ReactEmbeddableFactory,
reactEmbeddableRegistryHasKey,
registerReactEmbeddableFactory,
} from './react_embeddable_system';
import { registerSavedObjectToPanelMethod } from './registry/saved_object_to_panel_methods';
export interface EmbeddableSetupDependencies {
uiActions: UiActionsSetup;
@ -68,6 +76,23 @@ export interface EmbeddableStartDependencies {
}
export interface EmbeddableSetup {
registerReactEmbeddableSavedObject: typeof registerReactEmbeddableSavedObject;
registerSavedObjectToPanelMethod: typeof registerSavedObjectToPanelMethod;
/**
* Registers an async {@link ReactEmbeddableFactory} getter.
*/
registerReactEmbeddableFactory: <
StateType extends object = object,
APIType extends DefaultEmbeddableApi<StateType> = DefaultEmbeddableApi<StateType>
>(
type: string,
getFactory: () => Promise<ReactEmbeddableFactory<StateType, APIType>>
) => void;
/**
* @deprecated use {@link registerReactEmbeddableFactory} instead.
*/
registerEmbeddableFactory: <
I extends EmbeddableInput,
O extends EmbeddableOutput,
@ -76,11 +101,25 @@ export interface EmbeddableSetup {
id: string,
factory: EmbeddableFactoryDefinition<I, O, E>
) => () => EmbeddableFactory<I, O, E>;
/**
* @deprecated
*/
registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void;
/**
* @deprecated
*/
setCustomEmbeddableFactoryProvider: (customProvider: EmbeddableFactoryProvider) => void;
}
export interface EmbeddableStart extends PersistableStateService<EmbeddableStateWithType> {
/**
* Checks if a {@link ReactEmbeddableFactory} has been registered using {@link registerReactEmbeddableFactory}
*/
reactEmbeddableRegistryHasKey: (type: string) => boolean;
/**
* @deprecated use {@link registerReactEmbeddableFactory} instead.
*/
getEmbeddableFactory: <
I extends EmbeddableInput = EmbeddableInput,
O extends EmbeddableOutput = EmbeddableOutput,
@ -88,6 +127,10 @@ export interface EmbeddableStart extends PersistableStateService<EmbeddableState
>(
embeddableFactoryId: string
) => EmbeddableFactory<I, O, E> | undefined;
/**
* @deprecated
*/
getEmbeddableFactories: () => IterableIterator<EmbeddableFactory>;
getStateTransfer: (storage?: Storage) => EmbeddableStateTransfer;
getAttributeService: <
@ -121,6 +164,11 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
bootstrap(uiActions);
return {
registerReactEmbeddableFactory,
reactEmbeddableRegistryHasKey,
registerSavedObjectToPanelMethod,
registerReactEmbeddableSavedObject,
registerEmbeddableFactory: this.registerEmbeddableFactory,
registerEnhancement: this.registerEnhancement,
setCustomEmbeddableFactoryProvider: (provider: EmbeddableFactoryProvider) => {
@ -169,6 +217,8 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
);
const embeddableStart: EmbeddableStart = {
reactEmbeddableRegistryHasKey,
getEmbeddableFactory: this.getEmbeddableFactory,
getEmbeddableFactories: this.getEmbeddableFactories,
getAttributeService: (type: string, options) =>

View file

@ -40,7 +40,6 @@ export type ReactEmbeddableApiRegistration<
*
* Embeddables are React components that manage their own state, can be serialized and
* deserialized, and return an API that can be used to interact with them imperatively.
* provided by the parent, and will not save any state to an external store.
**/
export interface ReactEmbeddableFactory<
SerializedState extends object = object,

View file

@ -7,7 +7,7 @@
*/
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { EmbeddableSetup, registerReactEmbeddableFactory } from '@kbn/embeddable-plugin/public';
import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
import { FilesSetup, FilesStart } from '@kbn/files-plugin/public';
import {
ScreenshotModePluginSetup,
@ -33,6 +33,7 @@ export interface ImageEmbeddableStartDependencies {
files: FilesStart;
security?: SecurityPluginStart;
uiActions: UiActionsStart;
embeddable: EmbeddableStart;
screenshotMode?: ScreenshotModePluginStart;
embeddableEnhanced?: EmbeddableEnhancedPluginStart;
}
@ -59,6 +60,15 @@ export class ImageEmbeddablePlugin
plugins: ImageEmbeddableSetupDependencies
): SetupContract {
plugins.uiActions.registerTrigger(imageClickTrigger);
plugins.embeddable.registerReactEmbeddableFactory(IMAGE_EMBEDDABLE_TYPE, async () => {
const [_, { getImageEmbeddableFactory }, [__, { embeddableEnhanced }]] = await Promise.all([
untilPluginStartServicesReady(),
import('./image_embeddable/get_image_embeddable_factory'),
core.getStartServices(),
]);
return getImageEmbeddableFactory({ embeddableEnhanced });
});
return {};
}
@ -68,13 +78,6 @@ export class ImageEmbeddablePlugin
untilPluginStartServicesReady().then(() => {
registerCreateImageAction();
});
registerReactEmbeddableFactory(IMAGE_EMBEDDABLE_TYPE, async () => {
const [_, { getImageEmbeddableFactory }] = await Promise.all([
untilPluginStartServicesReady(),
import('./image_embeddable/get_image_embeddable_factory'),
]);
return getImageEmbeddableFactory({ embeddableEnhanced: plugins.embeddableEnhanced });
});
return {};
}

View file

@ -17,7 +17,7 @@ import type {
ContentManagementPublicStart,
} from '@kbn/content-management-plugin/public';
import type { SOWithMetadata } from '@kbn/content-management-utils';
import { EmbeddableStart, registerSavedObjectToPanelMethod } from '@kbn/embeddable-plugin/public';
import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
import {
getSavedSearch,
saveSavedSearch,
@ -68,6 +68,7 @@ export interface SavedSearchPublicPluginStart {
* Saved search plugin public Setup contract
*/
export interface SavedSearchPublicSetupDependencies {
embeddable: EmbeddableSetup;
contentManagement: ContentManagementPublicSetup;
expressions: ExpressionsSetup;
}
@ -94,7 +95,7 @@ export class SavedSearchPublicPlugin
{
public setup(
{ getStartServices }: CoreSetup,
{ contentManagement, expressions }: SavedSearchPublicSetupDependencies
{ contentManagement, expressions, embeddable }: SavedSearchPublicSetupDependencies
) {
contentManagement.registry.register({
id: SavedSearchType,
@ -117,7 +118,7 @@ export class SavedSearchPublicPlugin
expressions.registerType(kibanaContext);
registerSavedObjectToPanelMethod<SavedSearchAttributes, SearchByValueInput>(
embeddable.registerSavedObjectToPanelMethod<SavedSearchAttributes, SearchByValueInput>(
SavedSearchType,
(savedObject) => {
if (!savedObject.managed) {

View file

@ -47,11 +47,7 @@ import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { ExpressionsSetup, ExpressionsStart } from '@kbn/expressions-plugin/public';
import {
EmbeddableSetup,
EmbeddableStart,
registerSavedObjectToPanelMethod,
} from '@kbn/embeddable-plugin/public';
import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public';
import type { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public';
import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public';
@ -410,36 +406,36 @@ export class VisualizationsPlugin
name: 'Visualize Library',
});
registerSavedObjectToPanelMethod<VisualizationSavedObjectAttributes, VisualizeByValueInput>(
CONTENT_ID,
(savedObject) => {
const visState = savedObject.attributes.visState;
// not sure if visState actually is ever undefined, but following the type
if (!savedObject.managed || !visState) {
return {
savedObjectId: savedObject.id,
};
}
// data is not always defined, so I added a default value since the extract
// routine in the embeddable factory expects it to be there
const savedVis = JSON.parse(visState) as Omit<SerializedVis, 'data'> & {
data?: SerializedVisData;
};
if (!savedVis.data) {
savedVis.data = {
searchSource: {},
aggs: [],
};
}
embeddable.registerSavedObjectToPanelMethod<
VisualizationSavedObjectAttributes,
VisualizeByValueInput
>(CONTENT_ID, (savedObject) => {
const visState = savedObject.attributes.visState;
// not sure if visState actually is ever undefined, but following the type
if (!savedObject.managed || !visState) {
return {
savedVis: savedVis as SerializedVis, // now we're sure we have "data" prop
savedObjectId: savedObject.id,
};
}
);
// data is not always defined, so I added a default value since the extract
// routine in the embeddable factory expects it to be there
const savedVis = JSON.parse(visState) as Omit<SerializedVis, 'data'> & {
data?: SerializedVisData;
};
if (!savedVis.data) {
savedVis.data = {
searchSource: {},
aggs: [],
};
}
return {
savedVis: savedVis as SerializedVis, // now we're sure we have "data" prop
};
});
return {
...this.types.setup(),

View file

@ -12,7 +12,6 @@ import {
EmbeddablePanel,
IEmbeddable,
isErrorEmbeddable,
reactEmbeddableRegistryHasKey,
ReactEmbeddableRenderer,
} from '@kbn/embeddable-plugin/public';
import { PresentationContainer } from '@kbn/presentation-containers';
@ -21,6 +20,7 @@ import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import React, { FC } from 'react';
import ReactDOM from 'react-dom';
import useObservable from 'react-use/lib/useObservable';
import { pluginServices } from '../../../public/services';
import { CANVAS_APP, CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib';
import { RendererStrings } from '../../../i18n';
import {
@ -121,12 +121,13 @@ export const embeddableRendererFactory = (
help: strings.getHelpDescription(),
reuseDomNode: true,
render: async (domNode, { input, embeddableType, canvasApi }, handlers) => {
const { embeddables } = pluginServices.getServices();
const uniqueId = handlers.getElementId();
const isByValueEnabled = plugins.presentationUtil.labsService.isProjectEnabled(
'labs:canvas:byValueEmbeddable'
);
if (reactEmbeddableRegistryHasKey(embeddableType)) {
if (embeddables.reactEmbeddableRegistryHasKey(embeddableType)) {
/**
* Prioritize React embeddables
*/

View file

@ -8,6 +8,7 @@
import { EmbeddableFactory, EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public';
export interface CanvasEmbeddablesService {
reactEmbeddableRegistryHasKey: (key: string) => boolean;
getEmbeddableFactories: () => IterableIterator<EmbeddableFactory>;
getStateTransfer: () => EmbeddableStateTransfer;
}

View file

@ -15,6 +15,7 @@ export type EmbeddablesServiceFactory = KibanaPluginServiceFactory<
>;
export const embeddablesServiceFactory: EmbeddablesServiceFactory = ({ startPlugins }) => ({
reactEmbeddableRegistryHasKey: startPlugins.embeddable.reactEmbeddableRegistryHasKey,
getEmbeddableFactories: startPlugins.embeddable.getEmbeddableFactories,
getStateTransfer: startPlugins.embeddable.getStateTransfer,
});

View file

@ -13,6 +13,7 @@ type EmbeddablesServiceFactory = PluginServiceFactory<CanvasEmbeddablesService>;
const noop = (..._args: any[]): any => {};
export const embeddablesServiceFactory: EmbeddablesServiceFactory = () => ({
reactEmbeddableRegistryHasKey: noop,
getEmbeddableFactories: noop,
getStateTransfer: noop,
});

View file

@ -63,7 +63,6 @@ import {
} from '@kbn/content-management-plugin/public';
import { i18n } from '@kbn/i18n';
import type { ServerlessPluginStart } from '@kbn/serverless/public';
import { registerSavedObjectToPanelMethod } from '@kbn/embeddable-plugin/public';
import { LicensingPluginStart } from '@kbn/licensing-plugin/public';
import type { EditorFrameService as EditorFrameServiceType } from './editor_frame_service';
import type {
@ -387,6 +386,21 @@ export class LensPlugin {
'lens',
new EmbeddableFactory(getStartServicesForEmbeddable)
);
embeddable.registerSavedObjectToPanelMethod<LensSavedObjectAttributes, LensByValueInput>(
CONTENT_ID,
(savedObject) => {
if (!savedObject.managed) {
return { savedObjectId: savedObject.id };
}
const panel = {
attributes: savedObjectToEmbeddableAttributes(savedObject),
};
return panel;
}
);
}
if (share) {
@ -443,21 +457,6 @@ export class LensPlugin {
() => startServices().plugins.data.nowProvider.get()
);
registerSavedObjectToPanelMethod<LensSavedObjectAttributes, LensByValueInput>(
CONTENT_ID,
(savedObject) => {
if (!savedObject.managed) {
return { savedObjectId: savedObject.id };
}
const panel = {
attributes: savedObjectToEmbeddableAttributes(savedObject),
};
return panel;
}
);
const getPresentationUtilContext = () =>
startServices().plugins.presentationUtil.ContextProvider;

View file

@ -24,11 +24,7 @@ import type { HomePublicPluginSetup } from '@kbn/home-plugin/public';
import type { VisualizationsSetup, VisualizationsStart } from '@kbn/visualizations-plugin/public';
import type { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public';
import { VISUALIZE_GEO_FIELD_TRIGGER } from '@kbn/ui-actions-plugin/public';
import {
EmbeddableSetup,
EmbeddableStart,
registerSavedObjectToPanelMethod,
} from '@kbn/embeddable-plugin/public';
import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public';
import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public';
import type { MapsEmsPluginPublicStart } from '@kbn/maps-ems-plugin/public';
@ -228,15 +224,18 @@ export class MapsPlugin
name: APP_NAME,
});
registerSavedObjectToPanelMethod<MapAttributes, MapByValueInput>(CONTENT_ID, (savedObject) => {
if (!savedObject.managed) {
return { savedObjectId: savedObject.id };
}
plugins.embeddable.registerSavedObjectToPanelMethod<MapAttributes, MapByValueInput>(
CONTENT_ID,
(savedObject) => {
if (!savedObject.managed) {
return { savedObjectId: savedObject.id };
}
return {
attributes: savedObjectToEmbeddableAttributes(savedObject),
};
});
return {
attributes: savedObjectToEmbeddableAttributes(savedObject),
};
}
);
setupLensChoroplethChart(core, plugins.expressions, plugins.lens);

View file

@ -8,10 +8,7 @@
import { coreMock } from '@kbn/core/public/mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
import {
ReactEmbeddableRenderer,
registerReactEmbeddableFactory,
} from '@kbn/embeddable-plugin/public';
import { ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public';
import { setStubKibanaServices } from '@kbn/presentation-panel-plugin/public/mocks';
import { render, waitFor, screen } from '@testing-library/react';
import React from 'react';
@ -19,6 +16,7 @@ import { of } from 'rxjs';
import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE } from '../constants';
import { getAnomalySwimLaneEmbeddableFactory } from './anomaly_swimlane_embeddable_factory';
import type { AnomalySwimLaneEmbeddableApi, AnomalySwimLaneEmbeddableState } from './types';
import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks';
// Mock dependencies
const pluginStartDeps = {
@ -84,7 +82,8 @@ describe('getAnomalySwimLaneEmbeddableFactory', () => {
const factory = getAnomalySwimLaneEmbeddableFactory(getStartServices);
beforeAll(() => {
registerReactEmbeddableFactory(ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, async () => {
const embeddable = embeddablePluginMock.createSetupContract();
embeddable.registerReactEmbeddableFactory(ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, async () => {
return factory;
});
setStubKibanaServices();

View file

@ -6,7 +6,6 @@
*/
import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public';
import { registerReactEmbeddableFactory } from '@kbn/embeddable-plugin/public';
import type { MlCoreSetup } from '../plugin';
import { AnomalyChartsEmbeddableFactory } from './anomaly_charts';
import { ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE } from './constants';
@ -17,7 +16,7 @@ export { getEmbeddableComponent } from './get_embeddable_component';
export * from './types';
export function registerEmbeddables(embeddable: EmbeddableSetup, core: MlCoreSetup) {
registerReactEmbeddableFactory(ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, async () => {
embeddable.registerReactEmbeddableFactory(ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, async () => {
const { getAnomalySwimLaneEmbeddableFactory } = await import('./anomaly_swimlane');
return getAnomalySwimLaneEmbeddableFactory(core.getStartServices);
});
@ -25,8 +24,11 @@ export function registerEmbeddables(embeddable: EmbeddableSetup, core: MlCoreSet
const anomalyChartsFactory = new AnomalyChartsEmbeddableFactory(core.getStartServices);
embeddable.registerEmbeddableFactory(anomalyChartsFactory.type, anomalyChartsFactory);
registerReactEmbeddableFactory(ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE, async () => {
const { getSingleMetricViewerEmbeddableFactory } = await import('./single_metric_viewer');
return getSingleMetricViewerEmbeddableFactory(core.getStartServices);
});
embeddable.registerReactEmbeddableFactory(
ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE,
async () => {
const { getSingleMetricViewerEmbeddableFactory } = await import('./single_metric_viewer');
return getSingleMetricViewerEmbeddableFactory(core.getStartServices);
}
);
}

View file

@ -15,7 +15,6 @@ import {
PluginInitializerContext,
} from '@kbn/core/public';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { registerReactEmbeddableFactory } from '@kbn/embeddable-plugin/public';
import { SloPublicPluginsSetup, SloPublicPluginsStart } from './types';
import { PLUGIN_NAME, sloAppId } from '../common';
import type { SloPublicSetup, SloPublicStart } from './types';
@ -104,14 +103,16 @@ export class SloPlugin
return { width: 12, height: 8 };
}
);
registerReactEmbeddableFactory(SLO_OVERVIEW_EMBEDDABLE_ID, async () => {
const deps = { ...coreStart, ...pluginsStart };
const { getOverviewEmbeddableFactory } = await import(
'./embeddable/slo/overview/slo_embeddable_factory'
);
return getOverviewEmbeddableFactory(deps);
});
pluginsSetup.embeddable.registerReactEmbeddableFactory(
SLO_OVERVIEW_EMBEDDABLE_ID,
async () => {
const deps = { ...coreStart, ...pluginsStart };
const { getOverviewEmbeddableFactory } = await import(
'./embeddable/slo/overview/slo_embeddable_factory'
);
return getOverviewEmbeddableFactory(deps);
}
);
const registerSloAlertsEmbeddableFactory = async () => {
const { SloAlertsEmbeddableFactoryDefinition } = await import(
'./embeddable/slo/alerts/slo_alerts_embeddable_factory'
@ -124,7 +125,7 @@ export class SloPlugin
};
registerSloAlertsEmbeddableFactory();
registerReactEmbeddableFactory(SLO_ERROR_BUDGET_ID, async () => {
pluginsSetup.embeddable.registerReactEmbeddableFactory(SLO_ERROR_BUDGET_ID, async () => {
const deps = { ...coreStart, ...pluginsStart };
const { getErrorBudgetEmbeddableFactory } = await import(