Skip to content

Commit

Permalink
Drilldown context menu (#59638)
Browse files Browse the repository at this point in the history
* fix: πŸ› fix TypeScript error

* feat: 🎸 add CONTEXT_MENU_DRILLDOWNS_TRIGGER trigger

* fix: πŸ› correctly order context menu items

* fix: πŸ› set correct order on drilldown flyout actions

* fix: πŸ› clean up context menu building functions

* feat: 🎸 add context menu separator action
  • Loading branch information
streamich authored Mar 9, 2020
1 parent a7ed6ab commit 43583c2
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 41 deletions.
4 changes: 4 additions & 0 deletions src/plugins/embeddable/public/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Filter } from '../../data/public';
import {
applyFilterTrigger,
contextMenuTrigger,
contextMenuDrilldownsTrigger,
createFilterAction,
panelBadgeTrigger,
selectRangeTrigger,
Expand All @@ -32,6 +33,7 @@ import {
VALUE_CLICK_TRIGGER,
SELECT_RANGE_TRIGGER,
CONTEXT_MENU_TRIGGER,
CONTEXT_MENU_DRILLDOWNS_TRIGGER,
PANEL_BADGE_TRIGGER,
ACTION_ADD_PANEL,
ACTION_CUSTOMIZE_PANEL,
Expand All @@ -51,6 +53,7 @@ declare module '../../ui_actions/public' {
filters: Filter[];
};
[CONTEXT_MENU_TRIGGER]: EmbeddableContext;
[CONTEXT_MENU_DRILLDOWNS_TRIGGER]: EmbeddableContext;
[PANEL_BADGE_TRIGGER]: EmbeddableContext;
}

Expand All @@ -70,6 +73,7 @@ declare module '../../ui_actions/public' {
*/
export const bootstrap = (uiActions: UiActionsSetup) => {
uiActions.registerTrigger(contextMenuTrigger);
uiActions.registerTrigger(contextMenuDrilldownsTrigger);
uiActions.registerTrigger(applyFilterTrigger);
uiActions.registerTrigger(panelBadgeTrigger);
uiActions.registerTrigger(selectRangeTrigger);
Expand Down
1 change: 1 addition & 0 deletions src/plugins/embeddable/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export {
ContainerInput,
ContainerOutput,
CONTEXT_MENU_TRIGGER,
CONTEXT_MENU_DRILLDOWNS_TRIGGER,
contextMenuTrigger,
ACTION_EDIT_PANEL,
EditPanelAction,
Expand Down
47 changes: 35 additions & 12 deletions src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,22 @@ import { EuiContextMenuPanelDescriptor, EuiPanel, htmlIdGenerator } from '@elast
import classNames from 'classnames';
import React from 'react';
import { Subscription } from 'rxjs';
import { buildContextMenuForActions, UiActionsService, Action } from '../ui_actions';
import {
buildContextMenuForActions,
UiActionsService,
Action,
contextMenuSeparatorAction,
} from '../ui_actions';
import { CoreStart, OverlayStart } from '../../../../../core/public';
import { toMountPoint } from '../../../../kibana_react/public';

import { Start as InspectorStartContract } from '../inspector';
import { CONTEXT_MENU_TRIGGER, PANEL_BADGE_TRIGGER, EmbeddableContext } from '../triggers';
import {
CONTEXT_MENU_TRIGGER,
CONTEXT_MENU_DRILLDOWNS_TRIGGER,
PANEL_BADGE_TRIGGER,
EmbeddableContext,
} from '../triggers';
import { IEmbeddable } from '../embeddables/i_embeddable';
import { ViewMode, GetEmbeddableFactory, GetEmbeddableFactories } from '../types';

Expand All @@ -37,6 +47,14 @@ import { InspectPanelAction } from './panel_header/panel_actions/inspect_panel_a
import { EditPanelAction } from '../actions';
import { CustomizePanelModal } from './panel_header/panel_actions/customize_title/customize_panel_modal';

const sortByOrderField = (
{ order: orderA }: { order?: number },
{ order: orderB }: { order?: number }
) => (orderB || 0) - (orderA || 0);

const removeById = (disabledActions: string[]) => ({ id }: { id: string }) =>
disabledActions.indexOf(id) === -1;

interface Props {
embeddable: IEmbeddable<any, any>;
getActions: UiActionsService['getTriggerCompatibleActions'];
Expand Down Expand Up @@ -200,13 +218,18 @@ export class EmbeddablePanel extends React.Component<Props, State> {
};

private getActionContextMenuPanel = async () => {
let actions = await this.props.getActions(CONTEXT_MENU_TRIGGER, {
let regularActions = await this.props.getActions(CONTEXT_MENU_TRIGGER, {
embeddable: this.props.embeddable,
});
let drilldownActions = await this.props.getActions(CONTEXT_MENU_DRILLDOWNS_TRIGGER, {
embeddable: this.props.embeddable,
});

const { disabledActions } = this.props.embeddable.getInput();
if (disabledActions) {
actions = actions.filter(action => disabledActions.indexOf(action.id) === -1);
const removeDisabledActions = removeById(disabledActions);
regularActions = regularActions.filter(removeDisabledActions);
drilldownActions = drilldownActions.filter(removeDisabledActions);
}

const createGetUserData = (overlays: OverlayStart) =>
Expand Down Expand Up @@ -245,16 +268,16 @@ export class EmbeddablePanel extends React.Component<Props, State> {
new EditPanelAction(this.props.getEmbeddableFactory),
];

const sorted = (actions as Array<Action<EmbeddableContext>>)
.concat(extraActions)
.sort((a: Action<EmbeddableContext>, b: Action<EmbeddableContext>) => {
const bOrder = b.order || 0;
const aOrder = a.order || 0;
return bOrder - aOrder;
});
const sortedRegularActions = [...regularActions, ...extraActions].sort(sortByOrderField);
const sortedDrilldownActions = [...drilldownActions].sort(sortByOrderField);
const actions = [
...sortedDrilldownActions,
...(sortedDrilldownActions.length ? [contextMenuSeparatorAction] : []),
...sortedRegularActions,
];

return await buildContextMenuForActions({
actions: sorted,
actions,
actionContext: { embeddable: this.props.embeddable },
closeMenu: this.closeMyContextMenuPanel,
});
Expand Down
7 changes: 7 additions & 0 deletions src/plugins/embeddable/public/lib/triggers/triggers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ export const contextMenuTrigger: Trigger<'CONTEXT_MENU_TRIGGER'> = {
description: 'Triggered on top-right corner context-menu select.',
};

export const CONTEXT_MENU_DRILLDOWNS_TRIGGER = 'CONTEXT_MENU_DRILLDOWNS_TRIGGER';
export const contextMenuDrilldownsTrigger: Trigger<'CONTEXT_MENU_DRILLDOWNS_TRIGGER'> = {
id: CONTEXT_MENU_DRILLDOWNS_TRIGGER,
title: 'Drilldown context menu',
description: 'Triggered on top-right corner context-menu select.',
};

export const APPLY_FILTER_TRIGGER = 'FILTER_TRIGGER';
export const applyFilterTrigger: Trigger<'FILTER_TRIGGER'> = {
id: APPLY_FILTER_TRIGGER,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,23 @@ import * as React from 'react';
import { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor } from '@elastic/eui';
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import { uiToReactComponent } from '../../../kibana_react/public';
import { Action } from '../actions';
import { uiToReactComponent, reactToUiComponent } from '../../../kibana_react/public';
import { Action, ActionInternal } from '../actions';

export const contextMenuSeparatorAction = new ActionInternal({
id: 'CONTEXT_MENU_SEPARATOR',
getDisplayName: () => 'separator',
MenuItem: reactToUiComponent(() => (
<div
style={{
width: '100%',
height: '1px',
background: 'red',
}}
/>
)),
execute: () => Promise.resolve(),
});

/**
* Transforms an array of Actions to the shape EuiContextMenuPanel expects.
Expand Down Expand Up @@ -63,33 +78,25 @@ async function buildEuiContextMenuPanelItems<Context extends object>({
actionContext: Context;
closeMenu: () => void;
}) {
const items: EuiContextMenuPanelItemDescriptor[] = [];
const promises = actions.map(async action => {
const items: EuiContextMenuPanelItemDescriptor[] = new Array(actions.length);
const promises = actions.map(async (action, index) => {
const isCompatible = await action.isCompatible(actionContext);
if (!isCompatible) {
return;
}

items.push(
convertPanelActionToContextMenuItem({
action,
actionContext,
closeMenu,
})
);
items[index] = convertPanelActionToContextMenuItem({
action,
actionContext,
closeMenu,
});
});

await Promise.all(promises);

return items;
return items.filter(Boolean);
}

/**
*
* @param {ContextMenuAction} action
* @param {Embeddable} embeddable
* @return {EuiContextMenuPanelItemDescriptor}
*/
function convertPanelActionToContextMenuItem<Context extends object>({
action,
actionContext,
Expand All @@ -115,8 +122,11 @@ function convertPanelActionToContextMenuItem<Context extends object>({
closeMenu();
};

if (action.getHref && action.getHref(actionContext)) {
menuPanelItem.href = action.getHref(actionContext);
if (action.getHref) {
const href = action.getHref(actionContext);
if (href) {
menuPanelItem.href = action.getHref(actionContext);
}
}

return menuPanelItem;
Expand Down
5 changes: 4 additions & 1 deletion src/plugins/ui_actions/public/context_menu/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,8 @@
* under the License.
*/

export { buildContextMenuForActions } from './build_eui_context_menu_panels';
export {
buildContextMenuForActions,
contextMenuSeparatorAction,
} from './build_eui_context_menu_panels';
export { openContextMenu } from './open_context_menu';
2 changes: 1 addition & 1 deletion src/plugins/ui_actions/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export {
ActionContract as UiActionsActionContract,
} from './actions';
export { CollectConfigProps as UiActionsCollectConfigProps } from './util';
export { buildContextMenuForActions } from './context_menu';
export { buildContextMenuForActions, contextMenuSeparatorAction } from './context_menu';
export { Trigger, TriggerContext } from './triggers';
export { TriggerContextMapping, TriggerId, ActionContextMapping, ActionType } from './types';
export { ActionByType } from './actions';
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface OpenFlyoutAddDrilldownParams {
export class FlyoutCreateDrilldownAction implements ActionByType<typeof OPEN_FLYOUT_ADD_DRILLDOWN> {
public readonly type = OPEN_FLYOUT_ADD_DRILLDOWN;
public readonly id = OPEN_FLYOUT_ADD_DRILLDOWN;
public order = 100;
public order = 2;

constructor(protected readonly params: OpenFlyoutAddDrilldownParams) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
reactToUiComponent,
} from '../../../../../../src/plugins/kibana_react/public';
import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public';
import { FormCreateDrilldown } from '../../components/form_create_drilldown';
import { FormDrilldownWizard } from '../../components/form_drilldown_wizard';

export const OPEN_FLYOUT_EDIT_DRILLDOWN = 'OPEN_FLYOUT_EDIT_DRILLDOWN';

Expand All @@ -36,7 +36,7 @@ const drilldrownCount = 2;
export class FlyoutEditDrilldownAction implements ActionByType<typeof OPEN_FLYOUT_EDIT_DRILLDOWN> {
public readonly type = OPEN_FLYOUT_EDIT_DRILLDOWN;
public readonly id = OPEN_FLYOUT_EDIT_DRILLDOWN;
public order = 100;
public order = 1;

constructor(protected readonly params: FlyoutEditDrilldownParams) {}

Expand Down Expand Up @@ -67,6 +67,6 @@ export class FlyoutEditDrilldownAction implements ActionByType<typeof OPEN_FLYOU

public async execute({ embeddable }: FlyoutEditDrilldownActionContext) {
const overlays = await this.params.overlays();
overlays.openFlyout(toMountPoint(<FormCreateDrilldown />));
overlays.openFlyout(toMountPoint(<FormDrilldownWizard />));
}
}
6 changes: 3 additions & 3 deletions x-pack/plugins/drilldowns/public/service/drilldown_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { CoreSetup } from 'src/core/public';
import { CONTEXT_MENU_TRIGGER } from '../../../../../src/plugins/embeddable/public';
import { CONTEXT_MENU_DRILLDOWNS_TRIGGER } from '../../../../../src/plugins/embeddable/public';
import { FlyoutCreateDrilldownAction, FlyoutEditDrilldownAction } from '../actions';
import { DrilldownsSetupDependencies } from '../plugin';

Expand All @@ -15,11 +15,11 @@ export class DrilldownService {

const actionFlyoutCreateDrilldown = new FlyoutCreateDrilldownAction({ overlays });
uiActions.registerAction(actionFlyoutCreateDrilldown);
uiActions.attachAction(CONTEXT_MENU_TRIGGER, actionFlyoutCreateDrilldown);
uiActions.attachAction(CONTEXT_MENU_DRILLDOWNS_TRIGGER, actionFlyoutCreateDrilldown);

const actionFlyoutEditDrilldown = new FlyoutEditDrilldownAction({ overlays });
uiActions.registerAction(actionFlyoutEditDrilldown);
uiActions.attachAction(CONTEXT_MENU_TRIGGER, actionFlyoutEditDrilldown);
uiActions.attachAction(CONTEXT_MENU_DRILLDOWNS_TRIGGER, actionFlyoutEditDrilldown);
}

/**
Expand Down

0 comments on commit 43583c2

Please sign in to comment.