From 6efef2b5bcf084ee1ab8f1a46f572940f4be14b6 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 16 Mar 2020 15:02:43 +0100 Subject: [PATCH] Drilldowns various 4 (#60264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 hide "Create drilldown" from context menu when needed * style: 💄 remove AnyDrilldown type * feat: 🎸 add drilldown factory context * chore: 🤖 remove sample drilldown * fix: 🐛 increase spacing between action factory picker --- src/plugins/ui_actions/public/index.ts | 9 +- .../public/service/ui_actions_service.ts | 12 +- .../action_wizard/action_wizard.tsx | 26 +++-- .../actions/flyout_create_drilldown/index.tsx | 21 ++-- .../dashboard_drilldowns_services.ts | 3 +- .../drilldown.tsx | 5 - .../dashboard_to_dashboard_drilldown/types.ts | 7 +- .../public/services/drilldown_service.ts | 62 +++++----- x-pack/plugins/drilldowns/public/types.ts | 109 ++++++++++++++++-- 9 files changed, 177 insertions(+), 77 deletions(-) diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index e0ac61a55081b..f674179a217aa 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -28,14 +28,15 @@ export { UiActionsSetup, UiActionsStart } from './plugin'; export { UiActionsServiceParams, UiActionsService } from './service'; export { Action, - createAction, - IncompatibleActionError, ActionDefinition as UiActionsActionDefinition, + ActionFactoryDefinition as UiActionsActionFactoryDefinition, ActionInternal as UiActionsActionInternal, ActionStorage as UiActionsActionStorage, - SerializedEvent as UiActionsSerializedEvent, - SerializedAction as UiActionsSerializedAction, + createAction, DynamicActionManager, + IncompatibleActionError, + SerializedAction as UiActionsSerializedAction, + SerializedEvent as UiActionsSerializedEvent, } from './actions'; export { buildContextMenuForActions, contextMenuSeparatorAction } from './context_menu'; export { diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.ts b/src/plugins/ui_actions/public/service/ui_actions_service.ts index 824be69a991a8..deacf61af41a8 100644 --- a/src/plugins/ui_actions/public/service/ui_actions_service.ts +++ b/src/plugins/ui_actions/public/service/ui_actions_service.ts @@ -273,14 +273,20 @@ export class UiActionsService { * Register an action factory. Action factories are used to configure and * serialize/deserialize dynamic actions. */ - public readonly registerActionFactory = (definition: ActionFactoryDefinition) => { + public readonly registerActionFactory = < + Config extends object = object, + FactoryContext extends object = object, + ActionContext extends object = object + >( + definition: ActionFactoryDefinition + ) => { if (this.actionFactories.has(definition.id)) { throw new Error(`ActionFactory [actionFactory.id = ${definition.id}] already registered.`); } - const actionFactory = new ActionFactory(definition); + const actionFactory = new ActionFactory(definition); - this.actionFactories.set(actionFactory.id, actionFactory); + this.actionFactories.set(actionFactory.id, actionFactory as ActionFactory); }; public readonly getActionFactory = (actionFactoryId: string): ActionFactory => { diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index f6fcbba7f4a9a..783b0e1d4aff7 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -172,21 +172,23 @@ const ActionFactorySelector: React.FC = ({ } return ( - + {[...actionFactories] .sort((f1, f2) => f1.order - f2.order) .map(actionFactory => ( - onActionFactorySelected(actionFactory)} - > - {actionFactory.getIconType(context) && ( - - )} - + + onActionFactorySelected(actionFactory)} + > + {actionFactory.getIconType(context) && ( + + )} + + ))} ); diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/index.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/index.tsx index a6be110c44a02..9ef9800ab90e6 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/index.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/index.tsx @@ -9,15 +9,11 @@ import { i18n } from '@kbn/i18n'; import { CoreStart } from 'src/core/public'; import { ActionByType } from '../../../../../../../../src/plugins/ui_actions/public'; import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; -import { IEmbeddable } from '../../../../../../../../src/plugins/embeddable/public'; import { DrilldownsStartContract } from '../../../../../../drilldowns/public'; +import { EmbeddableContext } from '../../../../../../../../src/plugins/embeddable/public'; export const OPEN_FLYOUT_ADD_DRILLDOWN = 'OPEN_FLYOUT_ADD_DRILLDOWN'; -export interface FlyoutCreateDrilldownActionContext { - embeddable: IEmbeddable; -} - export interface OpenFlyoutAddDrilldownParams { overlays: () => Promise; drilldowns: () => Promise; @@ -40,14 +36,23 @@ export class FlyoutCreateDrilldownAction implements ActionByType -1; } - public async execute(context: FlyoutCreateDrilldownActionContext) { + public async isCompatible(context: EmbeddableContext) { + const isEditMode = context.embeddable.getInput().viewMode === 'edit'; + return isEditMode && this.isEmbeddableCompatible(context); + } + + public async execute(context: EmbeddableContext) { const overlays = await this.params.overlays(); const drilldowns = await this.params.drilldowns(); const dynamicActionManager = context.embeddable.dynamicActions; + if (!dynamicActionManager) { throw new Error(`Can't execute FlyoutCreateDrilldownAction without dynamicActionsManager`); } diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_drilldowns_services.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_drilldowns_services.ts index ed0cb425ee106..6695811500e73 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_drilldowns_services.ts +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_drilldowns_services.ts @@ -12,7 +12,6 @@ import { } from '../../../../../../src/plugins/embeddable/public'; import { FlyoutCreateDrilldownAction, - FlyoutCreateDrilldownActionContext, FlyoutEditDrilldownAction, OPEN_FLYOUT_ADD_DRILLDOWN, OPEN_FLYOUT_EDIT_DRILLDOWN, @@ -22,7 +21,7 @@ import { DashboardToDashboardDrilldown } from './dashboard_to_dashboard_drilldow declare module '../../../../../../src/plugins/ui_actions/public' { export interface ActionContextMapping { - [OPEN_FLYOUT_ADD_DRILLDOWN]: FlyoutCreateDrilldownActionContext; + [OPEN_FLYOUT_ADD_DRILLDOWN]: EmbeddableContext; [OPEN_FLYOUT_EDIT_DRILLDOWN]: EmbeddableContext; } } diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx index 211a16451b30c..e80f4a24a56de 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx @@ -13,11 +13,6 @@ import { DASHBOARD_TO_DASHBOARD_DRILLDOWN } from './constants'; import { DrilldownsDrilldown as Drilldown } from '../../../../../drilldowns/public'; import { txtGoToDashboard } from './i18n'; -export const dashboards = [ - { id: 'dashboard1', title: 'Dashboard 1' }, - { id: 'dashboard2', title: 'Dashboard 2' }, -]; - export interface Params { savedObjects: () => Promise; } diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts index 92f5a9be648bc..74be9c328f7f2 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts @@ -4,10 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EmbeddableVisTriggerContext } from '../../../../../../../src/plugins/embeddable/public'; +import { + EmbeddableVisTriggerContext, + EmbeddableContext, +} from '../../../../../../../src/plugins/embeddable/public'; import { UiActionsCollectConfigProps } from '../../../../../../../src/plugins/ui_actions/public'; -export type FactoryContext = any; +export type FactoryContext = EmbeddableContext; export type ActionContext = EmbeddableVisTriggerContext; export interface Config { diff --git a/x-pack/plugins/drilldowns/public/services/drilldown_service.ts b/x-pack/plugins/drilldowns/public/services/drilldown_service.ts index e258319a16b70..3f89a9f5d6441 100644 --- a/x-pack/plugins/drilldowns/public/services/drilldown_service.ts +++ b/x-pack/plugins/drilldowns/public/services/drilldown_service.ts @@ -6,14 +6,8 @@ import { CoreSetup } from 'src/core/public'; import { AdvancedUiActionsSetup } from '../../../advanced_ui_actions/public'; -import { AnyDrilldown } from '../types'; - -// TODO: MOCK DATA -import { - // dashboardDrilldownActionFactory, - urlDrilldownActionFactory, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../advanced_ui_actions/public/components/action_wizard/test_data'; +import { Drilldown, DrilldownFactoryContext } from '../types'; +import { UiActionsActionFactoryDefinition as ActionFactoryDefinition } from '../../../../../src/plugins/ui_actions/public'; export interface DrilldownServiceSetupDeps { advancedUiActions: AdvancedUiActionsSetup; @@ -23,7 +17,13 @@ export interface DrilldownServiceSetupContract { /** * Convenience method to register a drilldown. */ - registerDrilldown: (drilldown: AnyDrilldown) => void; + registerDrilldown: < + Config extends object = object, + CreationContext extends object = object, + ExecutionContext extends object = object + >( + drilldown: Drilldown + ) => void; } export class DrilldownService { @@ -31,8 +31,12 @@ export class DrilldownService { core: CoreSetup, { advancedUiActions }: DrilldownServiceSetupDeps ): DrilldownServiceSetupContract { - const registerDrilldown: DrilldownServiceSetupContract['registerDrilldown'] = ({ - id, + const registerDrilldown = < + Config extends object = object, + CreationContext extends object = object, + ExecutionContext extends object = object + >({ + id: factoryId, places, CollectConfig, createConfig, @@ -40,35 +44,33 @@ export class DrilldownService { getDisplayName, euiIcon, execute, - }) => { - advancedUiActions.registerActionFactory({ - id, + }: Drilldown) => { + const actionFactory: ActionFactoryDefinition< + Config, + DrilldownFactoryContext, + ExecutionContext + > = { + id: factoryId, CollectConfig, createConfig, isConfigValid, getDisplayName, getIconType: () => euiIcon, isCompatible: async ({ place }: any) => (!places ? true : places.indexOf(place) > -1), - create: config => ({ + create: serializedAction => ({ id: '', - type: id as any, + type: factoryId, getIconType: () => euiIcon, - execute: async context => await execute(config, context), + execute: async context => await execute(serializedAction.config, context), }), - }); - }; + } as ActionFactoryDefinition< + Config, + DrilldownFactoryContext, + ExecutionContext + >; - /* - registerDrilldown({ - ...dashboardDrilldownActionFactory, - execute: () => alert('Dashboard drilldown!'), - } as any); - */ - registerDrilldown({ - ...urlDrilldownActionFactory, - euiIcon: 'link', - execute: () => alert('URL drilldown!'), - } as any); + advancedUiActions.registerActionFactory(actionFactory); + }; return { registerDrilldown, diff --git a/x-pack/plugins/drilldowns/public/types.ts b/x-pack/plugins/drilldowns/public/types.ts index 4c5cfa2e596aa..21e28d8a1e64f 100644 --- a/x-pack/plugins/drilldowns/public/types.ts +++ b/x-pack/plugins/drilldowns/public/types.ts @@ -8,27 +8,93 @@ import { AdvancedUiActionsActionFactoryDefinition as ActionFactoryDefinition } f export interface Drilldown< Config extends object = object, - FactoryContext extends object = object, + CreationContext extends object = object, ExecutionContext extends object = object -> - extends Pick< - ActionFactoryDefinition, - 'id' | 'createConfig' | 'CollectConfig' | 'isConfigValid' | 'getDisplayName' - > { +> { /** - * List of places where this drilldown should be available, e.g "dashboard". + * Globally unique identifier for this drilldown. + */ + id: string; + + /** + * List of places where this drilldown should be available, e.g "dashboard", "graph". * If omitted, the drilldown will be shown in all places. */ places?: string[]; /** - * Name of EUI icon to display next to this drilldown. + * Function that returns default config for this drilldown. + */ + createConfig: ActionFactoryDefinition< + Config, + DrilldownFactoryContext, + ExecutionContext + >['createConfig']; + + /** + * `UiComponent` that collections config for this drilldown. You can create + * a React component and transform it `UiComponent` using `uiToReactComponent` + * helper from `kibana_utils` plugin. + * + * ```tsx + * import React from 'react'; + * import { uiToReactComponent } from 'src/plugins/kibana_utils'; + * import { UiActionsCollectConfigProps as CollectConfigProps } from 'src/plugins/ui_actions/public'; + * + * type Props = CollectConfigProps; + * + * const ReactCollectConfig: React.FC = () => { + * return
Collecting config...'
; + * }; + * + * export const CollectConfig = uiToReactComponent(ReactCollectConfig); + * ``` + */ + CollectConfig: ActionFactoryDefinition< + Config, + DrilldownFactoryContext, + ExecutionContext + >['CollectConfig']; + + /** + * A validator function for the config object. Should always return a boolean + * given any input. + */ + isConfigValid: ActionFactoryDefinition< + Config, + DrilldownFactoryContext, + ExecutionContext + >['isConfigValid']; + + /** + * Name of EUI icon to display when showing this drilldown to user. */ euiIcon?: string; /** - * Implements the "navigation" action when user clicks something in the UI and - * instance of this drilldown is triggered. + * Should return an internationalized name of the drilldown, which will be + * displayed to the user. + */ + getDisplayName: () => string; + + /** + * Whether this drilldown should be considered for execution given `config` + * and `context`. When multiple drilldowns are attached to the same trigger + * user is presented with a context menu to pick one drilldown for execute. If + * this method returns `true` this trigger will appear in the context menu + * list, if `false`, it will not be presented to the user. If `doExecute` is + * not implemented, this drilldown will always be show to the user. + * + * @param config Config object that user configured this drilldown with. + * @param context Object that represents context in which the underlying + * `UIAction` of this drilldown is being executed in. + */ + doExecute?(config: Config, context: ExecutionContext): Promise; + + /** + * Implements the "navigation" action of the drilldown. This happens when + * user clicks something in the UI that executes a trigger to which this + * drilldown was attached. * * @param config Config object that user configured this drilldown with. * @param context Object that represents context in which the underlying @@ -37,4 +103,25 @@ export interface Drilldown< execute(config: Config, context: ExecutionContext): void; } -export type AnyDrilldown = Drilldown; +/** + * Context object used when creating a drilldown. + */ +export interface DrilldownFactoryContext { + /** + * List of places as configured in @type {Drilldown} interface. + */ + places?: string[]; + + /** + * Context provided to the drilldown factory by the place where the UI is + * rendered. For example, for the "dashboard" place, this context contains + * the ID of the current dashboard, which could be used for filtering it out + * of the list. + */ + placeContext: T; + + /** + * List of triggers that user selected in the UI. + */ + triggers: string[]; +}