* Transitioned embeddable state transfer service to use sessionStorage
This commit is contained in:
parent
6ae2d0a542
commit
815793057c
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[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
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getEmbeddablePanel: (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC;
|
||||
```
|
|
@ -7,5 +7,5 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getStateTransfer: (history?: ScopedHistory) => EmbeddableStateTransfer;
|
||||
getStateTransfer: (storage?: Storage) => EmbeddableStateTransfer;
|
||||
```
|
||||
|
|
|
@ -18,6 +18,5 @@ export interface EmbeddableStart extends PersistableStateService<EmbeddableState
|
|||
| [getAttributeService](./kibana-plugin-plugins-embeddable-public.embeddablestart.getattributeservice.md) | <code><A extends {</code><br/><code> title: string;</code><br/><code> }, V extends EmbeddableInput & {</code><br/><code> [ATTRIBUTE_SERVICE_KEY]: A;</code><br/><code> } = EmbeddableInput & {</code><br/><code> [ATTRIBUTE_SERVICE_KEY]: A;</code><br/><code> }, R extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput>(type: string, options: AttributeServiceOptions<A>) => AttributeService<A, V, R></code> | |
|
||||
| [getEmbeddableFactories](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactories.md) | <code>() => IterableIterator<EmbeddableFactory></code> | |
|
||||
| [getEmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactory.md) | <code><I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>>(embeddableFactoryId: string) => EmbeddableFactory<I, O, E> | undefined</code> | |
|
||||
| [getEmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md) | <code>(stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC</code> | |
|
||||
| [getStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md) | <code>(history?: ScopedHistory) => EmbeddableStateTransfer</code> | |
|
||||
| [getStateTransfer](./kibana-plugin-plugins-embeddable-public.embeddablestart.getstatetransfer.md) | <code>(storage?: Storage) => EmbeddableStateTransfer</code> | |
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ Constructs a new instance of the `EmbeddableStateTransfer` class
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
constructor(navigateToApp: ApplicationStart['navigateToApp'], scopedHistory?: ScopedHistory<unknown> | undefined, appList?: ReadonlyMap<string, PublicAppInfo> | undefined);
|
||||
constructor(navigateToApp: ApplicationStart['navigateToApp'], appList?: ReadonlyMap<string, PublicAppInfo> | undefined, customStorage?: Storage);
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
@ -17,6 +17,6 @@ constructor(navigateToApp: ApplicationStart['navigateToApp'], scopedHistory?: Sc
|
|||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| navigateToApp | <code>ApplicationStart['navigateToApp']</code> | |
|
||||
| scopedHistory | <code>ScopedHistory<unknown> | undefined</code> | |
|
||||
| appList | <code>ReadonlyMap<string, PublicAppInfo> | undefined</code> | |
|
||||
| customStorage | <code>Storage</code> | |
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[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
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
clearEditorState(): void;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
`void`
|
||||
|
|
@ -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
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getIncomingEditorState(options?: {
|
||||
keysToRemoveAfterFetch?: string[];
|
||||
}): EmbeddableEditorState | undefined;
|
||||
getIncomingEditorState(removeAfterFetch?: boolean): EmbeddableEditorState | undefined;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| options | <code>{</code><br/><code> keysToRemoveAfterFetch?: string[];</code><br/><code> }</code> | |
|
||||
| removeAfterFetch | <code>boolean</code> | Whether to remove the package state after fetch to prevent duplicates. |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getIncomingEmbeddablePackage(options?: {
|
||||
keysToRemoveAfterFetch?: string[];
|
||||
}): EmbeddablePackageState | undefined;
|
||||
getIncomingEmbeddablePackage(removeAfterFetch?: boolean): EmbeddablePackageState | undefined;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| options | <code>{</code><br/><code> keysToRemoveAfterFetch?: string[];</code><br/><code> }</code> | |
|
||||
| removeAfterFetch | <code>boolean</code> | Whether to remove the package state after fetch to prevent duplicates. |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
|
@ -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 <code>EmbeddableStateTransfer</code> class |
|
||||
| [(constructor)(navigateToApp, appList, customStorage)](./kibana-plugin-plugins-embeddable-public.embeddablestatetransfer._constructor_.md) | | Constructs a new instance of the <code>EmbeddableStateTransfer</code> 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) |
|
||||
|
||||
|
|
|
@ -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<void>;
|
||||
```
|
||||
|
||||
|
@ -21,7 +20,7 @@ navigateToEditor(appId: string, options?: {
|
|||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| appId | <code>string</code> | |
|
||||
| options | <code>{</code><br/><code> path?: string;</code><br/><code> state: EmbeddableEditorState;</code><br/><code> appendToExistingState?: boolean;</code><br/><code> }</code> | |
|
||||
| options | <code>{</code><br/><code> path?: string;</code><br/><code> state: EmbeddableEditorState;</code><br/><code> }</code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
|
|
|
@ -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<void>;
|
||||
```
|
||||
|
||||
|
@ -21,7 +20,7 @@ navigateToWithEmbeddablePackage(appId: string, options?: {
|
|||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| appId | <code>string</code> | |
|
||||
| options | <code>{</code><br/><code> path?: string;</code><br/><code> state: EmbeddablePackageState;</code><br/><code> appendToExistingState?: boolean;</code><br/><code> }</code> | |
|
||||
| options | <code>{</code><br/><code> path?: string;</code><br/><code> state: EmbeddablePackageState;</code><br/><code> }</code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
|
|
|
@ -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) | |
|
||||
|
||||
|
|
|
@ -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<InheritedChildInput, DashboardContainerInput> {
|
||||
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<InheritedChildInput, Dashboard
|
|||
constructor(
|
||||
initialInput: DashboardContainerInput,
|
||||
private readonly services: DashboardContainerServices,
|
||||
stateTransfer?: EmbeddableStateTransfer,
|
||||
parent?: Container
|
||||
) {
|
||||
super(
|
||||
|
@ -134,7 +130,6 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard
|
|||
services.embeddable.getEmbeddableFactory,
|
||||
parent
|
||||
);
|
||||
this.embeddablePanel = services.embeddable.getEmbeddablePanel(stateTransfer);
|
||||
}
|
||||
|
||||
protected createNewPanelState<
|
||||
|
@ -258,11 +253,7 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard
|
|||
ReactDOM.render(
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider services={this.services}>
|
||||
<DashboardViewport
|
||||
container={this}
|
||||
PanelComponent={this.embeddablePanel}
|
||||
switchViewMode={this.switchViewMode}
|
||||
/>
|
||||
<DashboardViewport container={this} switchViewMode={this.switchViewMode} />
|
||||
</KibanaContextProvider>
|
||||
</I18nProvider>,
|
||||
dom
|
||||
|
|
|
@ -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<DashboardContainerServices>,
|
||||
private getHistory: () => ScopedHistory
|
||||
) {}
|
||||
constructor(private readonly getStartServices: () => Promise<DashboardContainerServices>) {}
|
||||
|
||||
public isEditable = async () => {
|
||||
// Currently unused for dashboards
|
||||
|
@ -74,7 +70,6 @@ export class DashboardContainerFactoryDefinition
|
|||
parent?: Container
|
||||
): Promise<DashboardContainer | ErrorEmbeddable> => {
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -83,7 +83,6 @@ function prepare(props?: Partial<DashboardGridProps>) {
|
|||
dashboardContainer = new DashboardContainer(initialInput, options);
|
||||
const defaultTestProps: DashboardGridProps = {
|
||||
container: dashboardContainer,
|
||||
PanelComponent: () => <div />,
|
||||
kibana: null as any,
|
||||
intl: null as any,
|
||||
};
|
||||
|
|
|
@ -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<DashboardGridProps, State> {
|
|||
key={panel.type}
|
||||
embeddableId={panel.explicitInput.id}
|
||||
container={this.props.container}
|
||||
PanelComponent={this.props.PanelComponent}
|
||||
PanelComponent={this.props.kibana.services.embeddable.EmbeddablePanel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -92,7 +92,6 @@ function getProps(
|
|||
dashboardContainer = new DashboardContainer(input, options);
|
||||
const defaultTestProps: DashboardViewportProps = {
|
||||
container: dashboardContainer,
|
||||
PanelComponent: () => <div />,
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
|
@ -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<DashboardViewportProps, S
|
|||
};
|
||||
|
||||
public render() {
|
||||
const { container, PanelComponent } = this.props;
|
||||
const { container } = this.props;
|
||||
const isEditMode = container.getInput().viewMode !== ViewMode.VIEW;
|
||||
const {
|
||||
isEmbeddedExternally,
|
||||
|
@ -174,7 +172,7 @@ export class DashboardViewport extends React.Component<DashboardViewportProps, S
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
<DashboardGrid container={container} PanelComponent={PanelComponent} />
|
||||
<DashboardGrid container={container} />
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
||||
import { EMBEDDABLE_EDITOR_STATE_KEY, EMBEDDABLE_PACKAGE_STATE_KEY } from './types';
|
||||
import { EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY } from './embeddable_state_transfer';
|
||||
|
||||
function mockHistoryState(state: unknown) {
|
||||
return scopedHistoryMock.create({ state });
|
||||
}
|
||||
const createStorage = (): Storage => {
|
||||
const createMockStore = () => {
|
||||
let innerStore: Record<string, any> = {};
|
||||
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<ApplicationStart>;
|
||||
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(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', {
|
||||
state: { originatingApp: 'superUltraTestDashboard' },
|
||||
});
|
||||
});
|
||||
|
||||
it('can send an outgoing originating app state in append mode', async () => {
|
||||
const historyMock = mockHistoryState({ kibanaIsNowForSports: 'extremeSportsKibana' });
|
||||
stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock);
|
||||
await stateTransfer.navigateToEditor(destinationApp, {
|
||||
state: { originatingApp },
|
||||
appendToExistingState: true,
|
||||
expect(store.set).toHaveBeenCalledWith(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, {
|
||||
[EMBEDDABLE_EDITOR_STATE_KEY]: { originatingApp: 'superUltraTestDashboard' },
|
||||
});
|
||||
expect(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', {
|
||||
path: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
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 },
|
||||
});
|
||||
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(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', {
|
||||
state: { type: 'coolestType', input: { savedObjectId: '150' } },
|
||||
});
|
||||
});
|
||||
|
||||
it('can send an outgoing embeddable package state in append mode', async () => {
|
||||
const historyMock = mockHistoryState({ kibanaIsNowForSports: 'extremeSportsKibana' });
|
||||
stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock);
|
||||
await stateTransfer.navigateToWithEmbeddablePackage(destinationApp, {
|
||||
state: { type: 'coolestType', input: { savedObjectId: '150' } },
|
||||
appendToExistingState: true,
|
||||
expect(store.set).toHaveBeenCalledWith(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY, {
|
||||
[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);
|
||||
const fetchedState = stateTransfer.getIncomingEditorState();
|
||||
expect(fetchedState).toEqual({ originatingApp: 'extremeSportsKibana' });
|
||||
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' } },
|
||||
});
|
||||
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,
|
||||
});
|
||||
});
|
||||
|
||||
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('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: 'superUltraTestDashboard' });
|
||||
});
|
||||
|
||||
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',
|
||||
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',
|
||||
});
|
||||
stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock);
|
||||
stateTransfer.getIncomingEmbeddablePackage({ keysToRemoveAfterFetch: ['type', 'input'] });
|
||||
expect(historyMock.replace).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ state: { test1: 'test1', test2: 'test2' } })
|
||||
);
|
||||
});
|
||||
|
||||
it('leaves state as is when no keysToRemove are supplied', async () => {
|
||||
const historyMock = mockHistoryState({
|
||||
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 = new EmbeddableStateTransfer(application.navigateToApp, historyMock);
|
||||
stateTransfer.getIncomingEmbeddablePackage();
|
||||
expect(historyMock.location.state).toEqual({
|
||||
type: 'skisEmbeddable',
|
||||
input: { savedObjectId: '123' },
|
||||
test1: 'test1',
|
||||
test2: 'test2',
|
||||
stateTransfer.getIncomingEditorState(true);
|
||||
expect(store.get(EMBEDDABLE_STATE_TRANSFER_STORAGE_KEY)).toEqual({
|
||||
iSHouldStillbeHere: 'doing the sports thing',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<string, PublicAppInfo> | undefined
|
||||
) {}
|
||||
private appList?: ReadonlyMap<string, PublicAppInfo> | 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<EmbeddableEditorState>(isEmbeddableEditorState, {
|
||||
keysToRemoveAfterFetch: options?.keysToRemoveAfterFetch,
|
||||
});
|
||||
public getIncomingEditorState(removeAfterFetch?: boolean): EmbeddableEditorState | undefined {
|
||||
return this.getIncomingState<EmbeddableEditorState>(
|
||||
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<EmbeddablePackageState>(isEmbeddablePackageState, {
|
||||
keysToRemoveAfterFetch: options?.keysToRemoveAfterFetch,
|
||||
});
|
||||
public getIncomingEmbeddablePackage(
|
||||
removeAfterFetch?: boolean
|
||||
): EmbeddablePackageState | undefined {
|
||||
return this.getIncomingState<EmbeddablePackageState>(
|
||||
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<void> {
|
||||
await this.navigateToWithState<EmbeddableEditorState>(appId, options);
|
||||
await this.navigateToWithState<EmbeddableEditorState>(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<void> {
|
||||
await this.navigateToWithState<EmbeddablePackageState>(appId, options);
|
||||
await this.navigateToWithState<EmbeddablePackageState>(appId, EMBEDDABLE_PACKAGE_STATE_KEY, {
|
||||
...options,
|
||||
appendToExistingState: true,
|
||||
});
|
||||
}
|
||||
|
||||
private getIncomingState<IncomingStateType>(
|
||||
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<OutgoingStateType = unknown>(
|
||||
appId: string,
|
||||
key: string,
|
||||
options?: { path?: string; state?: OutgoingStateType; appendToExistingState?: boolean }
|
||||
): Promise<void> {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -81,6 +81,7 @@ export const createEmbeddablePanelMock = ({
|
|||
|
||||
export const createEmbeddableStateTransferMock = (): Partial<EmbeddableStateTransfer> => {
|
||||
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(),
|
||||
};
|
||||
|
|
|
@ -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<EmbeddableState
|
|||
) => EmbeddableFactory<I, O, E> | undefined;
|
||||
getEmbeddableFactories: () => IterableIterator<EmbeddableFactory>;
|
||||
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<EmbeddableSetup, Embeddabl
|
|||
private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map();
|
||||
private readonly enhancements: EnhancementsRegistry = new Map();
|
||||
private customEmbeddableFactoryProvider?: EmbeddableFactoryProvider;
|
||||
private outgoingOnlyStateTransfer: EmbeddableStateTransfer = {} as EmbeddableStateTransfer;
|
||||
private stateTransferService: EmbeddableStateTransfer = {} as EmbeddableStateTransfer;
|
||||
private isRegistryReady = false;
|
||||
private appList?: ReadonlyMap<string, PublicAppInfo>;
|
||||
private appListSubscription?: Subscription;
|
||||
|
@ -160,14 +159,13 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
|
|||
this.appList = appList;
|
||||
});
|
||||
|
||||
this.outgoingOnlyStateTransfer = new EmbeddableStateTransfer(
|
||||
this.stateTransferService = new EmbeddableStateTransfer(
|
||||
core.application.navigateToApp,
|
||||
undefined,
|
||||
this.appList
|
||||
);
|
||||
this.isRegistryReady = true;
|
||||
|
||||
const getEmbeddablePanelHoc = (stateTransfer?: EmbeddableStateTransfer) => ({
|
||||
const getEmbeddablePanelHoc = () => ({
|
||||
embeddable,
|
||||
hideHeader,
|
||||
}: {
|
||||
|
@ -177,7 +175,7 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
|
|||
<EmbeddablePanel
|
||||
hideHeader={hideHeader}
|
||||
embeddable={embeddable}
|
||||
stateTransfer={stateTransfer ? stateTransfer : this.outgoingOnlyStateTransfer}
|
||||
stateTransfer={this.stateTransferService}
|
||||
getActions={uiActions.getTriggerCompatibleActions}
|
||||
getEmbeddableFactory={this.getEmbeddableFactory}
|
||||
getAllEmbeddableFactories={this.getEmbeddableFactories}
|
||||
|
@ -206,13 +204,11 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
|
|||
options,
|
||||
this.getEmbeddableFactory
|
||||
),
|
||||
getStateTransfer: (history?: ScopedHistory) => {
|
||||
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),
|
||||
|
|
|
@ -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<EmbeddableState
|
|||
getEmbeddableFactories: () => IterableIterator<EmbeddableFactory>;
|
||||
// (undocumented)
|
||||
getEmbeddableFactory: <I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>>(embeddableFactoryId: string) => EmbeddableFactory<I, O, E> | 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<unknown> | undefined, appList?: ReadonlyMap<string, PublicAppInfo> | undefined);
|
||||
constructor(navigateToApp: ApplicationStart['navigateToApp'], appList?: ReadonlyMap<string, PublicAppInfo> | 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<void>;
|
||||
// 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<void>;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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', {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<App
|
||||
incomingState={embeddableEditorIncomingState}
|
||||
editorFrame={instance}
|
||||
initialInput={getInitialInput(routeProps)}
|
||||
initialInput={getInitialInput(routeProps, editByValue)}
|
||||
redirectTo={(savedObjectId?: string) => redirectTo(routeProps, savedObjectId)}
|
||||
redirectToOrigin={redirectToOrigin}
|
||||
redirectToDashboard={redirectToDashboard}
|
||||
|
@ -182,7 +186,11 @@ export async function mountApp(
|
|||
<HashRouter>
|
||||
<Switch>
|
||||
<Route exact path="/edit/:id" render={renderEditor} />
|
||||
<Route exact path={`/${LENS_EDIT_BY_VALUE}`} render={renderEditor} />
|
||||
<Route
|
||||
exact
|
||||
path={`/${LENS_EDIT_BY_VALUE}`}
|
||||
render={(routeProps) => renderEditor(routeProps, true)}
|
||||
/>
|
||||
<Route exact path="/" render={renderEditor} />
|
||||
<Route path="/" component={NotFound} />
|
||||
</Switch>
|
||||
|
|
|
@ -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'];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}'`,
|
||||
|
|
Loading…
Reference in a new issue