diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md deleted file mode 100644 index 7ba24a62a3893..0000000000000 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStart](./kibana-plugin-plugins-embeddable-public.embeddablestart.md) > [getEmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md) - -## EmbeddableStart.getEmbeddablePanel property - -Signature: - -```typescript -getEmbeddablePanel: (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC; -``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md index dafc66b1a6e15..a07021ee456e0 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md @@ -7,5 +7,5 @@ Signature: ```typescript -getStateTransfer: (history?: ScopedHistory) => EmbeddableStateTransfer; +getStateTransfer: (storage?: Storage) => EmbeddableStateTransfer; ``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md index f500196d850a2..2b04d4502e8a8 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md @@ -18,6 +18,5 @@ export interface EmbeddableStart extends PersistableStateService<A extends {
title: string;
}, V extends EmbeddableInput & {
[ATTRIBUTE_SERVICE_KEY]: A;
} = EmbeddableInput & {
[ATTRIBUTE_SERVICE_KEY]: A;
}, R extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput>(type: string, options: AttributeServiceOptions<A>) => AttributeService<A, V, R> | | | [getEmbeddableFactories](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactories.md) | () => IterableIterator<EmbeddableFactory> | | | [getEmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactory.md) | <I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>>(embeddableFactoryId: string) => EmbeddableFactory<I, O, E> | undefined | | -| [getEmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md) | (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC | | -| [getStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md) | (history?: ScopedHistory) => EmbeddableStateTransfer | | +| [getStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md) | (storage?: Storage) => EmbeddableStateTransfer | | diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md index 323ed5e38bde1..276499b435e1f 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `EmbeddableStateTransfer` class Signature: ```typescript -constructor(navigateToApp: ApplicationStart['navigateToApp'], scopedHistory?: ScopedHistory | undefined, appList?: ReadonlyMap | undefined); +constructor(navigateToApp: ApplicationStart['navigateToApp'], appList?: ReadonlyMap | undefined, customStorage?: Storage); ``` ## Parameters @@ -17,6 +17,6 @@ constructor(navigateToApp: ApplicationStart['navigateToApp'], scopedHistory?: Sc | Parameter | Type | Description | | --- | --- | --- | | navigateToApp | ApplicationStart['navigateToApp'] | | -| scopedHistory | ScopedHistory<unknown> | undefined | | | appList | ReadonlyMap<string, PublicAppInfo> | undefined | | +| customStorage | Storage | | diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.cleareditorstate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.cleareditorstate.md new file mode 100644 index 0000000000000..5c1a6a0393c2e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.cleareditorstate.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) > [clearEditorState](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.cleareditorstate.md) + +## EmbeddableStateTransfer.clearEditorState() method + +Signature: + +```typescript +clearEditorState(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md index 2a0823a9bf835..1434de2c9870e 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md @@ -4,21 +4,19 @@ ## EmbeddableStateTransfer.getIncomingEditorState() method -Fetches an [originating app](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) argument from the scoped history's location state. +Fetches an [originating app](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) argument from the sessionStorage Signature: ```typescript -getIncomingEditorState(options?: { - keysToRemoveAfterFetch?: string[]; - }): EmbeddableEditorState | undefined; +getIncomingEditorState(removeAfterFetch?: boolean): EmbeddableEditorState | undefined; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| options | {
keysToRemoveAfterFetch?: string[];
} | | +| removeAfterFetch | boolean | Whether to remove the package state after fetch to prevent duplicates. | Returns: diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md index 2069f0ce084f9..9ead71f0bb22c 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md @@ -4,21 +4,19 @@ ## EmbeddableStateTransfer.getIncomingEmbeddablePackage() method -Fetches an [embeddable package](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) argument from the scoped history's location state. +Fetches an [embeddable package](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) argument from the sessionStorage Signature: ```typescript -getIncomingEmbeddablePackage(options?: { - keysToRemoveAfterFetch?: string[]; - }): EmbeddablePackageState | undefined; +getIncomingEmbeddablePackage(removeAfterFetch?: boolean): EmbeddablePackageState | undefined; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| options | {
keysToRemoveAfterFetch?: string[];
} | | +| removeAfterFetch | boolean | Whether to remove the package state after fetch to prevent duplicates. | Returns: diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md index 2b44693e14846..3676b744b8cc9 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md @@ -4,7 +4,7 @@ ## EmbeddableStateTransfer class -A wrapper around the state object in which provides strongly typed helper methods for common incoming and outgoing states used by the embeddable infrastructure. +A wrapper around the session storage which provides strongly typed helper methods for common incoming and outgoing states used by the embeddable infrastructure. Signature: @@ -16,7 +16,7 @@ export declare class EmbeddableStateTransfer | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(navigateToApp, scopedHistory, appList)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md) | | Constructs a new instance of the EmbeddableStateTransfer class | +| [(constructor)(navigateToApp, appList, customStorage)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md) | | Constructs a new instance of the EmbeddableStateTransfer class | ## Properties @@ -28,8 +28,9 @@ export declare class EmbeddableStateTransfer | Method | Modifiers | Description | | --- | --- | --- | -| [getIncomingEditorState(options)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md) | | Fetches an [originating app](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) argument from the scoped history's location state. | -| [getIncomingEmbeddablePackage(options)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md) | | Fetches an [embeddable package](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) argument from the scoped history's location state. | +| [clearEditorState()](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.cleareditorstate.md) | | | +| [getIncomingEditorState(removeAfterFetch)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingeditorstate.md) | | Fetches an [originating app](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) argument from the sessionStorage | +| [getIncomingEmbeddablePackage(removeAfterFetch)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.getincomingembeddablepackage.md) | | Fetches an [embeddable package](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) argument from the sessionStorage | | [navigateToEditor(appId, options)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md) | | A wrapper around the method which navigates to the specified appId with [embeddable editor state](./kibana-plugin-plugins-embeddable-public.embeddableeditorstate.md) | | [navigateToWithEmbeddablePackage(appId, options)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md) | | A wrapper around the method which navigates to the specified appId with [embeddable package state](./kibana-plugin-plugins-embeddable-public.embeddablepackagestate.md) | diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md index fa24784d9aac5..4bd5f44084a33 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetoeditor.md @@ -12,7 +12,6 @@ A wrapper around the method which navigates to the specified appId with [embedd navigateToEditor(appId: string, options?: { path?: string; state: EmbeddableEditorState; - appendToExistingState?: boolean; }): Promise; ``` @@ -21,7 +20,7 @@ navigateToEditor(appId: string, options?: { | Parameter | Type | Description | | --- | --- | --- | | appId | string | | -| options | {
path?: string;
state: EmbeddableEditorState;
appendToExistingState?: boolean;
} | | +| options | {
path?: string;
state: EmbeddableEditorState;
} | | Returns: diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md index 7173bc8b127cd..0fd82167805ab 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.navigatetowithembeddablepackage.md @@ -12,7 +12,6 @@ A wrapper around the method which navigates to the specified appId with [embedd navigateToWithEmbeddablePackage(appId: string, options?: { path?: string; state: EmbeddablePackageState; - appendToExistingState?: boolean; }): Promise; ``` @@ -21,7 +20,7 @@ navigateToWithEmbeddablePackage(appId: string, options?: { | Parameter | Type | Description | | --- | --- | --- | | appId | string | | -| options | {
path?: string;
state: EmbeddablePackageState;
appendToExistingState?: boolean;
} | | +| options | {
path?: string;
state: EmbeddablePackageState;
} | | Returns: diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md index f1ea605703e59..a6aeba23cd280 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md @@ -17,7 +17,7 @@ | [EmbeddableFactoryNotFoundError](./kibana-plugin-plugins-embeddable-public.embeddablefactorynotfounderror.md) | | | [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablepanel.md) | | | [EmbeddableRoot](./kibana-plugin-plugins-embeddable-public.embeddableroot.md) | | -| [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) | A wrapper around the state object in which provides strongly typed helper methods for common incoming and outgoing states used by the embeddable infrastructure. | +| [EmbeddableStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer.md) | A wrapper around the session storage which provides strongly typed helper methods for common incoming and outgoing states used by the embeddable infrastructure. | | [ErrorEmbeddable](./kibana-plugin-plugins-embeddable-public.errorembeddable.md) | | | [PanelNotFoundError](./kibana-plugin-plugins-embeddable-public.panelnotfounderror.md) | | diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index a4a79a5d183ae..01b4e81fc484c 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -36,7 +36,6 @@ import { EmbeddableStart, EmbeddableOutput, EmbeddableFactory, - EmbeddableStateTransfer, } from '../../services/embeddable'; import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; import { createPanelState } from './panel'; @@ -111,8 +110,6 @@ const defaultCapabilities = { export class DashboardContainer extends Container { public readonly type = DASHBOARD_CONTAINER_TYPE; - - private embeddablePanel: EmbeddableStart['EmbeddablePanel']; public switchViewMode?: (newViewMode: ViewMode) => void; public getPanelCount = () => { @@ -122,7 +119,6 @@ export class DashboardContainer extends Container - + , dom diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx index 98b4947066c00..0e1ee5bf82eac 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container_factory.tsx @@ -18,7 +18,6 @@ */ import { i18n } from '@kbn/i18n'; -import { ScopedHistory } from 'src/core/public'; import { Container, ErrorEmbeddable, @@ -44,10 +43,7 @@ export class DashboardContainerFactoryDefinition public readonly isContainerType = true; public readonly type = DASHBOARD_CONTAINER_TYPE; - constructor( - private readonly getStartServices: () => Promise, - private getHistory: () => ScopedHistory - ) {} + constructor(private readonly getStartServices: () => Promise) {} public isEditable = async () => { // Currently unused for dashboards @@ -74,7 +70,6 @@ export class DashboardContainerFactoryDefinition parent?: Container ): Promise => { const services = await this.getStartServices(); - const stateTransfer = services.embeddable.getStateTransfer(this.getHistory()); - return new DashboardContainer(initialInput, services, stateTransfer, parent); + return new DashboardContainer(initialInput, services, parent); }; } diff --git a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.test.tsx b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.test.tsx index fb29ef7b3c036..ef23c636ad482 100644 --- a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.test.tsx +++ b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.test.tsx @@ -83,7 +83,6 @@ function prepare(props?: Partial) { dashboardContainer = new DashboardContainer(initialInput, options); const defaultTestProps: DashboardGridProps = { container: dashboardContainer, - PanelComponent: () =>
, kibana: null as any, intl: null as any, }; diff --git a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx index c2e8661e2ab12..c5929c5d85dbb 100644 --- a/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx +++ b/src/plugins/dashboard/public/application/embeddable/grid/dashboard_grid.tsx @@ -30,7 +30,7 @@ import React from 'react'; import { Subscription } from 'rxjs'; import ReactGridLayout, { Layout } from 'react-grid-layout'; import { GridData } from '../../../../common'; -import { ViewMode, EmbeddableChildPanel, EmbeddableStart } from '../../../services/embeddable'; +import { ViewMode, EmbeddableChildPanel } from '../../../services/embeddable'; import { DASHBOARD_GRID_COLUMN_COUNT, DASHBOARD_GRID_HEIGHT } from '../dashboard_constants'; import { DashboardPanelState } from '../types'; import { withKibana } from '../../../services/kibana_react'; @@ -115,7 +115,6 @@ const ResponsiveSizedGrid = sizeMe(config)(ResponsiveGrid); export interface DashboardGridProps extends ReactIntl.InjectedIntlProps { kibana: DashboardReactContextValue; - PanelComponent: EmbeddableStart['EmbeddablePanel']; container: DashboardContainer; } @@ -277,7 +276,7 @@ class DashboardGridUi extends React.Component { key={panel.type} embeddableId={panel.explicitInput.id} container={this.props.container} - PanelComponent={this.props.PanelComponent} + PanelComponent={this.props.kibana.services.embeddable.EmbeddablePanel} />
); diff --git a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.test.tsx b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.test.tsx index e5a1852fa61a5..925687a07bb42 100644 --- a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.test.tsx +++ b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.test.tsx @@ -92,7 +92,6 @@ function getProps( dashboardContainer = new DashboardContainer(input, options); const defaultTestProps: DashboardViewportProps = { container: dashboardContainer, - PanelComponent: () =>
, }; return { diff --git a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx index 558867ba50091..2d8b2566358a1 100644 --- a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.tsx @@ -21,7 +21,6 @@ import React from 'react'; import { Subscription } from 'rxjs'; import { PanelState, - EmbeddableStart, ViewMode, isErrorEmbeddable, openAddPanelFlyout, @@ -33,7 +32,6 @@ import { context } from '../../../services/kibana_react'; import { DashboardEmptyScreen } from '../empty_screen/dashboard_empty_screen'; export interface DashboardViewportProps { - PanelComponent: EmbeddableStart['EmbeddablePanel']; switchViewMode?: (newViewMode: ViewMode) => void; container: DashboardContainer; } @@ -131,7 +129,7 @@ export class DashboardViewport extends React.Component
)} - + ); diff --git a/src/plugins/dashboard/public/application/hooks/use_dashboard_container.ts b/src/plugins/dashboard/public/application/hooks/use_dashboard_container.ts index a331871ea7e36..319794bae8847 100644 --- a/src/plugins/dashboard/public/application/hooks/use_dashboard_container.ts +++ b/src/plugins/dashboard/public/application/hooks/use_dashboard_container.ts @@ -79,9 +79,7 @@ export const useDashboardContainer = ( searchSession.restore(searchSessionIdFromURL); } - const incomingEmbeddable = embeddable - .getStateTransfer(scopedHistory()) - .getIncomingEmbeddablePackage(); + const incomingEmbeddable = embeddable.getStateTransfer().getIncomingEmbeddablePackage(true); (async function createContainer() { const newContainer = await dashboardFactory.create( diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 97e3174fba098..4dff423098c5a 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -280,11 +280,8 @@ export class DashboardPlugin getHistory: () => this.currentHistory!, }); - const factory = new DashboardContainerFactoryDefinition( - getStartServices, - () => this.currentHistory! - ); - embeddable.registerEmbeddableFactory(factory.type, factory); + const dashboardContainerFactory = new DashboardContainerFactoryDefinition(getStartServices); + embeddable.registerEmbeddableFactory(dashboardContainerFactory.type, dashboardContainerFactory); const placeholderFactory = new PlaceholderEmbeddableFactory(); embeddable.registerEmbeddableFactory(placeholderFactory.type, placeholderFactory); diff --git a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts index 4155cb4d3b60c..cbaeddf472d52 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts @@ -17,24 +17,45 @@ * under the License. */ -import { coreMock, scopedHistoryMock } from '../../../../../core/public/mocks'; +import { coreMock } from '../../../../../core/public/mocks'; +import { Storage } from '../../../../kibana_utils/public'; import { EmbeddableStateTransfer } from '.'; import { ApplicationStart, PublicAppInfo } from '../../../../../core/public'; - -function mockHistoryState(state: unknown) { - return scopedHistoryMock.create({ state }); -} +import { EMBEDDABLE_EDITOR_STATE_KEY, EMBEDDABLE_PACKAGE_STATE_KEY } from './types'; +import { EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY } from './embeddable_state_transfer'; + +const createStorage = (): Storage => { + const createMockStore = () => { + let innerStore: Record = {}; + return { + getItem: jest.fn().mockImplementation((key) => innerStore[key]), + setItem: jest.fn().mockImplementation((key, value) => (innerStore[key] = value)), + removeItem: jest.fn().mockImplementation((key: string) => delete innerStore[key]), + clear: jest.fn().mockImplementation(() => (innerStore = {})), + }; + }; + const store = createMockStore(); + const storage = new Storage(store); + storage.get = jest.fn().mockImplementation((key) => store.getItem(key)); + storage.set = jest.fn().mockImplementation((key, value) => store.setItem(key, value)); + storage.remove = jest.fn().mockImplementation((key: string) => store.removeItem(key)); + storage.clear = jest.fn().mockImplementation(() => store.clear()); + return storage; +}; describe('embeddable state transfer', () => { let application: jest.Mocked; let stateTransfer: EmbeddableStateTransfer; + let store: Storage; + const destinationApp = 'superUltraVisualize'; const originatingApp = 'superUltraTestDashboard'; beforeEach(() => { const core = coreMock.createStart(); application = core.application; - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp); + store = createStorage(); + stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, undefined, store); }); it('cannot fetch app name when given no app list', async () => { @@ -46,7 +67,7 @@ describe('embeddable state transfer', () => { ['testId', { title: 'State Transfer Test App Hello' } as PublicAppInfo], ['testId2', { title: 'State Transfer Test App Goodbye' } as PublicAppInfo], ]); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, undefined, appsList); + stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, appsList); expect(stateTransfer.getAppNameFromId('kibanana')).toBeUndefined(); }); @@ -55,31 +76,34 @@ describe('embeddable state transfer', () => { ['testId', { title: 'State Transfer Test App Hello' } as PublicAppInfo], ['testId2', { title: 'State Transfer Test App Goodbye' } as PublicAppInfo], ]); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, undefined, appsList); + stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, appsList); expect(stateTransfer.getAppNameFromId('testId')).toBe('State Transfer Test App Hello'); expect(stateTransfer.getAppNameFromId('testId2')).toBe('State Transfer Test App Goodbye'); }); - it('can send an outgoing originating app state', async () => { + it('can send an outgoing editor state', async () => { await stateTransfer.navigateToEditor(destinationApp, { state: { originatingApp } }); + expect(store.set).toHaveBeenCalledWith(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + [EMBEDDABLE_EDITOR_STATE_KEY]: { originatingApp: 'superUltraTestDashboard' }, + }); expect(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', { - state: { originatingApp: 'superUltraTestDashboard' }, + path: undefined, }); }); - it('can send an outgoing originating app state in append mode', async () => { - const historyMock = mockHistoryState({ kibanaIsNowForSports: 'extremeSportsKibana' }); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); + it('can send an outgoing editor state and retain other embeddable state keys', async () => { + store.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + kibanaIsNowForSports: 'extremeSportsKibana', + }); await stateTransfer.navigateToEditor(destinationApp, { state: { originatingApp }, - appendToExistingState: true, + }); + expect(store.set).toHaveBeenCalledWith(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + kibanaIsNowForSports: 'extremeSportsKibana', + [EMBEDDABLE_EDITOR_STATE_KEY]: { originatingApp: 'superUltraTestDashboard' }, }); expect(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', { path: undefined, - state: { - kibanaIsNowForSports: 'extremeSportsKibana', - originatingApp: 'superUltraTestDashboard', - }, }); }); @@ -87,87 +111,81 @@ describe('embeddable state transfer', () => { await stateTransfer.navigateToWithEmbeddablePackage(destinationApp, { state: { type: 'coolestType', input: { savedObjectId: '150' } }, }); + expect(store.set).toHaveBeenCalledWith(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + [EMBEDDABLE_PACKAGE_STATE_KEY]: { type: 'coolestType', input: { savedObjectId: '150' } }, + }); expect(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', { - state: { type: 'coolestType', input: { savedObjectId: '150' } }, + path: undefined, }); }); - it('can send an outgoing embeddable package state in append mode', async () => { - const historyMock = mockHistoryState({ kibanaIsNowForSports: 'extremeSportsKibana' }); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); + it('can send an outgoing embeddable and retain other embeddable state keys', async () => { + store.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + kibanaIsNowForSports: 'extremeSportsKibana', + }); await stateTransfer.navigateToWithEmbeddablePackage(destinationApp, { state: { type: 'coolestType', input: { savedObjectId: '150' } }, - appendToExistingState: true, + }); + expect(store.set).toHaveBeenCalledWith(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + kibanaIsNowForSports: 'extremeSportsKibana', + [EMBEDDABLE_PACKAGE_STATE_KEY]: { type: 'coolestType', input: { savedObjectId: '150' } }, }); expect(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', { path: undefined, - state: { - kibanaIsNowForSports: 'extremeSportsKibana', - type: 'coolestType', - input: { savedObjectId: '150' }, - }, }); }); - it('can fetch an incoming originating app state', async () => { - const historyMock = mockHistoryState({ originatingApp: 'extremeSportsKibana' }); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); + it('can fetch an incoming editor state', async () => { + store.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + [EMBEDDABLE_EDITOR_STATE_KEY]: { originatingApp: 'superUltraTestDashboard' }, + }); const fetchedState = stateTransfer.getIncomingEditorState(); - expect(fetchedState).toEqual({ originatingApp: 'extremeSportsKibana' }); + expect(fetchedState).toEqual({ originatingApp: 'superUltraTestDashboard' }); }); - it('returns undefined with originating app state is not in the right shape', async () => { - const historyMock = mockHistoryState({ kibanaIsNowForSports: 'extremeSportsKibana' }); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); + it('incoming editor state returns undefined when state is not in the right shape', async () => { + store.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + [EMBEDDABLE_EDITOR_STATE_KEY]: { helloSportsKibana: 'superUltraTestDashboard' }, + }); const fetchedState = stateTransfer.getIncomingEditorState(); expect(fetchedState).toBeUndefined(); }); it('can fetch an incoming embeddable package state', async () => { - const historyMock = mockHistoryState({ - type: 'skisEmbeddable', - input: { savedObjectId: '123' }, + store.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + [EMBEDDABLE_PACKAGE_STATE_KEY]: { type: 'skisEmbeddable', input: { savedObjectId: '123' } }, }); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); const fetchedState = stateTransfer.getIncomingEmbeddablePackage(); expect(fetchedState).toEqual({ type: 'skisEmbeddable', input: { savedObjectId: '123' } }); }); - it('returns undefined when embeddable package is not in the right shape', async () => { - const historyMock = mockHistoryState({ kibanaIsNowForSports: 'extremeSportsKibana' }); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); + it('embeddable package state returns undefined when state is not in the right shape', async () => { + store.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + [EMBEDDABLE_PACKAGE_STATE_KEY]: { kibanaIsFor: 'sports' }, + }); const fetchedState = stateTransfer.getIncomingEmbeddablePackage(); expect(fetchedState).toBeUndefined(); }); - it('removes all keys in the keysToRemoveAfterFetch array', async () => { - const historyMock = mockHistoryState({ - type: 'skisEmbeddable', - input: { savedObjectId: '123' }, - test1: 'test1', - test2: 'test2', - }); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); - stateTransfer.getIncomingEmbeddablePackage({ keysToRemoveAfterFetch: ['type', 'input'] }); - expect(historyMock.replace).toHaveBeenCalledWith( - expect.objectContaining({ state: { test1: 'test1', test2: 'test2' } }) - ); + it('removes embeddable package key when removeAfterFetch is true', async () => { + store.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + [EMBEDDABLE_PACKAGE_STATE_KEY]: { type: 'coolestType', input: { savedObjectId: '150' } }, + iSHouldStillbeHere: 'doing the sports thing', + }); + stateTransfer.getIncomingEmbeddablePackage(true); + expect(store.get(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY)).toEqual({ + iSHouldStillbeHere: 'doing the sports thing', + }); }); - it('leaves state as is when no keysToRemove are supplied', async () => { - const historyMock = mockHistoryState({ - type: 'skisEmbeddable', - input: { savedObjectId: '123' }, - test1: 'test1', - test2: 'test2', - }); - stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); - stateTransfer.getIncomingEmbeddablePackage(); - expect(historyMock.location.state).toEqual({ - type: 'skisEmbeddable', - input: { savedObjectId: '123' }, - test1: 'test1', - test2: 'test2', + it('removes editor state key when removeAfterFetch is true', async () => { + store.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, { + [EMBEDDABLE_EDITOR_STATE_KEY]: { originatingApp: 'superCoolFootballDashboard' }, + iSHouldStillbeHere: 'doing the sports thing', + }); + stateTransfer.getIncomingEditorState(true); + expect(store.get(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY)).toEqual({ + iSHouldStillbeHere: 'doing the sports thing', }); }); }); diff --git a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts index 184178ba80e84..0b34bea810520 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.ts @@ -18,26 +18,35 @@ */ import { cloneDeep } from 'lodash'; -import { ScopedHistory, ApplicationStart, PublicAppInfo } from '../../../../../core/public'; +import { Storage } from '../../../../kibana_utils/public'; +import { ApplicationStart, PublicAppInfo } from '../../../../../core/public'; import { EmbeddableEditorState, isEmbeddableEditorState, EmbeddablePackageState, isEmbeddablePackageState, + EMBEDDABLE_PACKAGE_STATE_KEY, + EMBEDDABLE_EDITOR_STATE_KEY, } from './types'; +export const EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY = 'EMBEDDABLE_STATE_TRANSFER'; + /** - * A wrapper around the state object in {@link ScopedHistory | core scoped history} which provides - * strongly typed helper methods for common incoming and outgoing states used by the embeddable infrastructure. + * A wrapper around the session storage which provides strongly typed helper methods + * for common incoming and outgoing states used by the embeddable infrastructure. * * @public */ export class EmbeddableStateTransfer { + private storage: Storage; + constructor( private navigateToApp: ApplicationStart['navigateToApp'], - private scopedHistory?: ScopedHistory, - private appList?: ReadonlyMap | undefined - ) {} + private appList?: ReadonlyMap | undefined, + customStorage?: Storage + ) { + this.storage = customStorage ? customStorage : new Storage(sessionStorage); + } /** * Fetches an internationalized app title when given an appId. @@ -46,33 +55,43 @@ export class EmbeddableStateTransfer { public getAppNameFromId = (appId: string): string | undefined => this.appList?.get(appId)?.title; /** - * Fetches an {@link EmbeddableEditorState | originating app} argument from the scoped - * history's location state. + * Fetches an {@link EmbeddableEditorState | originating app} argument from the sessionStorage * - * @param history - the scoped history to fetch from - * @param options.keysToRemoveAfterFetch - an array of keys to be removed from the state after they are retrieved + * @param removeAfterFetch - Whether to remove the package state after fetch to prevent duplicates. */ - public getIncomingEditorState(options?: { - keysToRemoveAfterFetch?: string[]; - }): EmbeddableEditorState | undefined { - return this.getIncomingState(isEmbeddableEditorState, { - keysToRemoveAfterFetch: options?.keysToRemoveAfterFetch, - }); + public getIncomingEditorState(removeAfterFetch?: boolean): EmbeddableEditorState | undefined { + return this.getIncomingState( + isEmbeddableEditorState, + EMBEDDABLE_EDITOR_STATE_KEY, + { + keysToRemoveAfterFetch: removeAfterFetch ? [EMBEDDABLE_EDITOR_STATE_KEY] : undefined, + } + ); + } + + public clearEditorState() { + const currentState = this.storage.get(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY); + if (currentState) { + delete currentState[EMBEDDABLE_EDITOR_STATE_KEY]; + this.storage.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, currentState); + } } /** - * Fetches an {@link EmbeddablePackageState | embeddable package} argument from the scoped - * history's location state. + * Fetches an {@link EmbeddablePackageState | embeddable package} argument from the sessionStorage * - * @param history - the scoped history to fetch from - * @param options.keysToRemoveAfterFetch - an array of keys to be removed from the state after they are retrieved + * @param removeAfterFetch - Whether to remove the package state after fetch to prevent duplicates. */ - public getIncomingEmbeddablePackage(options?: { - keysToRemoveAfterFetch?: string[]; - }): EmbeddablePackageState | undefined { - return this.getIncomingState(isEmbeddablePackageState, { - keysToRemoveAfterFetch: options?.keysToRemoveAfterFetch, - }); + public getIncomingEmbeddablePackage( + removeAfterFetch?: boolean + ): EmbeddablePackageState | undefined { + return this.getIncomingState( + isEmbeddablePackageState, + EMBEDDABLE_PACKAGE_STATE_KEY, + { + keysToRemoveAfterFetch: removeAfterFetch ? [EMBEDDABLE_PACKAGE_STATE_KEY] : undefined, + } + ); } /** @@ -84,10 +103,12 @@ export class EmbeddableStateTransfer { options?: { path?: string; state: EmbeddableEditorState; - appendToExistingState?: boolean; } ): Promise { - await this.navigateToWithState(appId, options); + await this.navigateToWithState(appId, EMBEDDABLE_EDITOR_STATE_KEY, { + ...options, + appendToExistingState: true, + }); } /** @@ -96,44 +117,46 @@ export class EmbeddableStateTransfer { */ public async navigateToWithEmbeddablePackage( appId: string, - options?: { path?: string; state: EmbeddablePackageState; appendToExistingState?: boolean } + options?: { path?: string; state: EmbeddablePackageState } ): Promise { - await this.navigateToWithState(appId, options); + await this.navigateToWithState(appId, EMBEDDABLE_PACKAGE_STATE_KEY, { + ...options, + appendToExistingState: true, + }); } private getIncomingState( guard: (state: unknown) => state is IncomingStateType, + key: string, options?: { keysToRemoveAfterFetch?: string[]; } ): IncomingStateType | undefined { - if (!this.scopedHistory) { - throw new TypeError('ScopedHistory is required to fetch incoming state'); - } - const incomingState = this.scopedHistory.location?.state; + const incomingState = this.storage.get(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY)?.[key]; const castState = !guard || guard(incomingState) ? (cloneDeep(incomingState) as IncomingStateType) : undefined; if (castState && options?.keysToRemoveAfterFetch) { - const stateReplace = { ...(this.scopedHistory.location.state as { [key: string]: unknown }) }; - options.keysToRemoveAfterFetch.forEach((key: string) => { - delete stateReplace[key]; + const stateReplace = { ...this.storage.get(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY) }; + options.keysToRemoveAfterFetch.forEach((keyToRemove: string) => { + delete stateReplace[keyToRemove]; }); - this.scopedHistory.replace({ ...this.scopedHistory.location, state: stateReplace }); + this.storage.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, stateReplace); } return castState; } private async navigateToWithState( appId: string, + key: string, options?: { path?: string; state?: OutgoingStateType; appendToExistingState?: boolean } ): Promise { - const stateObject = - options?.appendToExistingState && this.scopedHistory - ? { - ...(this.scopedHistory?.location.state as { [key: string]: unknown }), - ...options.state, - } - : options?.state; - await this.navigateToApp(appId, { path: options?.path, state: stateObject }); + const stateObject = options?.appendToExistingState + ? { + ...this.storage.get(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY), + [key]: options.state, + } + : { [key]: options?.state }; + this.storage.set(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, stateObject); + await this.navigateToApp(appId, { path: options?.path }); } } diff --git a/src/plugins/embeddable/public/lib/state_transfer/types.ts b/src/plugins/embeddable/public/lib/state_transfer/types.ts index d36954528dbf0..3ce62a6acb35b 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/types.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/types.ts @@ -20,6 +20,8 @@ import { Optional } from '@kbn/utility-types'; import { EmbeddableInput, SavedObjectEmbeddableInput } from '..'; +export const EMBEDDABLE_EDITOR_STATE_KEY = 'embeddable_editor_state'; + /** * A state package that contains information an editor will need to create or edit an embeddable then redirect back. * @public @@ -34,6 +36,8 @@ export function isEmbeddableEditorState(state: unknown): state is EmbeddableEdit return ensureFieldOfTypeExists('originatingApp', state, 'string'); } +export const EMBEDDABLE_PACKAGE_STATE_KEY = 'embeddable_package_state'; + /** * A state package that contains all fields necessary to create or update an embeddable by reference or by value in a container. * @public diff --git a/src/plugins/embeddable/public/mocks.tsx b/src/plugins/embeddable/public/mocks.tsx index c5a9860498117..df24d9c0393fe 100644 --- a/src/plugins/embeddable/public/mocks.tsx +++ b/src/plugins/embeddable/public/mocks.tsx @@ -81,6 +81,7 @@ export const createEmbeddablePanelMock = ({ export const createEmbeddableStateTransferMock = (): Partial => { return { + clearEditorState: jest.fn(), getIncomingEditorState: jest.fn(), getIncomingEmbeddablePackage: jest.fn(), navigateToEditor: jest.fn(), @@ -125,7 +126,6 @@ const createStartContract = (): Start => { inject: jest.fn(), migrate: jest.fn(), EmbeddablePanel: jest.fn(), - getEmbeddablePanel: jest.fn(), getStateTransfer: jest.fn(() => createEmbeddableStateTransferMock() as EmbeddableStateTransfer), getAttributeService: jest.fn(), }; diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index 4f3de0425579c..5118a1a8818c0 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -28,7 +28,6 @@ import { CoreSetup, CoreStart, Plugin, - ScopedHistory, PublicAppInfo, } from '../../../core/public'; import { @@ -50,6 +49,7 @@ import { } from './lib'; import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition'; import { EmbeddableStateTransfer } from './lib/state_transfer'; +import { Storage } from '../../kibana_utils/public'; import { PersistableStateService, SerializableState } from '../../kibana_utils/common'; import { ATTRIBUTE_SERVICE_KEY, AttributeService } from './lib/attribute_service'; import { AttributeServiceOptions } from './lib/attribute_service/attribute_service'; @@ -95,8 +95,7 @@ export interface EmbeddableStart extends PersistableStateService EmbeddableFactory | undefined; getEmbeddableFactories: () => IterableIterator; EmbeddablePanel: EmbeddablePanelHOC; - getEmbeddablePanel: (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC; - getStateTransfer: (history?: ScopedHistory) => EmbeddableStateTransfer; + getStateTransfer: (storage?: Storage) => EmbeddableStateTransfer; getAttributeService: < A extends { title: string }, V extends EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: A } = EmbeddableInput & { @@ -119,7 +118,7 @@ export class EmbeddablePublicPlugin implements Plugin; private appListSubscription?: Subscription; @@ -160,14 +159,13 @@ export class EmbeddablePublicPlugin implements Plugin ({ + const getEmbeddablePanelHoc = () => ({ embeddable, hideHeader, }: { @@ -177,7 +175,7 @@ export class EmbeddablePublicPlugin implements Plugin { - return history - ? new EmbeddableStateTransfer(core.application.navigateToApp, history, this.appList) - : this.outgoingOnlyStateTransfer; - }, + getStateTransfer: (storage?: Storage) => + storage + ? new EmbeddableStateTransfer(core.application.navigateToApp, this.appList, storage) + : this.stateTransferService, EmbeddablePanel: getEmbeddablePanelHoc(), - getEmbeddablePanel: getEmbeddablePanelHoc, telemetry: getTelemetryFunction(commonContract), extract: getExtractFunction(commonContract), inject: getInjectFunction(commonContract), diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md index 4b7d60b4dc9ec..fc9454d7f77ed 100644 --- a/src/plugins/embeddable/public/public.api.md +++ b/src/plugins/embeddable/public/public.api.md @@ -39,7 +39,7 @@ import { I18nStart as I18nStart_2 } from 'src/core/public'; import { IconType } from '@elastic/eui'; import { ISearchOptions } from 'src/plugins/data/public'; import { ISearchSource } from 'src/plugins/data/public'; -import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { IStorageWrapper as IStorageWrapper_2 } from 'src/plugins/kibana_utils/public'; import { IUiSettingsClient as IUiSettingsClient_2 } from 'src/core/public'; import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType } from 'src/core/server/kibana_config'; @@ -604,12 +604,10 @@ export interface EmbeddableStart extends PersistableStateService IterableIterator; // (undocumented) getEmbeddableFactory: = IEmbeddable>(embeddableFactoryId: string) => EmbeddableFactory | undefined; - // (undocumented) - getEmbeddablePanel: (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC; - // Warning: (ae-forgotten-export) The symbol "ScopedHistory" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "Storage" needs to be exported by the entry point index.d.ts // // (undocumented) - getStateTransfer: (history?: ScopedHistory) => EmbeddableStateTransfer; + getStateTransfer: (storage?: Storage) => EmbeddableStateTransfer; } // Warning: (ae-missing-release-tag) "EmbeddableStartDependencies" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -630,31 +628,25 @@ export interface EmbeddableStartDependencies { uiActions: UiActionsStart; } -// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ScopedHistory" -// // @public export class EmbeddableStateTransfer { // Warning: (ae-forgotten-export) The symbol "ApplicationStart" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "PublicAppInfo" needs to be exported by the entry point index.d.ts - constructor(navigateToApp: ApplicationStart['navigateToApp'], scopedHistory?: ScopedHistory | undefined, appList?: ReadonlyMap | undefined); + constructor(navigateToApp: ApplicationStart['navigateToApp'], appList?: ReadonlyMap | undefined, customStorage?: Storage); + // (undocumented) + clearEditorState(): void; getAppNameFromId: (appId: string) => string | undefined; - getIncomingEditorState(options?: { - keysToRemoveAfterFetch?: string[]; - }): EmbeddableEditorState | undefined; - getIncomingEmbeddablePackage(options?: { - keysToRemoveAfterFetch?: string[]; - }): EmbeddablePackageState | undefined; + getIncomingEditorState(removeAfterFetch?: boolean): EmbeddableEditorState | undefined; + getIncomingEmbeddablePackage(removeAfterFetch?: boolean): EmbeddablePackageState | undefined; // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ApplicationStart" navigateToEditor(appId: string, options?: { path?: string; state: EmbeddableEditorState; - appendToExistingState?: boolean; }): Promise; // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ApplicationStart" navigateToWithEmbeddablePackage(appId: string, options?: { path?: string; state: EmbeddablePackageState; - appendToExistingState?: boolean; }): Promise; } diff --git a/src/plugins/visualize/public/application/components/visualize_byvalue_editor.tsx b/src/plugins/visualize/public/application/components/visualize_byvalue_editor.tsx index 1c1eb9956a329..e4577cb76ab06 100644 --- a/src/plugins/visualize/public/application/components/visualize_byvalue_editor.tsx +++ b/src/plugins/visualize/public/application/components/visualize_byvalue_editor.tsx @@ -45,10 +45,7 @@ export const VisualizeByValueEditor = ({ onAppLeave }: VisualizeAppProps) => { useEffect(() => { const { originatingApp: value, embeddableId: embeddableIdValue, valueInput: valueInputValue } = - services.embeddable - .getStateTransfer(services.scopedHistory) - .getIncomingEditorState({ keysToRemoveAfterFetch: ['id', 'embeddableId', 'valueInput'] }) || - {}; + services.embeddable.getStateTransfer().getIncomingEditorState() || {}; setOriginatingApp(value); setValueInput(valueInputValue); setEmbeddableId(embeddableIdValue); diff --git a/src/plugins/visualize/public/application/components/visualize_editor.tsx b/src/plugins/visualize/public/application/components/visualize_editor.tsx index 7c0fa065c3a71..b13169d4b62ec 100644 --- a/src/plugins/visualize/public/application/components/visualize_editor.tsx +++ b/src/plugins/visualize/public/application/components/visualize_editor.tsx @@ -65,9 +65,7 @@ export const VisualizeEditor = ({ onAppLeave }: VisualizeAppProps) => { useEffect(() => { const { originatingApp: value } = - services.embeddable - .getStateTransfer(services.scopedHistory) - .getIncomingEditorState({ keysToRemoveAfterFetch: ['id', 'input'] }) || {}; + services.embeddable.getStateTransfer().getIncomingEditorState() || {}; setOriginatingApp(value); }, [services]); diff --git a/src/plugins/visualize/public/application/components/visualize_listing.tsx b/src/plugins/visualize/public/application/components/visualize_listing.tsx index b2ca784162623..5720eca57e7a5 100644 --- a/src/plugins/visualize/public/application/components/visualize_listing.tsx +++ b/src/plugins/visualize/public/application/components/visualize_listing.tsx @@ -45,6 +45,7 @@ export const VisualizeListing = () => { savedVisualizations, toastNotifications, visualizations, + embeddable, savedObjects, savedObjectsPublic, savedObjectsTagging, @@ -72,6 +73,8 @@ export const VisualizeListing = () => { }, [history, pathname, visualizations]); useMount(() => { + // Reset editor state if the visualize listing page is loaded. + embeddable.getStateTransfer().clearEditorState(); chrome.setBreadcrumbs([ { text: i18n.translate('visualize.visualizeListingBreadcrumbsTitle', { diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index dbdef182d419d..6ebe65fd960b4 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -77,6 +77,7 @@ export const getTopNavConfig = ( { application, chrome, + embeddable, history, share, setActiveUrl, @@ -137,6 +138,8 @@ export const getTopNavConfig = ( } else { if (setOriginatingApp && originatingApp && newlyCreated) { setOriginatingApp(undefined); + // remove editor state so the connection is still broken after reload + stateTransfer.clearEditorState(); } chrome.docTitle.change(savedVis.lastSavedTitle); chrome.setBreadcrumbs(getEditBreadcrumbs(savedVis.lastSavedTitle)); diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 7e7156793e18b..1496b0c335322 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -37,9 +37,13 @@ import { LensByReferenceInput, } from '../editor_frame_service/embeddable/embeddable'; import { SavedObjectReference } from '../../../../../src/core/types'; -import { mockAttributeService } from '../../../../../src/plugins/embeddable/public/mocks'; +import { + mockAttributeService, + createEmbeddableStateTransferMock, +} from '../../../../../src/plugins/embeddable/public/mocks'; import { LensAttributeService } from '../lens_attribute_service'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; +import { EmbeddableStateTransfer } from '../../../../../src/plugins/embeddable/public'; jest.mock('../editor_frame_service/editor_frame/expression_helpers'); jest.mock('src/core/public'); @@ -181,6 +185,7 @@ describe('Lens App', () => { attributeService: makeAttributeService(), savedObjectsClient: core.savedObjects.client, dashboardFeatureFlag: { allowByValueEmbeddables: false }, + stateTransfer: createEmbeddableStateTransferMock() as EmbeddableStateTransfer, getOriginatingAppName: jest.fn(() => 'defaultOriginatingApp'), application: { ...core.application, diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index 931e0df2cd50e..bb77c5998519d 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -59,6 +59,7 @@ export function App({ navigation, uiSettings, application, + stateTransfer, notifications, attributeService, savedObjectsClient, @@ -463,6 +464,9 @@ export function App({ isSaveModalVisible: false, isLinkedToOriginatingApp: false, })); + // remove editor state so the connection is still broken after reload + stateTransfer.clearEditorState(); + redirectTo(newInput.savedObjectId); return; } diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index 3bc2a8956e61a..b09ecfdcd5553 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -46,7 +46,7 @@ export async function mountApp( const instance = await createEditorFrame(); const storage = new Storage(localStorage); - const stateTransfer = embeddable?.getStateTransfer(params.history); + const stateTransfer = embeddable?.getStateTransfer(); const historyLocationState = params.history.location.state as HistoryLocationState; const embeddableEditorIncomingState = stateTransfer?.getIncomingEditorState(); @@ -54,6 +54,7 @@ export async function mountApp( data, storage, navigation, + stateTransfer, savedObjectsTagging, attributeService: await attributeService(), http: coreStart.http, @@ -86,14 +87,15 @@ export async function mountApp( ); const getInitialInput = ( - routeProps: RouteComponentProps<{ id?: string }> + routeProps: RouteComponentProps<{ id?: string }>, + editByValue?: boolean ): LensEmbeddableInput | undefined => { + if (editByValue) { + return embeddableEditorIncomingState?.valueInput as LensByValueInput; + } if (routeProps.match.params.id) { return { savedObjectId: routeProps.match.params.id } as LensByReferenceInput; } - if (embeddableEditorIncomingState?.valueInput) { - return embeddableEditorIncomingState?.valueInput as LensByValueInput; - } }; const redirectTo = (routeProps: RouteComponentProps<{ id?: string }>, savedObjectId?: string) => { @@ -142,14 +144,16 @@ export async function mountApp( } }; - // const featureFlagConfig = await getByValueFeatureFlag(); - const renderEditor = (routeProps: RouteComponentProps<{ id?: string }>) => { + const renderEditor = ( + routeProps: RouteComponentProps<{ id?: string }>, + editByValue?: boolean + ) => { trackUiEvent('loaded'); return ( redirectTo(routeProps, savedObjectId)} redirectToOrigin={redirectToOrigin} redirectToDashboard={redirectToDashboard} @@ -182,7 +186,11 @@ export async function mountApp( - + renderEditor(routeProps, true)} + /> diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts index e09d7389b9d46..869ccf52fb0bd 100644 --- a/x-pack/plugins/lens/public/app_plugin/types.ts +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -33,7 +33,10 @@ import { VisualizeFieldContext, ACTION_VISUALIZE_LENS_FIELD, } from '../../../../../src/plugins/ui_actions/public'; -import { EmbeddableEditorState } from '../../../../../src/plugins/embeddable/public'; +import { + EmbeddableEditorState, + EmbeddableStateTransfer, +} from '../../../../../src/plugins/embeddable/public'; import { TableInspectorAdapter } from '../editor_frame_service/types'; import { EditorFrameInstance } from '..'; @@ -100,6 +103,7 @@ export interface LensAppServices { uiSettings: IUiSettingsClient; application: ApplicationStart; notifications: NotificationsStart; + stateTransfer: EmbeddableStateTransfer; navigation: NavigationPublicPluginStart; attributeService: LensAttributeService; savedObjectsClient: SavedObjectsStart['client']; diff --git a/x-pack/plugins/maps/public/render_app.tsx b/x-pack/plugins/maps/public/render_app.tsx index 68b74211ee273..a20649f8ebe1e 100644 --- a/x-pack/plugins/maps/public/render_app.tsx +++ b/x-pack/plugins/maps/public/render_app.tsx @@ -76,12 +76,10 @@ export async function renderApp({ setAppChrome(); function renderMapApp(routeProps: RouteComponentProps<{ savedMapId?: string }>) { - const stateTransfer = getEmbeddableService()?.getStateTransfer( - history as AppMountParameters['history'] - ); + const stateTransfer = getEmbeddableService()?.getStateTransfer(); const { embeddableId, originatingApp, valueInput } = - stateTransfer?.getIncomingEditorState({ keysToRemoveAfterFetch: ['originatingApp'] }) || {}; + stateTransfer?.getIncomingEditorState() || {}; let mapEmbeddableInput; if (routeProps.match.params.savedMapId) { diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts index 43f9e47aa8677..3930b57b32280 100644 --- a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts +++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts @@ -329,6 +329,10 @@ export class SavedMap { this._mapEmbeddableInput = updatedMapEmbeddableInput; // break connection to originating application this._originatingApp = undefined; + + // remove editor state so the connection is still broken after reload + this._getStateTransfer().clearEditorState(); + getToasts().addSuccess({ title: i18n.translate('xpack.maps.topNav.saveSuccessMessage', { defaultMessage: `Saved '{title}'`,