diff --git a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts
index ac2e8e8babee1e6..f1ede936c6ace44 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts
+++ b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts
@@ -6,12 +6,11 @@
*/
import { ExpressionTypeDefinition } from '../../../../../src/plugins/expressions';
-import { EmbeddableInput } from '../../../../../src/plugins/embeddable/common/';
+import { EmbeddableInput } from '../../types';
import { EmbeddableTypes } from './embeddable_types';
export const EmbeddableExpressionType = 'embeddable';
export { EmbeddableTypes, EmbeddableInput };
-
export interface EmbeddableExpression {
/**
* The type of the expression result
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts
index 6642a6e64fdaef3..f846f23ff7f73dc 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts
+++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts
@@ -6,14 +6,8 @@
*/
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
-import { TimeRange } from 'src/plugins/data/public';
-import { Filter } from '@kbn/es-query';
-import { ExpressionValueFilter } from '../../../types';
-import {
- EmbeddableExpressionType,
- EmbeddableExpression,
- EmbeddableInput as Input,
-} from '../../expression_types';
+import { ExpressionValueFilter, EmbeddableInput } from '../../../types';
+import { EmbeddableExpressionType, EmbeddableExpression } from '../../expression_types';
import { getFunctionHelp } from '../../../i18n';
import { SavedObjectReference } from '../../../../../../src/core/types';
import { getQueryFilters } from '../../../common/lib/build_embeddable_filters';
@@ -29,12 +23,6 @@ const defaultTimeRange = {
to: 'now',
};
-export type EmbeddableInput = Input & {
- timeRange?: TimeRange;
- filters?: Filter[];
- savedObjectId: string;
-};
-
const baseEmbeddableInput = {
timeRange: defaultTimeRange,
disableTriggers: true,
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts
index 082a69a874cae27..5dcad702bcf6978 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts
+++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts
@@ -9,9 +9,8 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
import { PaletteOutput } from 'src/plugins/charts/common';
import { Filter as DataFilter } from '@kbn/es-query';
import { TimeRange } from 'src/plugins/data/common';
-import { EmbeddableInput } from 'src/plugins/embeddable/common';
import { getQueryFilters } from '../../../common/lib/build_embeddable_filters';
-import { ExpressionValueFilter, TimeRange as TimeRangeArg } from '../../../types';
+import { ExpressionValueFilter, EmbeddableInput, TimeRange as TimeRangeArg } from '../../../types';
import {
EmbeddableTypes,
EmbeddableExpressionType,
diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx
index 2db4c78ca4b3259..953746c28084061 100644
--- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx
+++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx
@@ -13,12 +13,12 @@ import {
IEmbeddable,
EmbeddableFactory,
EmbeddableFactoryNotFoundError,
+ isErrorEmbeddable,
} from '../../../../../../src/plugins/embeddable/public';
import { EmbeddableExpression } from '../../expression_types/embeddable';
import { RendererStrings } from '../../../i18n';
import { embeddableInputToExpression } from './embeddable_input_to_expression';
-import { EmbeddableInput } from '../../expression_types';
-import { RendererFactory } from '../../../types';
+import { RendererFactory, EmbeddableInput } from '../../../types';
import { CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib';
const { embeddable: strings } = RendererStrings;
@@ -71,16 +71,27 @@ export const embeddableRendererFactory = (
throw new EmbeddableFactoryNotFoundError(embeddableType);
}
- const embeddablePromise = factory
- .createFromSavedObject(input.id, input)
- .then((embeddable) => {
- // stores embeddable in registrey
- embeddablesRegistry[uniqueId] = embeddable;
- return embeddable;
- });
- embeddablesRegistry[uniqueId] = embeddablePromise;
-
- const embeddableObject = await (async () => embeddablePromise)();
+ const embeddableInput = { ...input, id: uniqueId };
+
+ const embeddablePromise = input.savedObjectId
+ ? factory
+ .createFromSavedObject(input.savedObjectId, embeddableInput)
+ .then((embeddable) => {
+ // stores embeddable in registrey
+ embeddablesRegistry[uniqueId] = embeddable;
+ return embeddable;
+ })
+ : factory.create(embeddableInput).then((embeddable) => {
+ if (!embeddable || isErrorEmbeddable(embeddable)) {
+ return;
+ }
+ // stores embeddable in registry
+ embeddablesRegistry[uniqueId] = embeddable as IEmbeddable;
+ return embeddable;
+ });
+ embeddablesRegistry[uniqueId] = embeddablePromise as Promise;
+
+ const embeddableObject = (await (async () => embeddablePromise)()) as IEmbeddable;
const palettes = await plugins.charts.palettes.getPalettes();
diff --git a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts b/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts
index d8246449f90ba9d..e76dedfe63b14a5 100644
--- a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts
+++ b/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { EmbeddableInput } from '../../canvas_plugin_src/expression_types';
+import { EmbeddableInput } from '../../types';
export const encode = (input: Partial) =>
Buffer.from(JSON.stringify(input)).toString('base64');
diff --git a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx
index 5985c997478702d..4dc8d963932d8f4 100644
--- a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx
+++ b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx
@@ -85,9 +85,10 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({
};
// If by-value is enabled, we'll handle both by-reference and by-value embeddables
- // with the new generic `embeddable` function
+ // with the new generic `embeddable` function.
+ // Otherwise we fallback to the embeddable type specific expressions.
if (isByValueEnabled) {
- const config = encode({ id });
+ const config = encode({ savedObjectId: id });
partialElement.expression = `embeddable config="${config}"
type="${type}"
| render`;
diff --git a/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx b/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx
index 50d527036560adf..ffd5b095b12e522 100644
--- a/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx
+++ b/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx
@@ -6,3 +6,5 @@
*/
export { useDownloadWorkpad, useDownloadRenderedWorkpad } from './use_download_workpad';
+
+export { useIncomingEmbeddable } from './use_incoming_embeddable';
diff --git a/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts b/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts
new file mode 100644
index 000000000000000..27f68ca15a20002
--- /dev/null
+++ b/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts
@@ -0,0 +1,63 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useEffect } from 'react';
+import { useDispatch } from 'react-redux';
+import { CANVAS_APP } from '../../../../common/lib';
+import { encode } from '../../../../common/lib/embeddable_dataurl';
+import { useEmbeddablesService, useLabsService } from '../../../services';
+// @ts-expect-error unconverted file
+import { addElement } from '../../../state/actions/elements';
+// @ts-expect-error unconverted file
+import { selectToplevelNodes } from '../../../state/actions/transient';
+
+import {
+ updateEmbeddableExpression,
+ fetchEmbeddableRenderable,
+} from '../../../state/actions/embeddable';
+import { clearValue } from '../../../state/actions/resolved_args';
+
+export const useIncomingEmbeddable = (pageId: string) => {
+ const embeddablesService = useEmbeddablesService();
+ const labsService = useLabsService();
+ const dispatch = useDispatch();
+ const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable');
+ const stateTransferService = embeddablesService.getStateTransfer();
+
+ // fetch incoming embeddable from state transfer service.
+ const incomingEmbeddable = stateTransferService.getIncomingEmbeddablePackage(CANVAS_APP, true);
+
+ useEffect(() => {
+ if (isByValueEnabled && incomingEmbeddable) {
+ const { embeddableId, input, type } = incomingEmbeddable;
+
+ const config = encode(input);
+ const expression = `embeddable config="${config}"
+ type="${type}"
+| render`;
+
+ if (embeddableId) {
+ // clear out resolved arg for old embeddable
+ const argumentPath = [embeddableId, 'expressionRenderable'];
+ dispatch(clearValue({ path: argumentPath }));
+
+ // update existing embeddable expression
+ dispatch(
+ updateEmbeddableExpression({ elementId: embeddableId, embeddableExpression: expression })
+ );
+
+ // update resolved args
+ dispatch(fetchEmbeddableRenderable(embeddableId));
+
+ // select new embeddable element
+ dispatch(selectToplevelNodes([embeddableId]));
+ } else {
+ dispatch(addElement(pageId, { expression }));
+ }
+ }
+ }, [dispatch, pageId, incomingEmbeddable, isByValueEnabled]);
+};
diff --git a/x-pack/plugins/canvas/public/components/workpad/workpad.tsx b/x-pack/plugins/canvas/public/components/workpad/workpad.tsx
index 622c885b6ef281d..bc867333b648f93 100644
--- a/x-pack/plugins/canvas/public/components/workpad/workpad.tsx
+++ b/x-pack/plugins/canvas/public/components/workpad/workpad.tsx
@@ -27,6 +27,7 @@ import { WorkpadRoutingContext } from '../../routes/workpad';
import { usePlatformService } from '../../services';
import { Workpad as WorkpadComponent, Props } from './workpad.component';
import { State } from '../../../types';
+import { useIncomingEmbeddable } from '../hooks';
type ContainerProps = Pick;
@@ -58,6 +59,9 @@ export const Workpad: FC = (props) => {
};
});
+ const pageId = propsFromState.pages[propsFromState.selectedPageNumber - 1].id;
+ useIncomingEmbeddable(pageId);
+
const fetchAllRenderables = useCallback(() => {
dispatch(fetchAllRenderablesAction());
}, [dispatch]);
diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/__stories__/element_menu.stories.tsx b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/__stories__/element_menu.stories.tsx
index 80280d55a4e1c8f..390d4a75ea0ae29 100644
--- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/__stories__/element_menu.stories.tsx
+++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/__stories__/element_menu.stories.tsx
@@ -136,5 +136,6 @@ storiesOf('components/WorkpadHeader/ElementMenu', module).add('default', () => (
elements={testElements}
addElement={action('addElement')}
renderEmbedPanel={mockRenderEmbedPanel}
+ createNewEmbeddable={action('createNewEmbeddable')}
/>
));
diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx
index 937912570b77fbf..a8713f380d6a41c 100644
--- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx
+++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx
@@ -22,6 +22,7 @@ import { ElementSpec } from '../../../../types';
import { flattenPanelTree } from '../../../lib/flatten_panel_tree';
import { AssetManager } from '../../asset_manager';
import { SavedElementsModal } from '../../saved_elements_modal';
+import { useLabsService } from '../../../services';
interface CategorizedElementLists {
[key: string]: ElementSpec[];
@@ -129,13 +130,20 @@ export interface Props {
* Renders embeddable flyout
*/
renderEmbedPanel: (onClose: () => void) => JSX.Element;
+ /**
+ * Crete new embeddable
+ */
+ createNewEmbeddable: () => void;
}
export const ElementMenu: FunctionComponent = ({
elements,
addElement,
renderEmbedPanel,
+ createNewEmbeddable,
}) => {
+ const labsService = useLabsService();
+ const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable');
const [isAssetModalVisible, setAssetModalVisible] = useState(false);
const [isEmbedPanelVisible, setEmbedPanelVisible] = useState(false);
const [isSavedElementsModalVisible, setSavedElementsModalVisible] = useState(false);
@@ -223,6 +231,18 @@ export const ElementMenu: FunctionComponent = ({
closePopover();
},
},
+ // TODO: Remove this menu option. This is a temporary menu options just for testing,
+ // will be removed once toolbar is implemented
+ isByValueEnabled
+ ? {
+ name: 'Lens',
+ icon: ,
+ onClick: () => {
+ createNewEmbeddable();
+ closePopover();
+ },
+ }
+ : {},
],
};
};
diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx
index 5b5491a7c6454a0..8b891a625101460 100644
--- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx
+++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx
@@ -5,10 +5,14 @@
* 2.0.
*/
-import React from 'react';
+import React, { useCallback } from 'react';
import { connect } from 'react-redux';
+import { useLocation } from 'react-router-dom';
+
import { compose, withProps } from 'recompose';
import { Dispatch } from 'redux';
+import { trackCanvasUiMetric, METRIC_TYPE } from '../../../../public/lib/ui_metric';
+import { CANVAS_APP } from '../../../../common/lib';
import { State, ElementSpec } from '../../../../types';
// @ts-expect-error untyped local
import { elementsRegistry } from '../../../lib/elements_registry';
@@ -17,6 +21,7 @@ import { ElementMenu as Component, Props as ComponentProps } from './element_men
import { addElement } from '../../../state/actions/elements';
import { getSelectedPage } from '../../../state/selectors/workpad';
import { AddEmbeddablePanel } from '../../embeddable_flyout';
+import { useEmbeddablesService } from '../../../services';
interface StateProps {
pageId: string;
@@ -42,7 +47,32 @@ const mergeProps = (stateProps: StateProps, dispatchProps: DispatchProps) => ({
renderEmbedPanel: (onClose: () => void) => ,
});
+const ElementMenuComponent = (props: ComponentProps) => {
+ const embeddablesService = useEmbeddablesService();
+ const stateTransferService = embeddablesService.getStateTransfer();
+ const { pathname, search } = useLocation();
+
+ const createNewEmbeddable = useCallback(() => {
+ const path = '#/';
+ const appId = 'lens';
+
+ if (trackCanvasUiMetric) {
+ trackCanvasUiMetric(METRIC_TYPE.CLICK, `${appId}:create`);
+ }
+
+ stateTransferService.navigateToEditor(appId, {
+ path,
+ state: {
+ originatingApp: CANVAS_APP,
+ originatingPath: `#/${pathname}${search}`,
+ },
+ });
+ }, [pathname, search, stateTransferService]);
+
+ return ;
+};
+
export const ElementMenu = compose(
connect(mapStateToProps, mapDispatchToProps, mergeProps),
withProps(() => ({ elements: elementsRegistry.toJS() }))
-)(Component);
+)(ElementMenuComponent);
diff --git a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts
index f8ddd769aac43d6..a0076970fbcf798 100644
--- a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts
+++ b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts
@@ -46,9 +46,11 @@ export const useWorkpad = (
workpad.aliasId = aliasId;
}
- dispatch(setAssets(assets));
- dispatch(setWorkpad(workpad, { loadPages }));
- dispatch(setZoomScale(1));
+ if (storedWorkpad.id !== workpadId || storedWorkpad.aliasId !== aliasId) {
+ dispatch(setAssets(assets));
+ dispatch(setWorkpad(workpad, { loadPages }));
+ dispatch(setZoomScale(1));
+ }
if (outcome === 'aliasMatch' && platformService.redirectLegacyUrl && aliasId) {
platformService.redirectLegacyUrl(`#${getRedirectPath(aliasId)}`, getWorkpadLabel());
@@ -57,7 +59,17 @@ export const useWorkpad = (
setError(e as Error | string);
}
})();
- }, [workpadId, dispatch, setError, loadPages, workpadService, getRedirectPath, platformService]);
+ }, [
+ workpadId,
+ dispatch,
+ setError,
+ loadPages,
+ workpadService,
+ getRedirectPath,
+ platformService,
+ storedWorkpad.id,
+ storedWorkpad.aliasId,
+ ]);
return [storedWorkpad.id === workpadId ? storedWorkpad : undefined, error];
};
diff --git a/x-pack/plugins/canvas/public/services/embeddables.ts b/x-pack/plugins/canvas/public/services/embeddables.ts
index 24d7a57e086f2db..26b150b7a534936 100644
--- a/x-pack/plugins/canvas/public/services/embeddables.ts
+++ b/x-pack/plugins/canvas/public/services/embeddables.ts
@@ -5,8 +5,12 @@
* 2.0.
*/
-import { EmbeddableFactory } from '../../../../../src/plugins/embeddable/public';
+import {
+ EmbeddableFactory,
+ EmbeddableStateTransfer,
+} from '../../../../../src/plugins/embeddable/public';
export interface CanvasEmbeddablesService {
getEmbeddableFactories: () => IterableIterator;
+ getStateTransfer: () => EmbeddableStateTransfer;
}
diff --git a/x-pack/plugins/canvas/public/services/kibana/embeddables.ts b/x-pack/plugins/canvas/public/services/kibana/embeddables.ts
index 054b9da7409fbbc..8d1a86edab3d890 100644
--- a/x-pack/plugins/canvas/public/services/kibana/embeddables.ts
+++ b/x-pack/plugins/canvas/public/services/kibana/embeddables.ts
@@ -16,4 +16,5 @@ export type EmbeddablesServiceFactory = KibanaPluginServiceFactory<
export const embeddablesServiceFactory: EmbeddablesServiceFactory = ({ startPlugins }) => ({
getEmbeddableFactories: startPlugins.embeddable.getEmbeddableFactories,
+ getStateTransfer: startPlugins.embeddable.getStateTransfer,
});
diff --git a/x-pack/plugins/canvas/public/services/stubs/embeddables.ts b/x-pack/plugins/canvas/public/services/stubs/embeddables.ts
index 173d27563e2b2a3..9c2cf4d0650abef 100644
--- a/x-pack/plugins/canvas/public/services/stubs/embeddables.ts
+++ b/x-pack/plugins/canvas/public/services/stubs/embeddables.ts
@@ -14,4 +14,5 @@ const noop = (..._args: any[]): any => {};
export const embeddablesServiceFactory: EmbeddablesServiceFactory = () => ({
getEmbeddableFactories: noop,
+ getStateTransfer: noop,
});
diff --git a/x-pack/plugins/canvas/types/embeddables.ts b/x-pack/plugins/canvas/types/embeddables.ts
new file mode 100644
index 000000000000000..b78efece59d8f88
--- /dev/null
+++ b/x-pack/plugins/canvas/types/embeddables.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
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { TimeRange } from 'src/plugins/data/public';
+import { Filter } from '@kbn/es-query';
+import { EmbeddableInput as Input } from '../../../../src/plugins/embeddable/common/';
+
+export type EmbeddableInput = Input & {
+ timeRange?: TimeRange;
+ filters?: Filter[];
+ savedObjectId?: string;
+};
diff --git a/x-pack/plugins/canvas/types/index.ts b/x-pack/plugins/canvas/types/index.ts
index 09ae1510be6da0e..930f33729208842 100644
--- a/x-pack/plugins/canvas/types/index.ts
+++ b/x-pack/plugins/canvas/types/index.ts
@@ -9,6 +9,7 @@ export * from '../../../../src/plugins/expressions/common';
export * from './assets';
export * from './canvas';
export * from './elements';
+export * from './embeddables';
export * from './filters';
export * from './functions';
export * from './renderers';