Add editApp and editPath to embeddable (#64297)

This commit is contained in:
Joe Reuter 2020-04-27 18:07:10 +02:00 committed by GitHub
parent 45aa090e99
commit a32d7b1344
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 66 additions and 10 deletions

View file

@ -275,6 +275,7 @@ class DashboardGridUi extends React.Component<DashboardGridProps, State> {
getEmbeddableFactory={this.props.kibana.services.embeddable.getEmbeddableFactory}
getAllEmbeddableFactories={this.props.kibana.services.embeddable.getEmbeddableFactories}
overlays={this.props.kibana.services.overlays}
application={this.props.kibana.services.application}
notifications={this.props.kibana.services.notifications}
inspector={this.props.kibana.services.inspector}
SavedObjectFinder={this.props.kibana.services.SavedObjectFinder}

View file

@ -84,6 +84,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => {
getAllEmbeddableFactories={(() => []) as any}
getEmbeddableFactory={(() => null) as any}
notifications={{} as any}
application={{} as any}
overlays={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}

View file

@ -41,7 +41,7 @@ class EditableEmbeddable extends Embeddable {
}
test('is compatible when edit url is available, in edit mode and editable', async () => {
const action = new EditPanelAction(getFactory);
const action = new EditPanelAction(getFactory, {} as any);
expect(
await action.isCompatible({
embeddable: new EditableEmbeddable({ id: '123', viewMode: ViewMode.EDIT }, true),
@ -50,7 +50,7 @@ test('is compatible when edit url is available, in edit mode and editable', asyn
});
test('getHref returns the edit urls', async () => {
const action = new EditPanelAction(getFactory);
const action = new EditPanelAction(getFactory, {} as any);
expect(action.getHref).toBeDefined();
if (action.getHref) {
@ -64,7 +64,7 @@ test('getHref returns the edit urls', async () => {
});
test('is not compatible when edit url is not available', async () => {
const action = new EditPanelAction(getFactory);
const action = new EditPanelAction(getFactory, {} as any);
const embeddable = new ContactCardEmbeddable(
{
id: '123',
@ -83,7 +83,7 @@ test('is not compatible when edit url is not available', async () => {
});
test('is not visible when edit url is available but in view mode', async () => {
const action = new EditPanelAction(getFactory);
const action = new EditPanelAction(getFactory, {} as any);
expect(
await action.isCompatible({
embeddable: new EditableEmbeddable(
@ -98,7 +98,7 @@ test('is not visible when edit url is available but in view mode', async () => {
});
test('is not compatible when edit url is available, in edit mode, but not editable', async () => {
const action = new EditPanelAction(getFactory);
const action = new EditPanelAction(getFactory, {} as any);
expect(
await action.isCompatible({
embeddable: new EditableEmbeddable(

View file

@ -18,6 +18,7 @@
*/
import { i18n } from '@kbn/i18n';
import { ApplicationStart } from 'kibana/public';
import { Action } from 'src/plugins/ui_actions/public';
import { ViewMode } from '../types';
import { EmbeddableFactoryNotFoundError } from '../errors';
@ -35,7 +36,10 @@ export class EditPanelAction implements Action<ActionContext> {
public readonly id = ACTION_EDIT_PANEL;
public order = 15;
constructor(private readonly getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']) {}
constructor(
private readonly getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'],
private readonly application: ApplicationStart
) {}
public getDisplayName({ embeddable }: ActionContext) {
const factory = this.getEmbeddableFactory(embeddable.type);
@ -56,18 +60,35 @@ export class EditPanelAction implements Action<ActionContext> {
public async isCompatible({ embeddable }: ActionContext) {
const canEditEmbeddable = Boolean(
embeddable && embeddable.getOutput().editable && embeddable.getOutput().editUrl
embeddable &&
embeddable.getOutput().editable &&
(embeddable.getOutput().editUrl ||
(embeddable.getOutput().editApp && embeddable.getOutput().editPath))
);
const inDashboardEditMode = embeddable.getInput().viewMode === ViewMode.EDIT;
return Boolean(canEditEmbeddable && inDashboardEditMode);
}
public async execute(context: ActionContext) {
const appTarget = this.getAppTarget(context);
if (appTarget) {
await this.application.navigateToApp(appTarget.app, { path: appTarget.path });
return;
}
const href = await this.getHref(context);
if (href) {
// TODO: when apps start using browser router instead of hash router this has to be fixed
// https://github.com/elastic/kibana/issues/58217
window.location.href = href;
return;
}
}
public getAppTarget({ embeddable }: ActionContext): { app: string; path: string } | undefined {
const app = embeddable ? embeddable.getOutput().editApp : undefined;
const path = embeddable ? embeddable.getOutput().editPath : undefined;
if (app && path) {
return { app, path };
}
}

View file

@ -66,6 +66,7 @@ test('EmbeddableChildPanel renders an embeddable when it is done loading', async
getAllEmbeddableFactories={start.getEmbeddableFactories}
getEmbeddableFactory={getEmbeddableFactory}
notifications={{} as any}
application={{} as any}
overlays={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
@ -105,6 +106,7 @@ test(`EmbeddableChildPanel renders an error message if the factory doesn't exist
getEmbeddableFactory={(() => undefined) as any}
notifications={{} as any}
overlays={{} as any}
application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
/>

View file

@ -40,6 +40,7 @@ export interface EmbeddableChildPanelProps {
getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: CoreStart['overlays'];
notifications: CoreStart['notifications'];
application: CoreStart['application'];
inspector: InspectorStartContract;
SavedObjectFinder: React.ComponentType<any>;
}
@ -101,6 +102,7 @@ export class EmbeddableChildPanel extends React.Component<EmbeddableChildPanelPr
getEmbeddableFactory={this.props.getEmbeddableFactory}
getAllEmbeddableFactories={this.props.getAllEmbeddableFactories}
overlays={this.props.overlays}
application={this.props.application}
notifications={this.props.notifications}
inspector={this.props.inspector}
SavedObjectFinder={this.props.SavedObjectFinder}

View file

@ -55,6 +55,8 @@ export interface EmbeddableInput {
export interface EmbeddableOutput {
editUrl?: string;
editApp?: string;
editPath?: string;
defaultTitle?: string;
title?: string;
editable?: boolean;

View file

@ -159,6 +159,7 @@ test('HelloWorldContainer in view mode hides edit mode actions', async () => {
getAllEmbeddableFactories={start.getEmbeddableFactories}
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
application={{} as any}
overlays={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
@ -198,6 +199,7 @@ const renderInEditModeAndOpenContextMenu = async (
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
/>
@ -296,6 +298,7 @@ test('HelloWorldContainer in edit mode shows edit mode actions', async () => {
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
/>
@ -358,6 +361,7 @@ test('Updates when hidePanelTitles is toggled', async () => {
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
/>
@ -410,6 +414,7 @@ test('Check when hide header option is false', async () => {
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
hideHeader={false}
@ -447,6 +452,7 @@ test('Check when hide header option is true', async () => {
getEmbeddableFactory={start.getEmbeddableFactory}
notifications={{} as any}
overlays={{} as any}
application={{} as any}
inspector={inspector}
SavedObjectFinder={() => null}
hideHeader={true}

View file

@ -45,6 +45,7 @@ interface Props {
getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: CoreStart['overlays'];
notifications: CoreStart['notifications'];
application: CoreStart['application'];
inspector: InspectorStartContract;
SavedObjectFinder: React.ComponentType<any>;
hideHeader?: boolean;
@ -243,7 +244,7 @@ export class EmbeddablePanel extends React.Component<Props, State> {
),
new InspectPanelAction(this.props.inspector),
new RemovePanelAction(),
new EditPanelAction(this.props.getEmbeddableFactory),
new EditPanelAction(this.props.getEmbeddableFactory, this.props.application),
];
const sorted = actions

View file

@ -49,6 +49,7 @@ interface HelloWorldContainerOptions {
getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'];
getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: CoreStart['overlays'];
application: CoreStart['application'];
notifications: CoreStart['notifications'];
inspector: InspectorStartContract;
SavedObjectFinder: React.ComponentType<any>;
@ -81,6 +82,7 @@ export class HelloWorldContainer extends Container<InheritedInput, HelloWorldCon
getAllEmbeddableFactories={this.options.getAllEmbeddableFactories}
getEmbeddableFactory={this.options.getEmbeddableFactory}
overlays={this.options.overlays}
application={this.options.application}
notifications={this.options.notifications}
inspector={this.options.inspector}
SavedObjectFinder={this.options.SavedObjectFinder}

View file

@ -32,6 +32,7 @@ interface Props {
getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'];
getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: CoreStart['overlays'];
application: CoreStart['application'];
notifications: CoreStart['notifications'];
inspector: InspectorStartContract;
SavedObjectFinder: React.ComponentType<any>;
@ -112,6 +113,7 @@ export class HelloWorldContainerComponent extends Component<Props, State> {
getAllEmbeddableFactories={this.props.getAllEmbeddableFactories}
overlays={this.props.overlays}
notifications={this.props.notifications}
application={this.props.application}
inspector={this.props.inspector}
SavedObjectFinder={this.props.SavedObjectFinder}
/>

View file

@ -118,6 +118,7 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
getAllEmbeddableFactories={this.getEmbeddableFactories}
overlays={core.overlays}
notifications={core.notifications}
application={core.application}
inspector={inspector}
SavedObjectFinder={getSavedObjectFinder(core.savedObjects, core.uiSettings)}
/>

View file

@ -110,6 +110,7 @@ test('ApplyFilterAction is incompatible if the root container does not accept a
getAllEmbeddableFactories: api.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector,
SavedObjectFinder: () => null,
}
@ -145,6 +146,7 @@ test('trying to execute on incompatible context throws an error ', async () => {
getAllEmbeddableFactories: api.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector,
SavedObjectFinder: () => null,
}

View file

@ -74,6 +74,7 @@ async function creatHelloWorldContainerAndEmbeddable(
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
});
@ -147,6 +148,7 @@ test('Container.removeEmbeddable removes and cleans up', async done => {
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@ -327,6 +329,7 @@ test(`Container updates its state when a child's input is updated`, async done =
getEmbeddableFactory: start.getEmbeddableFactory,
notifications: coreStart.notifications,
overlays: coreStart.overlays,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
});
@ -584,6 +587,7 @@ test('Container changes made directly after adding a new embeddable are propagat
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@ -708,6 +712,7 @@ test('untilEmbeddableLoaded() throws an error if there is no such child panel in
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@ -742,6 +747,7 @@ test('untilEmbeddableLoaded() resolves if child is loaded in the container', asy
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@ -781,6 +787,7 @@ test('untilEmbeddableLoaded resolves with undefined if child is subsequently rem
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@ -821,6 +828,7 @@ test('adding a panel then subsequently removing it before its loaded removes the
getAllEmbeddableFactories: start.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}

View file

@ -63,6 +63,7 @@ beforeEach(async () => {
getAllEmbeddableFactories: api.getEmbeddableFactories,
overlays: coreStart.overlays,
notifications: coreStart.notifications,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}

View file

@ -88,6 +88,7 @@ test('Explicit embeddable input mapped to undefined with no inherited value will
getEmbeddableFactory: start.getEmbeddableFactory,
notifications: coreStart.notifications,
overlays: coreStart.overlays,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}
@ -136,6 +137,7 @@ test('Explicit input tests in async situations', (done: () => void) => {
getEmbeddableFactory: start.getEmbeddableFactory,
notifications: coreStart.notifications,
overlays: coreStart.overlays,
application: coreStart.application,
inspector: {} as any,
SavedObjectFinder: () => null,
}

View file

@ -45,6 +45,7 @@ const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => {
getAllEmbeddableFactories={plugins.embeddable.getEmbeddableFactories}
notifications={core.notifications}
overlays={core.overlays}
application={core.application}
inspector={plugins.inspector}
SavedObjectFinder={getSavedObjectFinder(core.savedObjects, core.uiSettings)}
/>

View file

@ -208,6 +208,7 @@ export const EmbeddedMapComponent = ({
notifications={services.notifications}
overlays={services.overlays}
inspector={services.inspector}
application={services.application}
SavedObjectFinder={getSavedObjectFinder(services.savedObjects, services.uiSettings)}
/>
) : !isLoading && isIndexError ? (