[Drilldowns] improve action.getHref() for drilldowns (#63228)

getHref on Action interfaces in uiActions plugin is now async. getHref is now used only to support right click behaviour. execute() takes control on regular click.
This commit is contained in:
Anton Dosov 2020-04-15 15:41:42 +02:00 committed by GitHub
parent 88d3523925
commit 3b64a65ed6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 46 additions and 25 deletions

View file

@ -56,7 +56,7 @@ test('getHref returns the edit urls', async () => {
if (action.getHref) {
const embeddable = new EditableEmbeddable({ id: '123', viewMode: ViewMode.EDIT }, true);
expect(
action.getHref({
await action.getHref({
embeddable,
})
).toBe(embeddable.getOutput().editUrl);

View file

@ -62,11 +62,16 @@ export class EditPanelAction implements Action<ActionContext> {
return Boolean(canEditEmbeddable && inDashboardEditMode);
}
public async execute() {
return;
public async execute(context: ActionContext) {
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;
}
}
public getHref({ embeddable }: ActionContext): string {
public async getHref({ embeddable }: ActionContext): Promise<string> {
const editUrl = embeddable ? embeddable.getOutput().editUrl : undefined;
return editUrl ? editUrl : '';
}

View file

@ -63,9 +63,11 @@ export interface Action<Context = {}, T = ActionType> {
isCompatible(context: Context): Promise<boolean>;
/**
* If this returns something truthy, this is used in addition to the `execute` method when clicked.
* If this returns something truthy, this will be used as [href] attribute on a link if possible (e.g. in context menu item)
* to support right click -> open in a new tab behavior.
* For regular click navigation is prevented and `execute()` takes control.
*/
getHref?(context: Context): string | undefined;
getHref?(context: Context): Promise<string | undefined>;
/**
* Executes the action.

View file

@ -63,7 +63,7 @@ export interface ActionDefinition<T extends ActionType> {
/**
* If this returns something truthy, this is used in addition to the `execute` method when clicked.
*/
getHref?(context: ActionContextMapping[T]): string | undefined;
getHref?(context: ActionContextMapping[T]): Promise<string | undefined>;
/**
* Executes the action.

View file

@ -28,7 +28,6 @@ export function createAction<T extends ActionType>(action: ActionDefinition<T>):
id: action.type,
isCompatible: () => Promise.resolve(true),
getDisplayName: () => '',
getHref: () => undefined,
...action,
};
}

View file

@ -71,7 +71,7 @@ async function buildEuiContextMenuPanelItems<A>({
}
items.push(
convertPanelActionToContextMenuItem({
await convertPanelActionToContextMenuItem({
action,
actionContext,
closeMenu,
@ -88,9 +88,9 @@ async function buildEuiContextMenuPanelItems<A>({
*
* @param {ContextMenuAction} action
* @param {Embeddable} embeddable
* @return {EuiContextMenuPanelItemDescriptor}
* @return {Promise<EuiContextMenuPanelItemDescriptor>}
*/
function convertPanelActionToContextMenuItem<A>({
async function convertPanelActionToContextMenuItem<A>({
action,
actionContext,
closeMenu,
@ -98,7 +98,7 @@ function convertPanelActionToContextMenuItem<A>({
action: Action<A>;
actionContext: A;
closeMenu: () => void;
}): EuiContextMenuPanelItemDescriptor {
}): Promise<EuiContextMenuPanelItemDescriptor> {
const menuPanelItem: EuiContextMenuPanelItemDescriptor = {
name: action.MenuItem
? React.createElement(uiToReactComponent(action.MenuItem), {
@ -110,13 +110,33 @@ function convertPanelActionToContextMenuItem<A>({
'data-test-subj': `embeddablePanelAction-${action.id}`,
};
menuPanelItem.onClick = () => {
action.execute(actionContext);
menuPanelItem.onClick = event => {
if (event.currentTarget instanceof HTMLAnchorElement) {
// from react-router's <Link/>
if (
!event.defaultPrevented && // onClick prevented default
event.button === 0 && // ignore everything but left clicks
(!event.currentTarget.target || event.currentTarget.target === '_self') && // let browser handle "target=_blank" etc.
!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) // ignore clicks with modifier keys
) {
event.preventDefault();
action.execute(actionContext);
} else {
// let browser handle navigation
}
} else {
// not a link
action.execute(actionContext);
}
closeMenu();
};
if (action.getHref && action.getHref(actionContext)) {
menuPanelItem.href = action.getHref(actionContext);
if (action.getHref) {
const href = await action.getHref(actionContext);
if (href) {
menuPanelItem.href = href;
}
}
return menuPanelItem;

View file

@ -55,13 +55,6 @@ export class TriggerInternal<T extends TriggerId> {
action: Action<TriggerContextMapping[T]>,
context: TriggerContextMapping[T]
) {
const href = action.getHref && action.getHref(context);
if (href) {
window.location.href = href;
return;
}
await action.execute(context);
}

View file

@ -26,6 +26,8 @@ export const createSamplePanelLink = (): Action =>
createAction<typeof SAMPLE_PANEL_LINK>({
type: SAMPLE_PANEL_LINK,
getDisplayName: () => 'Sample panel Link',
execute: async () => {},
getHref: () => 'https://example.com/kibana/test',
execute: async () => {
window.location.href = 'https://example.com/kibana/test';
},
getHref: async () => 'https://example.com/kibana/test',
});