Skip to content

Commit

Permalink
Move away from npStart for embeddables in canvas (#62680) (#63705)
Browse files Browse the repository at this point in the history
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
Corey Robertson and elasticmachine authored Apr 23, 2020
1 parent 07c7ee2 commit 60bc29b
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 90 deletions.
28 changes: 20 additions & 8 deletions x-pack/legacy/plugins/canvas/canvas_plugin_src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@

import { CoreSetup, CoreStart, Plugin } from 'src/core/public';
import { CanvasSetup } from '../public';
import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public';
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import { Start as InspectorStart } from '../../../../../src/plugins/inspector/public';

import { functions } from './functions/browser';
import { typeFunctions } from './expression_types';
// @ts-ignore: untyped local
import { renderFunctions } from './renderers';
import { renderFunctions, renderFunctionFactories } from './renderers';

import { elementSpecs } from './elements';
// @ts-ignore Untyped Local
Expand All @@ -30,13 +33,26 @@ interface SetupDeps {
canvas: CanvasSetup;
}

export interface StartDeps {
embeddable: EmbeddableStart;
uiActions: UiActionsStart;
inspector: InspectorStart;
}

/** @internal */
export class CanvasSrcPlugin implements Plugin<{}, {}, SetupDeps, {}> {
public setup(core: CoreSetup, plugins: SetupDeps) {
export class CanvasSrcPlugin implements Plugin<void, void, SetupDeps, StartDeps> {
public setup(core: CoreSetup<StartDeps>, plugins: SetupDeps) {
plugins.canvas.addFunctions(functions);
plugins.canvas.addTypes(typeFunctions);

plugins.canvas.addRenderers(renderFunctions);

core.getStartServices().then(([coreStart, depsStart]) => {
plugins.canvas.addRenderers(
renderFunctionFactories.map((factory: any) => factory(coreStart, depsStart))
);
});

plugins.canvas.addElements(elementSpecs);
plugins.canvas.addDatasourceUIs(datasourceSpecs);
plugins.canvas.addModelUIs(modelSpecs);
Expand All @@ -45,11 +61,7 @@ export class CanvasSrcPlugin implements Plugin<{}, {}, SetupDeps, {}> {
plugins.canvas.addTagUIs(tagSpecs);
plugins.canvas.addTemplates(templateSpecs);
plugins.canvas.addTransformUIs(transformSpecs);

return {};
}

public start(core: CoreStart, plugins: {}) {
return {};
}
public start(core: CoreStart, plugins: StartDeps) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { I18nContext } from 'ui/i18n';
import { npStart } from 'ui/new_platform';
import { CoreStart } from '../../../../../../../src/core/public';
import { StartDeps } from '../../plugin';
import {
IEmbeddable,
EmbeddableFactory,
Expand All @@ -28,86 +29,88 @@ const embeddablesRegistry: {
[key: string]: IEmbeddable;
} = {};

const renderEmbeddable = (embeddableObject: IEmbeddable, domNode: HTMLElement) => {
return (
<div
className={CANVAS_EMBEDDABLE_CLASSNAME}
style={{ width: domNode.offsetWidth, height: domNode.offsetHeight, cursor: 'auto' }}
>
<I18nContext>
<EmbeddablePanel
embeddable={embeddableObject}
getActions={npStart.plugins.uiActions.getTriggerCompatibleActions}
getEmbeddableFactory={npStart.plugins.embeddable.getEmbeddableFactory}
getAllEmbeddableFactories={npStart.plugins.embeddable.getEmbeddableFactories}
notifications={npStart.core.notifications}
overlays={npStart.core.overlays}
inspector={npStart.plugins.inspector}
SavedObjectFinder={getSavedObjectFinder(
npStart.core.savedObjects,
npStart.core.uiSettings
)}
/>
</I18nContext>
</div>
);
const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => {
return (embeddableObject: IEmbeddable, domNode: HTMLElement) => {
return (
<div
className={CANVAS_EMBEDDABLE_CLASSNAME}
style={{ width: domNode.offsetWidth, height: domNode.offsetHeight, cursor: 'auto' }}
>
<I18nContext>
<EmbeddablePanel
embeddable={embeddableObject}
getActions={plugins.uiActions.getTriggerCompatibleActions}
getEmbeddableFactory={plugins.embeddable.getEmbeddableFactory}
getAllEmbeddableFactories={plugins.embeddable.getEmbeddableFactories}
notifications={core.notifications}
overlays={core.overlays}
inspector={plugins.inspector}
SavedObjectFinder={getSavedObjectFinder(core.savedObjects, core.uiSettings)}
/>
</I18nContext>
</div>
);
};
};

const embeddable = () => ({
name: 'embeddable',
displayName: strings.getDisplayName(),
help: strings.getHelpDescription(),
reuseDomNode: true,
render: async (
domNode: HTMLElement,
{ input, embeddableType }: EmbeddableExpression<EmbeddableInput>,
handlers: RendererHandlers
) => {
const uniqueId = handlers.getElementId();

if (!embeddablesRegistry[uniqueId]) {
const factory = Array.from(npStart.plugins.embeddable.getEmbeddableFactories()).find(
embeddableFactory => embeddableFactory.type === embeddableType
) as EmbeddableFactory<EmbeddableInput>;

if (!factory) {
handlers.done();
throw new EmbeddableFactoryNotFoundError(embeddableType);
}

const embeddableObject = await factory.createFromSavedObject(input.id, input);
export const embeddableRendererFactory = (core: CoreStart, plugins: StartDeps) => {
const renderEmbeddable = renderEmbeddableFactory(core, plugins);
return () => ({
name: 'embeddable',
displayName: strings.getDisplayName(),
help: strings.getHelpDescription(),
reuseDomNode: true,
render: async (
domNode: HTMLElement,
{ input, embeddableType }: EmbeddableExpression<EmbeddableInput>,
handlers: RendererHandlers
) => {
const uniqueId = handlers.getElementId();

if (!embeddablesRegistry[uniqueId]) {
const factory = Array.from(plugins.embeddable.getEmbeddableFactories()).find(
embeddableFactory => embeddableFactory.type === embeddableType
) as EmbeddableFactory<EmbeddableInput>;

if (!factory) {
handlers.done();
throw new EmbeddableFactoryNotFoundError(embeddableType);
}

embeddablesRegistry[uniqueId] = embeddableObject;
ReactDOM.unmountComponentAtNode(domNode);
const embeddableObject = await factory.createFromSavedObject(input.id, input);

const subscription = embeddableObject.getInput$().subscribe(function(updatedInput) {
const updatedExpression = embeddableInputToExpression(updatedInput, embeddableType);
embeddablesRegistry[uniqueId] = embeddableObject;
ReactDOM.unmountComponentAtNode(domNode);

if (updatedExpression) {
handlers.onEmbeddableInputChange(updatedExpression);
}
});
const subscription = embeddableObject.getInput$().subscribe(function(updatedInput) {
const updatedExpression = embeddableInputToExpression(updatedInput, embeddableType);

ReactDOM.render(renderEmbeddable(embeddableObject, domNode), domNode, () => handlers.done());
if (updatedExpression) {
handlers.onEmbeddableInputChange(updatedExpression);
}
});

handlers.onResize(() => {
ReactDOM.render(renderEmbeddable(embeddableObject, domNode), domNode, () =>
handlers.done()
);
});

handlers.onDestroy(() => {
subscription.unsubscribe();
handlers.onEmbeddableDestroyed();
handlers.onResize(() => {
ReactDOM.render(renderEmbeddable(embeddableObject, domNode), domNode, () =>
handlers.done()
);
});

delete embeddablesRegistry[uniqueId];
handlers.onDestroy(() => {
subscription.unsubscribe();
handlers.onEmbeddableDestroyed();

return ReactDOM.unmountComponentAtNode(domNode);
});
} else {
embeddablesRegistry[uniqueId].updateInput(input);
}
},
});
delete embeddablesRegistry[uniqueId];

export { embeddable };
return ReactDOM.unmountComponentAtNode(domNode);
});
} else {
embeddablesRegistry[uniqueId].updateInput(input);
}
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { advancedFilter } from './advanced_filter';
import { debug } from './debug';
import { dropdownFilter } from './dropdown_filter';
import { embeddable } from './embeddable/embeddable';
import { embeddableRendererFactory } from './embeddable/embeddable';
import { error } from './error';
import { image } from './image';
import { markdown } from './markdown';
Expand All @@ -26,7 +26,6 @@ export const renderFunctions = [
advancedFilter,
debug,
dropdownFilter,
embeddable,
error,
image,
markdown,
Expand All @@ -41,3 +40,5 @@ export const renderFunctions = [
text,
timeFilter,
];

export const renderFunctionFactories = [embeddableRendererFactory];
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
*/

import React from 'react';
import { npStart } from 'ui/new_platform';
import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody, EuiTitle } from '@elastic/eui';
import {
SavedObjectFinderUi,
SavedObjectMetaData,
} from '../../../../../../../src/plugins/saved_objects/public/';
import { ComponentStrings } from '../../../i18n';
import { CoreStart } from '../../../../../../../src/core/public';
import { CanvasStartDeps } from '../../plugin';

const { AddEmbeddableFlyout: strings } = ComponentStrings;

Expand All @@ -22,11 +22,12 @@ export interface Props {
availableEmbeddables: string[];
savedObjects: CoreStart['savedObjects'];
uiSettings: CoreStart['uiSettings'];
getEmbeddableFactories: CanvasStartDeps['embeddable']['getEmbeddableFactories'];
}

export class AddEmbeddableFlyout extends React.Component<Props> {
onAddPanel = (id: string, savedObjectType: string, name: string) => {
const embeddableFactories = npStart.plugins.embeddable.getEmbeddableFactories();
const embeddableFactories = this.props.getEmbeddableFactories();

// Find the embeddable type from the saved object type
const found = Array.from(embeddableFactories).find(embeddableFactory => {
Expand All @@ -42,7 +43,7 @@ export class AddEmbeddableFlyout extends React.Component<Props> {
};

render() {
const embeddableFactories = npStart.plugins.embeddable.getEmbeddableFactories();
const embeddableFactories = this.props.getEmbeddableFactories();

const availableSavedObjects = Array.from(embeddableFactories)
.filter(factory => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export class EmbeddableFlyoutPortal extends React.Component<Props & WithKibanaPr
availableEmbeddables={Object.keys(allowedEmbeddables)}
savedObjects={this.props.kibana.services.savedObjects}
uiSettings={this.props.kibana.services.uiSettings}
getEmbeddableFactories={this.props.kibana.services.embeddable.getEmbeddableFactories}
/>,
this.el
);
Expand Down
2 changes: 2 additions & 0 deletions x-pack/legacy/plugins/canvas/public/legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ const shimSetupPlugins: CanvasSetupDeps = {
};
const shimStartPlugins: CanvasStartDeps = {
...npStart.plugins,
embeddable: npStart.plugins.embeddable,
expressions: npStart.plugins.expressions,
inspector: npStart.plugins.inspector,
uiActions: npStart.plugins.uiActions,
__LEGACY: {
// ToDo: Copy directly into canvas
Expand Down
18 changes: 11 additions & 7 deletions x-pack/legacy/plugins/canvas/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { initLoadingIndicator } from './lib/loading_indicator';
import { featureCatalogueEntry } from './feature_catalogue_entry';
import { ExpressionsSetup, ExpressionsStart } from '../../../../../src/plugins/expressions/public';
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public';
import { Start as InspectorStart } from '../../../../../src/plugins/inspector/public';
// @ts-ignore untyped local
import { argTypeSpecs } from './expression_types/arg_types';
import { transitions } from './transitions';
Expand All @@ -31,7 +33,9 @@ export interface CanvasSetupDeps {
}

export interface CanvasStartDeps {
embeddable: EmbeddableStart;
expressions: ExpressionsStart;
inspector: InspectorStart;
uiActions: UiActionsStart;
__LEGACY: {
absoluteToParsedUrl: (url: string, basePath: string) => any;
Expand All @@ -48,14 +52,19 @@ export interface CanvasStartDeps {
// These interfaces are empty for now but will be populate as we need to export
// things for other plugins to use at startup or runtime
export type CanvasSetup = CanvasApi;
export interface CanvasStart {} // eslint-disable-line @typescript-eslint/no-empty-interface
export type CanvasStart = void;

/** @internal */
export class CanvasPlugin
implements Plugin<CanvasSetup, CanvasStart, CanvasSetupDeps, CanvasStartDeps> {
// TODO: Do we want to completely move canvas_plugin_src into it's own plugin?
private srcPlugin = new CanvasSrcPlugin();

public setup(core: CoreSetup<CanvasStartDeps>, plugins: CanvasSetupDeps) {
const { api: canvasApi, registries } = getPluginApi(plugins.expressions);

this.srcPlugin.setup(core, { canvas: canvasApi });

core.application.register({
id: 'canvas',
title: 'Canvas App',
Expand Down Expand Up @@ -84,10 +93,6 @@ export class CanvasPlugin
canvasApi.addElements(legacyRegistries.elements.getOriginalFns());
canvasApi.addTypes(legacyRegistries.types.getOriginalFns());

// TODO: Do we want to completely move canvas_plugin_src into it's own plugin?
const srcPlugin = new CanvasSrcPlugin();
srcPlugin.setup(core, { canvas: canvasApi });

// Register core canvas stuff
canvasApi.addFunctions(initFunctions({ typesRegistry: plugins.expressions.__LEGACY.types }));
canvasApi.addArgumentUIs(argTypeSpecs);
Expand All @@ -99,8 +104,7 @@ export class CanvasPlugin
}

public start(core: CoreStart, plugins: CanvasStartDeps) {
this.srcPlugin.start(core, plugins);
initLoadingIndicator(core.http.addLoadingCountSource);

return {};
}
}

0 comments on commit 60bc29b

Please sign in to comment.