diff --git a/.i18nrc.json b/.i18nrc.json index a589b14dfc7ee..818b0bf38afc2 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -28,7 +28,8 @@ "kbnESQuery": "packages/kbn-es-query", "inspector": "src/plugins/inspector", "kibana-react": "src/plugins/kibana_react", - "esUi": "src/plugins/es_ui_shared" + "esUi": "src/plugins/es_ui_shared", + "uiActions": "src/plugins/ui_actions" }, "exclude": ["src/legacy/ui/ui_render/ui_render_mixin.js"], "translations": [] diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 24c2825e28174..39bd2015c3643 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -51,6 +51,7 @@ export default { '!src/legacy/ui/public/{agg_types,vis}/**/*.d.ts', ], moduleNameMapper: { + '^src/plugins/(.*)': '/src/plugins/$1', '^plugins/([^\/.]*)(.*)': '/src/legacy/core_plugins/$1/public$2', '^ui/(.*)': '/src/legacy/ui/public/$1', '^uiExports/(.*)': '/src/dev/jest/mocks/file_mock.js', diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/kibana.json b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/kibana.json index 7bfad6b852e88..417168ba61dd9 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/kibana.json +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/kibana.json @@ -3,7 +3,8 @@ "version": "kibana", "requiredPlugins": [ "embeddable", - "inspector" + "inspector", + "ui_actions" ], "server": false, "ui": true diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/legacy.ts b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/legacy.ts index 6a7c60191a4ce..ccb04d8c2e027 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/legacy.ts +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/legacy.ts @@ -33,6 +33,7 @@ const pluginInstance = plugin({} as any); export const setup = pluginInstance.setup(npSetup.core, { embeddable: embeddableSetup, + uiActions: npSetup.plugins.uiActions, }); export const start = pluginInstance.start(npStart.core, { @@ -42,4 +43,5 @@ export const start = pluginInstance.start(npStart.core, { SavedObjectFinder, ExitFullScreenButton, }, + uiActions: npStart.plugins.uiActions, }); diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.test.tsx b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.test.tsx index 7eb2f682e70ea..611b37dd7d54e 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.test.tsx +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.test.tsx @@ -53,6 +53,7 @@ beforeEach(async () => { notifications: {} as any, overlays: {} as any, savedObjectMetaData: {} as any, + uiActions: {} as any, }; const input = getSampleDashboardInput({ panels: { diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.tsx b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.tsx index 494110750fcf3..f05d0b0efc2ee 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.tsx +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/actions/expand_panel_action.tsx @@ -18,12 +18,12 @@ */ import { i18n } from '@kbn/i18n'; +import { IEmbeddable } from '../../../../../../embeddable_api/public/np_ready/public'; +import { DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '../embeddable'; import { - Action, - IEmbeddable, + IAction, IncompatibleActionError, -} from '../../../../../../embeddable_api/public/np_ready/public'; -import { DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '../embeddable'; +} from '../../../../../../../../../src/plugins/ui_actions/public'; export const EXPAND_PANEL_ACTION = 'togglePanel'; @@ -43,13 +43,12 @@ interface ActionContext { embeddable: IEmbeddable; } -export class ExpandPanelAction extends Action { +export class ExpandPanelAction implements IAction { public readonly type = EXPAND_PANEL_ACTION; + public readonly id = EXPAND_PANEL_ACTION; + public order = 7; - constructor() { - super(EXPAND_PANEL_ACTION); - this.order = 7; - } + constructor() {} public getDisplayName({ embeddable }: ActionContext) { if (!embeddable.parent || !isDashboard(embeddable.parent)) { diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.test.tsx b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.test.tsx index d71088335c2ad..8529f8944cbab 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.test.tsx +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.test.tsx @@ -45,6 +45,7 @@ const options: DashboardContainerOptions = { inspector: {} as any, SavedObjectFinder: () => null, ExitFullScreenButton: () => null, + uiActions: {} as any, }; beforeEach(() => { diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.tsx b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.tsx index dc7aee514ab26..919995f544960 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.tsx +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/dashboard_container.tsx @@ -22,6 +22,7 @@ import ReactDOM from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; import { Filter } from '@kbn/es-query'; import { RefreshInterval, TimeRange } from 'src/plugins/data/public'; +import { IUiActionsStart } from '../../../../../../../../plugins/ui_actions/public'; import { Container, ContainerInput, @@ -82,6 +83,7 @@ export interface DashboardContainerOptions { inspector: InspectorStartContract; SavedObjectFinder: React.ComponentType; ExitFullScreenButton: React.ComponentType; + uiActions: IUiActionsStart; } export type DashboardReactContextValue = KibanaReactContextValue; diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.test.tsx b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.test.tsx index 70eb59643ea08..641d55aea6c58 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.test.tsx +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.test.tsx @@ -68,6 +68,7 @@ function prepare(props?: Partial) { inspector: {} as any, SavedObjectFinder: () => null, ExitFullScreenButton: () => null, + uiActions: {} as any, }; dashboardContainer = new DashboardContainer(initialInput, options); const defaultTestProps: DashboardGridProps = { diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.tsx b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.tsx index d34a88c474e38..291bef15641f3 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.tsx +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/lib/embeddable/grid/dashboard_grid.tsx @@ -266,7 +266,7 @@ class DashboardGridUi extends React.Component { null, ExitFullScreenButton, + uiActions: {} as any, }; const input = getSampleDashboardInput({ diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/plugin.ts index 350def9833732..3d243ab3aa37a 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/plugin.ts @@ -18,17 +18,20 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { IUiActionsSetup, IUiActionsStart } from '../../../../../../plugins/ui_actions/public'; import { CONTEXT_MENU_TRIGGER, Plugin as EmbeddablePlugin } from './lib/embeddable_api'; import { ExpandPanelAction, DashboardContainerFactory } from './lib'; import { Start as InspectorStartContract } from '../../../../../../plugins/inspector/public'; interface SetupDependencies { embeddable: ReturnType; + uiActions: IUiActionsSetup; } interface StartDependencies { embeddable: ReturnType; inspector: InspectorStartContract; + uiActions: IUiActionsStart; __LEGACY: { SavedObjectFinder: React.ComponentType; ExitFullScreenButton: React.ComponentType; @@ -42,15 +45,15 @@ export class DashboardEmbeddableContainerPublicPlugin implements Plugin { constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup, { embeddable }: SetupDependencies): Setup { + public setup(core: CoreSetup, { embeddable, uiActions }: SetupDependencies): Setup { const expandPanelAction = new ExpandPanelAction(); - embeddable.registerAction(expandPanelAction); - embeddable.attachAction(CONTEXT_MENU_TRIGGER, expandPanelAction.id); + uiActions.registerAction(expandPanelAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, expandPanelAction.id); } public start(core: CoreStart, plugins: StartDependencies): Start { const { application, notifications, overlays } = core; - const { embeddable, inspector, __LEGACY } = plugins; + const { embeddable, inspector, __LEGACY, uiActions } = plugins; const factory = new DashboardContainerFactory({ application, @@ -60,6 +63,7 @@ export class DashboardEmbeddableContainerPublicPlugin inspector, SavedObjectFinder: __LEGACY.SavedObjectFinder, ExitFullScreenButton: __LEGACY.ExitFullScreenButton, + uiActions, }); embeddable.registerEmbeddableFactory(factory.type, factory); diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/tests/dashboard_container.test.tsx b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/tests/dashboard_container.test.tsx index e19d95aefe770..e9b84eca594ae 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/tests/dashboard_container.test.tsx +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public/tests/dashboard_container.test.tsx @@ -39,18 +39,21 @@ import { ContactCardEmbeddableOutput, } from '../../../../../embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable'; import { embeddablePluginMock } from '../../../../../embeddable_api/public/np_ready/public/mocks'; -import { EditModeAction } from '../../../../../embeddable_api/public/np_ready/public/lib/test_samples/actions/edit_mode_action'; +import { createEditModeAction } from '../../../../../embeddable_api/public/np_ready/public/lib/test_samples/actions/edit_mode_action'; // eslint-disable-next-line import { inspectorPluginMock } from '../../../../../../../plugins/inspector/public/mocks'; import { KibanaContextProvider } from '../../../../../../../plugins/kibana_react/public'; +// eslint-disable-next-line +import { uiActionsPluginMock } from 'src/plugins/ui_actions/public/mocks'; test('DashboardContainer in edit mode shows edit mode actions', async () => { const inspector = inspectorPluginMock.createStartContract(); const { setup, doStart } = embeddablePluginMock.createInstance(); + const uiActionsSetup = uiActionsPluginMock.createSetupContract(); - const editModeAction = new EditModeAction(); - setup.registerAction(editModeAction); - setup.attachAction(CONTEXT_MENU_TRIGGER, editModeAction.id); + const editModeAction = createEditModeAction(); + uiActionsSetup.registerAction(editModeAction); + uiActionsSetup.attachAction(CONTEXT_MENU_TRIGGER, editModeAction.id); setup.registerEmbeddableFactory( CONTACT_CARD_EMBEDDABLE, new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any) @@ -67,6 +70,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { inspector: {} as any, SavedObjectFinder: () => null, ExitFullScreenButton: () => null, + uiActions: {} as any, }; const container = new DashboardContainer(initialInput, options); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/kibana.json b/src/legacy/core_plugins/embeddable_api/public/np_ready/kibana.json index ff9ec7b2dbc62..54c1d1b640c98 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/kibana.json +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/kibana.json @@ -2,5 +2,9 @@ "id": "embeddable", "version": "kibana", "server": false, - "ui": true + "ui": true, + "requiredPlugins": [ + "embeddable", + "inspector" + ] } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/index.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/index.ts index b3fe77eb1e950..aec539330de9a 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/index.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/index.ts @@ -23,32 +23,16 @@ import { EmbeddableApi, EmbeddableDependenciesInternal, } from './types'; -import { attachAction } from './attach_action'; -import { detachAction } from './detach_action'; -import { executeTriggerActions } from './execute_trigger_actions'; import { getEmbeddableFactories } from './get_embeddable_factories'; import { getEmbeddableFactory } from './get_embeddable_factory'; -import { getTrigger } from './get_trigger'; -import { getTriggerActions } from './get_trigger_actions'; -import { getTriggerCompatibleActions } from './get_trigger_compatible_actions'; -import { registerAction } from './register_action'; import { registerEmbeddableFactory } from './register_embeddable_factory'; -import { registerTrigger } from './register_trigger'; export * from './types'; export const pureApi: EmbeddableApiPure = { - attachAction, - detachAction, - executeTriggerActions, getEmbeddableFactories, getEmbeddableFactory, - getTrigger, - getTriggerActions, - getTriggerCompatibleActions, - registerAction, registerEmbeddableFactory, - registerTrigger, }; export const createApi = (deps: EmbeddableDependencies) => { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/helpers.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/helpers.ts index 4889251c1a49c..be8e9a0dec3c2 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/helpers.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/helpers.ts @@ -21,8 +21,6 @@ import { EmbeddableDependencies } from '../types'; export const createDeps = (): EmbeddableDependencies => { const deps: EmbeddableDependencies = { - triggers: new Map(), - actions: new Map(), embeddableFactories: new Map(), }; return deps; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/registry.test.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/registry.test.ts index cfb51b78018d6..30a8a71d243f9 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/registry.test.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/registry.test.ts @@ -19,151 +19,6 @@ import { createApi } from '..'; import { createDeps } from './helpers'; -import { expectError } from '../../tests/helpers'; - -const HELLO_WORLD_ACTION_ID = 'HELLO_WORLD_ACTION_ID'; - -test('can register trigger', () => { - const deps = createDeps(); - const { api } = createApi(deps); - - api.registerTrigger({ - actionIds: [], - description: 'foo', - id: 'bar', - title: 'baz', - }); - - expect(deps.triggers.get('bar')).toEqual({ - actionIds: [], - description: 'foo', - id: 'bar', - title: 'baz', - }); -}); - -test('can register action', () => { - const deps = createDeps(); - const { api } = createApi(deps); - - api.registerAction({ - id: HELLO_WORLD_ACTION_ID, - order: 13, - } as any); - - expect(deps.actions.get(HELLO_WORLD_ACTION_ID)).toMatchObject({ - id: HELLO_WORLD_ACTION_ID, - order: 13, - }); -}); - -test('can attach an action to a trigger', () => { - const deps = createDeps(); - const { api } = createApi(deps); - const trigger = { - id: 'MY-TRIGGER', - actionIds: [], - }; - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - expect(trigger.actionIds).toEqual([]); - - api.registerTrigger(trigger); - api.registerAction(action); - api.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); - - expect(trigger.actionIds).toEqual([HELLO_WORLD_ACTION_ID]); -}); - -test('can detach an action to a trigger', () => { - const deps = createDeps(); - const { api } = createApi(deps); - const trigger = { - id: 'MY-TRIGGER', - actionIds: [], - }; - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - expect(trigger.actionIds).toEqual([]); - - api.registerTrigger(trigger); - api.registerAction(action); - api.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); - api.detachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); - - expect(trigger.actionIds).toEqual([]); -}); - -test('detaching an invalid action from a trigger throws an error', async () => { - const deps = createDeps(); - const { api } = createApi(deps); - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - api.registerAction(action); - const error = expectError(() => api.detachAction('i do not exist', HELLO_WORLD_ACTION_ID)); - - expect(error).toBeInstanceOf(Error); - expect(error.message).toMatchInlineSnapshot( - `"No trigger [triggerId = i do not exist] exists, for detaching action [actionId = HELLO_WORLD_ACTION_ID]."` - ); -}); - -test('attaching an invalid action to a trigger throws an error', async () => { - const deps = createDeps(); - const { api } = createApi(deps); - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - api.registerAction(action); - const error = expectError(() => api.attachAction('i do not exist', HELLO_WORLD_ACTION_ID)); - - expect(error).toBeInstanceOf(Error); - expect(error.message).toMatchInlineSnapshot( - `"No trigger [triggerId = i do not exist] exists, for attaching action [actionId = HELLO_WORLD_ACTION_ID]."` - ); -}); - -test('cannot register another action with the same ID', async () => { - const deps = createDeps(); - const { api } = createApi(deps); - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - api.registerAction(action); - const error = expectError(() => api.registerAction(action)); - - expect(error).toBeInstanceOf(Error); - expect(error.message).toMatchInlineSnapshot( - `"Action [action.id = HELLO_WORLD_ACTION_ID] already registered in Embeddables API."` - ); -}); - -test('cannot register another trigger with the same ID', async () => { - const deps = createDeps(); - const { api } = createApi(deps); - const trigger = { id: 'MY-TRIGGER' } as any; - - api.registerTrigger(trigger); - const error = expectError(() => api.registerTrigger(trigger)); - - expect(error).toBeInstanceOf(Error); - expect(error.message).toMatchInlineSnapshot( - `"Trigger [trigger.id = MY-TRIGGER] already registered in Embeddables API."` - ); -}); test('cannot register embeddable factory with the same ID', async () => { const deps = createDeps(); @@ -172,12 +27,7 @@ test('cannot register embeddable factory with the same ID', async () => { const embeddableFactory = {} as any; api.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory); - const error = expectError(() => - api.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory) - ); - - expect(error).toBeInstanceOf(Error); - expect(error.message).toMatchInlineSnapshot( - `"Embeddable factory [embeddableFactoryId = ID] already registered in Embeddables API."` + expect(() => api.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory)).toThrowError( + 'Embeddable factory [embeddableFactoryId = ID] already registered in Embeddables API.' ); }); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/types.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/types.ts index 18073d2b90083..30fa495785412 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/types.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/types.ts @@ -17,34 +17,18 @@ * under the License. */ -import { TriggerRegistry, ActionRegistry, EmbeddableFactoryRegistry } from '../types'; -import { - Trigger, - Action, - EmbeddableFactory, - ExecuteTriggerActions, - GetEmbeddableFactories, -} from '../lib'; +import { EmbeddableFactoryRegistry } from '../types'; +import { EmbeddableFactory, GetEmbeddableFactories } from '../lib'; export interface EmbeddableApi { - attachAction: (triggerId: string, actionId: string) => void; - detachAction: (triggerId: string, actionId: string) => void; - executeTriggerActions: ExecuteTriggerActions; getEmbeddableFactory: (embeddableFactoryId: string) => EmbeddableFactory; getEmbeddableFactories: GetEmbeddableFactories; - getTrigger: (id: string) => Trigger; - getTriggerActions: (id: string) => Action[]; - getTriggerCompatibleActions: (triggerId: string, context: C) => Promise>>; - registerAction: (action: Action) => void; // TODO: Make `registerEmbeddableFactory` receive only `factory` argument. registerEmbeddableFactory: (id: string, factory: EmbeddableFactory) => void; - registerTrigger: (trigger: Trigger) => void; } export interface EmbeddableDependencies { - actions: ActionRegistry; embeddableFactories: EmbeddableFactoryRegistry; - triggers: TriggerRegistry; } export interface EmbeddableDependenciesInternal extends EmbeddableDependencies { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/bootstrap.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/bootstrap.ts index 2ccc0bdb5f7af..aa000ed7798e9 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/bootstrap.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/bootstrap.ts @@ -17,11 +17,11 @@ * under the License. */ -import { EmbeddableApi } from './api/types'; +import { IUiActionsSetup } from 'src/plugins/ui_actions/public'; import { CONTEXT_MENU_TRIGGER, APPLY_FILTER_TRIGGER, - ApplyFilterAction, + createFilterAction, PANEL_BADGE_TRIGGER, } from './lib'; @@ -31,7 +31,7 @@ import { * * @param api */ -export const bootstrap = (api: EmbeddableApi) => { +export const bootstrap = (uiActions: IUiActionsSetup) => { const triggerContext = { id: CONTEXT_MENU_TRIGGER, title: 'Context menu', @@ -50,11 +50,11 @@ export const bootstrap = (api: EmbeddableApi) => { description: 'Actions appear in title bar when an embeddable loads in a panel', actionIds: [], }; - const actionApplyFilter = new ApplyFilterAction(); + const actionApplyFilter = createFilterAction(); - api.registerTrigger(triggerContext); - api.registerTrigger(triggerFilter); - api.registerAction(actionApplyFilter); - api.registerTrigger(triggerBadge); - api.attachAction(triggerFilter.id, actionApplyFilter.id); + uiActions.registerTrigger(triggerContext); + uiActions.registerTrigger(triggerFilter); + uiActions.registerAction(actionApplyFilter); + uiActions.registerTrigger(triggerBadge); + uiActions.attachAction(triggerFilter.id, actionApplyFilter.id); }; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/index.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/index.ts index 79bdd65f9cfcd..33855b07df7a1 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/index.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/index.ts @@ -25,10 +25,8 @@ export { APPLY_FILTER_ACTION, APPLY_FILTER_TRIGGER, PANEL_BADGE_TRIGGER, - Action, Adapters, AddPanelAction, - ApplyFilterAction, CONTEXT_MENU_TRIGGER, Container, ContainerInput, @@ -45,19 +43,15 @@ export { EmbeddableOutput, EmbeddablePanel, ErrorEmbeddable, - ExecuteTriggerActions, - GetActionsCompatibleWithTrigger, GetEmbeddableFactories, GetEmbeddableFactory, IContainer, IEmbeddable, - IncompatibleActionError, OutputSpec, PanelNotFoundError, PanelState, PropertySpec, SavedObjectMetaData, - Trigger, ViewMode, isErrorEmbeddable, openAddPanelFlyout, diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy.ts index 127c55713580e..00672edc20500 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy.ts @@ -24,5 +24,5 @@ import { npSetup, npStart } from 'ui/new_platform'; import { plugin } from '.'; const pluginInstance = plugin({} as any); -export const setup = pluginInstance.setup(npSetup.core); +export const setup = pluginInstance.setup(npSetup.core, { uiActions: npSetup.plugins.uiActions }); export const start = pluginInstance.start(npStart.core); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/apply_filter_action.test.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/apply_filter_action.test.ts index c14f258b5641c..a9e0e33cbb967 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/apply_filter_action.test.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/apply_filter_action.test.ts @@ -17,29 +17,23 @@ * under the License. */ -import { Action } from './action'; -import { ApplyFilterAction } from './apply_filter_action'; +import { createFilterAction } from './apply_filter_action'; import { expectError } from '../../tests/helpers'; -test('is instance of Action', () => { - const action = new ApplyFilterAction(); - expect(action).toBeInstanceOf(Action); -}); - test('has APPLY_FILTER_ACTION type and id', () => { - const action = new ApplyFilterAction(); + const action = createFilterAction(); expect(action.id).toBe('APPLY_FILTER_ACTION'); expect(action.type).toBe('APPLY_FILTER_ACTION'); }); test('has expected display name', () => { - const action = new ApplyFilterAction(); - expect(action.getDisplayName()).toMatchInlineSnapshot(`"Apply filter to current view"`); + const action = createFilterAction(); + expect(action.getDisplayName({} as any)).toMatchInlineSnapshot(`"Apply filter to current view"`); }); describe('isCompatible()', () => { - test('when embeddable filters and triggerContext filters exist, returns true', async () => { - const action = new ApplyFilterAction(); + test('when embeddable filters and filters exist, returns true', async () => { + const action = createFilterAction(); const result = await action.isCompatible({ embeddable: { getRoot: () => ({ @@ -54,7 +48,7 @@ describe('isCompatible()', () => { }); test('when embeddable filters not set, returns false', async () => { - const action = new ApplyFilterAction(); + const action = createFilterAction(); const result = await action.isCompatible({ embeddable: { getRoot: () => ({ @@ -68,8 +62,8 @@ describe('isCompatible()', () => { expect(result).toBe(false); }); - test('when triggerContext or triggerContext filters are not set, returns false', async () => { - const action = new ApplyFilterAction(); + test('when triggerContext or filters are not set, returns false', async () => { + const action = createFilterAction(); const result1 = await action.isCompatible({ embeddable: { @@ -98,9 +92,9 @@ const getEmbeddable = () => { }; describe('execute()', () => { - describe('when triggerContext not set', () => { + describe('when no filters are given', () => { test('throws an error', async () => { - const action = new ApplyFilterAction(); + const action = createFilterAction(); const error = expectError(() => action.execute({ embeddable: getEmbeddable(), @@ -109,8 +103,8 @@ describe('execute()', () => { expect(error).toBeInstanceOf(Error); }); - test('updates filter input on success', async done => { - const action = new ApplyFilterAction(); + test('updates filter input on success', async () => { + const action = createFilterAction(); const [embeddable, root] = getEmbeddable(); await action.execute({ @@ -122,23 +116,6 @@ describe('execute()', () => { expect(root.updateInput.mock.calls[0][0]).toMatchObject({ filters: ['FILTER'], }); - - done(); - }); - - test('checks if action isCompatible', async done => { - const action = new ApplyFilterAction(); - const spy = jest.spyOn(action, 'isCompatible'); - const [embeddable] = getEmbeddable(); - - await action.execute({ - embeddable, - filters: ['FILTER' as any], - }); - - expect(spy).toHaveBeenCalledTimes(1); - - done(); }); }); }); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/apply_filter_action.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/apply_filter_action.ts index 24549a64bfc23..8ca5b25703a4e 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/apply_filter_action.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/apply_filter_action.ts @@ -19,9 +19,12 @@ import { i18n } from '@kbn/i18n'; import { Filter } from '@kbn/es-query'; +import { + IAction, + createAction, + IncompatibleActionError, +} from '../../../../../../../../plugins/ui_actions/public'; import { IEmbeddable, EmbeddableInput } from '../embeddables'; -import { Action } from './action'; -import { IncompatibleActionError } from '../errors'; export const APPLY_FILTER_ACTION = 'APPLY_FILTER_ACTION'; @@ -31,40 +34,38 @@ interface ActionContext { filters: Filter[]; } -export class ApplyFilterAction extends Action { - public readonly type = APPLY_FILTER_ACTION; - - constructor() { - super(APPLY_FILTER_ACTION); - } - - public getDisplayName() { - return i18n.translate('embeddableApi.actions.applyFilterActionTitle', { - defaultMessage: 'Apply filter to current view', - }); - } - - public async isCompatible(context: ActionContext) { - if (context.embeddable === undefined) { - return false; - } - const root = context.embeddable.getRoot() as RootEmbeddable; - return Boolean(root.getInput().filters !== undefined && context.filters !== undefined); +async function isCompatible(context: ActionContext) { + if (context.embeddable === undefined) { + return false; } + const root = context.embeddable.getRoot() as RootEmbeddable; + return Boolean(root.getInput().filters !== undefined && context.filters !== undefined); +} - public async execute({ embeddable, filters }: ActionContext) { - if (!filters || !embeddable) { - throw new Error('Applying a filter requires a filter and embeddable as context'); - } +export function createFilterAction(): IAction { + return createAction({ + type: APPLY_FILTER_ACTION, + id: APPLY_FILTER_ACTION, + getDisplayName: () => { + return i18n.translate('embeddableApi.actions.applyFilterActionTitle', { + defaultMessage: 'Apply filter to current view', + }); + }, + isCompatible, + execute: async ({ embeddable, filters }) => { + if (!filters || !embeddable) { + throw new Error('Applying a filter requires a filter and embeddable as context'); + } - if (!(await this.isCompatible({ embeddable, filters }))) { - throw new IncompatibleActionError(); - } + if (!(await isCompatible({ embeddable, filters }))) { + throw new IncompatibleActionError(); + } - const root = embeddable.getRoot() as RootEmbeddable; + const root = embeddable.getRoot() as RootEmbeddable; - root.updateInput({ - filters, - }); - } + root.updateInput({ + filters, + }); + }, + }); } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/edit_panel_action.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/edit_panel_action.ts index 0b37491212810..1f3eb6355e247 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/edit_panel_action.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/edit_panel_action.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { Action } from './action'; +import { IAction } from 'src/plugins/ui_actions/public'; import { GetEmbeddableFactory, ViewMode } from '../types'; import { EmbeddableFactoryNotFoundError } from '../errors'; import { IEmbeddable } from '../embeddables'; @@ -29,12 +29,12 @@ interface ActionContext { embeddable: IEmbeddable; } -export class EditPanelAction extends Action { +export class EditPanelAction implements IAction { public readonly type = EDIT_PANEL_ACTION_ID; - constructor(private readonly getEmbeddableFactory: GetEmbeddableFactory) { - super(EDIT_PANEL_ACTION_ID); - this.order = 15; - } + public readonly id = EDIT_PANEL_ACTION_ID; + public order = 15; + + constructor(private readonly getEmbeddableFactory: GetEmbeddableFactory) {} public getDisplayName({ embeddable }: ActionContext) { const factory = this.getEmbeddableFactory(embeddable.type); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/index.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/index.ts index 7deace8345af9..ea32c6aa2d455 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/index.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/index.ts @@ -17,6 +17,5 @@ * under the License. */ -export { Action } from './action'; export * from './apply_filter_action'; export * from './edit_panel_action'; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/containers/embeddable_child_panel.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/containers/embeddable_child_panel.tsx index 5d316d59a536b..f441d84cfce85 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/containers/embeddable_child_panel.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/containers/embeddable_child_panel.tsx @@ -23,21 +23,19 @@ import React from 'react'; import { EuiLoadingChart } from '@elastic/eui'; import { Subscription } from 'rxjs'; import { CoreStart } from 'src/core/public'; +import { TGetActionsCompatibleWithTrigger } from 'src/plugins/ui_actions/public'; + import { ErrorEmbeddable, IEmbeddable } from '../embeddables'; import { EmbeddablePanel } from '../panel'; import { IContainer } from './i_container'; -import { - GetActionsCompatibleWithTrigger, - GetEmbeddableFactory, - GetEmbeddableFactories, -} from '../types'; +import { GetEmbeddableFactory, GetEmbeddableFactories } from '../types'; import { Start as InspectorStartContract } from '../../../../../../../../plugins/inspector/public'; export interface EmbeddableChildPanelProps { embeddableId: string; className?: string; container: IContainer; - getActions: GetActionsCompatibleWithTrigger; + getActions: TGetActionsCompatibleWithTrigger; getEmbeddableFactory: GetEmbeddableFactory; getAllEmbeddableFactories: GetEmbeddableFactories; overlays: CoreStart['overlays']; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/errors.test.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/errors.test.ts index c7830e48133aa..b972a9a22d289 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/errors.test.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/errors.test.ts @@ -17,11 +17,8 @@ * under the License. */ -import { - IncompatibleActionError, - PanelNotFoundError, - EmbeddableFactoryNotFoundError, -} from './errors'; +import { IncompatibleActionError } from 'src/plugins/ui_actions/public'; +import { PanelNotFoundError, EmbeddableFactoryNotFoundError } from './errors'; describe('IncompatibleActionError', () => { test('is instance of error', () => { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/errors.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/errors.ts index 3e1f357cd6023..5983f5e06a94a 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/errors.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/errors.ts @@ -20,18 +20,6 @@ /* eslint-disable max-classes-per-file */ import { i18n } from '@kbn/i18n'; -export class IncompatibleActionError extends Error { - code = 'INCOMPATIBLE_ACTION'; - - constructor() { - super( - i18n.translate('embeddableApi.errors.incompatibleAction', { - defaultMessage: 'Action is incompatible', - }) - ); - } -} - export class PanelNotFoundError extends Error { code = 'PANEL_NOT_FOUND'; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/index.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/index.ts index 94c26c252ef7d..172d9bdc9ec47 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/index.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/index.ts @@ -24,4 +24,3 @@ export * from './actions'; export * from './triggers'; export * from './containers'; export * from './panel'; -export * from './context_menu_actions'; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/embeddable_panel.test.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/embeddable_panel.test.tsx index 72e26434df095..ed3e4b2ccff30 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/embeddable_panel.test.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/embeddable_panel.test.tsx @@ -25,11 +25,11 @@ import { nextTick } from 'test_utils/enzyme_helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { I18nProvider } from '@kbn/i18n/react'; import { CONTEXT_MENU_TRIGGER } from '../triggers'; -import { Action } from '../actions'; +import { IAction, ITrigger } from 'src/plugins/ui_actions/public'; import { Trigger, GetEmbeddableFactory, ViewMode } from '../types'; import { EmbeddableFactory, isErrorEmbeddable } from '../embeddables'; import { EmbeddablePanel } from './embeddable_panel'; -import { EditModeAction } from '../test_samples/actions/edit_mode_action'; +import { createEditModeAction } from '../test_samples/actions'; import { ContactCardEmbeddableFactory, CONTACT_CARD_EMBEDDABLE, @@ -43,12 +43,12 @@ import { // eslint-disable-next-line import { inspectorPluginMock } from '../../../../../../../../plugins/inspector/public/mocks'; -const actionRegistry = new Map(); -const triggerRegistry = new Map(); +const actionRegistry = new Map(); +const triggerRegistry = new Map(); const embeddableFactories = new Map(); const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); -const editModeAction = new EditModeAction(); +const editModeAction = createEditModeAction(); const trigger: Trigger = { id: CONTEXT_MENU_TRIGGER, actionIds: [editModeAction.id], diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/embeddable_panel.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/embeddable_panel.tsx index 172a5a76c4b21..708d3e9695b07 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/embeddable_panel.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/embeddable_panel.tsx @@ -20,30 +20,29 @@ import { EuiContextMenuPanelDescriptor, EuiPanel } from '@elastic/eui'; import classNames from 'classnames'; import React from 'react'; import { Subscription } from 'rxjs'; +import { + buildContextMenuForActions, + TGetActionsCompatibleWithTrigger, + IAction, +} from '../../../../../../../../plugins/ui_actions/public'; import { CoreStart } from '../../../../../../../../core/public'; -import { buildContextMenuForActions } from '../context_menu_actions'; import { CONTEXT_MENU_TRIGGER, PANEL_BADGE_TRIGGER } from '../triggers'; import { IEmbeddable } from '../embeddables/i_embeddable'; -import { - ViewMode, - GetActionsCompatibleWithTrigger, - GetEmbeddableFactory, - GetEmbeddableFactories, -} from '../types'; +import { ViewMode, GetEmbeddableFactory, GetEmbeddableFactories } from '../types'; import { RemovePanelAction } from './panel_header/panel_actions'; import { AddPanelAction } from './panel_header/panel_actions/add_panel/add_panel_action'; import { CustomizePanelTitleAction } from './panel_header/panel_actions/customize_title/customize_panel_action'; import { PanelHeader } from './panel_header/panel_header'; import { InspectPanelAction } from './panel_header/panel_actions/inspect_panel_action'; -import { EditPanelAction, Action } from '../actions'; +import { EditPanelAction } from '../actions'; import { CustomizePanelModal } from './panel_header/panel_actions/customize_title/customize_panel_modal'; import { Start as InspectorStartContract } from '../../../../../../../../plugins/inspector/public'; interface Props { embeddable: IEmbeddable; - getActions: GetActionsCompatibleWithTrigger; + getActions: TGetActionsCompatibleWithTrigger; getEmbeddableFactory: GetEmbeddableFactory; getAllEmbeddableFactories: GetEmbeddableFactories; overlays: CoreStart['overlays']; @@ -58,7 +57,7 @@ interface State { viewMode: ViewMode; hidePanelTitles: boolean; closeContextMenu: boolean; - badges: Action[]; + badges: IAction[]; } export class EmbeddablePanel extends React.Component { @@ -211,7 +210,7 @@ export class EmbeddablePanel extends React.Component { // These actions are exposed on the context menu for every embeddable, they bypass the trigger // registry. - const extraActions = [ + const extraActions: Array> = [ new CustomizePanelTitleAction(createGetUserData(this.props.overlays)), new AddPanelAction( this.props.getEmbeddableFactory, @@ -225,8 +224,10 @@ export class EmbeddablePanel extends React.Component { new EditPanelAction(this.props.getEmbeddableFactory), ]; - const sorted = actions.concat(extraActions).sort((a: Action, b: Action) => { - return b.order - a.order; + const sorted = actions.concat(extraActions).sort((a: IAction, b: IAction) => { + const bOrder = b.order || 0; + const aOrder = a.order || 0; + return bOrder - aOrder; }); return await buildContextMenuForActions({ diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx index 1a3382497ade4..9799155f42d28 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx @@ -30,6 +30,7 @@ import { FilterableEmbeddableFactory } from '../../../../test_samples/embeddable import { FilterableContainer } from '../../../../test_samples/embeddables/filterable_container'; import { GetEmbeddableFactory } from '../../../../types'; import { coreMock } from '../../../../../../../../../../../core/public/mocks'; +import { ContactCardEmbeddable } from '../../../../test_samples'; const embeddableFactories = new Map(); embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); @@ -88,10 +89,7 @@ test('Is not compatible when container is in view mode', async () => { }); test('Is not compatible when embeddable is not a container', async () => { - expect( - // @ts-ignore - await action.isCompatible({ embeddable }) - ).toBe(false); + expect(await action.isCompatible({ embeddable } as any)).toBe(false); }); test('Is compatible when embeddable is a parent and in edit mode', async () => { @@ -102,13 +100,15 @@ test('Is compatible when embeddable is a parent and in edit mode', async () => { test('Execute throws an error when called with an embeddable that is not a container', async () => { async function check() { await action.execute({ - // @ts-ignore - embeddable: new ContactCardEmbeddable({ - firstName: 'sue', - id: '123', - viewMode: ViewMode.EDIT, - }), - }); + embeddable: new ContactCardEmbeddable( + { + firstName: 'sue', + id: '123', + viewMode: ViewMode.EDIT, + }, + {} as any + ), + } as any); } await expect(check()).rejects.toThrow(Error); }); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts index 7a49927e47251..e6db1a05ca238 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts @@ -17,8 +17,8 @@ * under the License. */ import { i18n } from '@kbn/i18n'; +import { IAction } from 'src/plugins/ui_actions/public'; import { ViewMode, GetEmbeddableFactory, GetEmbeddableFactories } from '../../../../types'; -import { Action } from '../../../../actions'; import { openAddPanelFlyout } from './open_add_panel_flyout'; import { NotificationsStart } from '../../../../../../../../../../../core/public'; import { KibanaReactOverlays } from '../../../../../../../../../../../plugins/kibana_react/public'; @@ -30,8 +30,9 @@ interface ActionContext { embeddable: IContainer; } -export class AddPanelAction extends Action { +export class AddPanelAction implements IAction { public readonly type = ADD_PANEL_ACTION_ID; + public readonly id = ADD_PANEL_ACTION_ID; constructor( private readonly getFactory: GetEmbeddableFactory, @@ -39,9 +40,7 @@ export class AddPanelAction extends Action { private readonly overlays: KibanaReactOverlays, private readonly notifications: NotificationsStart, private readonly SavedObjectFinder: React.ComponentType - ) { - super(ADD_PANEL_ACTION_ID); - } + ) {} public getDisplayName() { return i18n.translate('embeddableApi.addPanel.displayName', { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts index d91cee3048c1f..55aaca64d5db3 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { Action } from '../../../../actions'; +import { IAction } from 'src/plugins/ui_actions/public'; import { ViewMode } from '../../../../types'; import { IEmbeddable } from '../../../../embeddables'; @@ -30,11 +30,12 @@ interface ActionContext { embeddable: IEmbeddable; } -export class CustomizePanelTitleAction extends Action { +export class CustomizePanelTitleAction implements IAction { public readonly type = CUSTOMIZE_PANEL_ACTION_ID; + public id = CUSTOMIZE_PANEL_ACTION_ID; + public order = 10; constructor(private readonly getDataFromUser: GetUserData) { - super(CUSTOMIZE_PANEL_ACTION_ID); this.order = 10; } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/inspect_panel_action.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/inspect_panel_action.ts index 3e4e2a36d46b9..f951ad01cbf78 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/inspect_panel_action.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/inspect_panel_action.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { Action } from '../../../actions'; +import { IAction } from '../../../../../../../../../../plugins/ui_actions/public'; import { Start as InspectorStartContract } from '../../../../../../../../../../plugins/inspector/public'; import { IEmbeddable } from '../../../embeddables'; @@ -28,13 +28,12 @@ interface ActionContext { embeddable: IEmbeddable; } -export class InspectPanelAction extends Action { +export class InspectPanelAction implements IAction { public readonly type = INSPECT_PANEL_ACTION_ID; + public readonly id = INSPECT_PANEL_ACTION_ID; + public order = 10; - constructor(private readonly inspector: InspectorStartContract) { - super(INSPECT_PANEL_ACTION_ID); - this.order = 20; - } + constructor(private readonly inspector: InspectorStartContract) {} public getDisplayName() { return i18n.translate('embeddableApi.panel.inspectPanel.displayName', { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/remove_panel_action.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/remove_panel_action.ts index 2a61857d17f77..36322e684d58c 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/remove_panel_action.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_actions/remove_panel_action.ts @@ -17,10 +17,12 @@ * under the License. */ import { i18n } from '@kbn/i18n'; +import { + IAction, + IncompatibleActionError, +} from '../../../../../../../../../../plugins/ui_actions/public'; import { ContainerInput, IContainer } from '../../../containers'; import { ViewMode } from '../../../types'; -import { Action } from '../../../actions'; -import { IncompatibleActionError } from '../../../errors'; import { IEmbeddable } from '../../../embeddables'; export const REMOVE_PANEL_ACTION = 'deletePanel'; @@ -39,13 +41,12 @@ function hasExpandedPanelInput( return (container as IContainer<{}, ExpandedPanelInput>).getInput().expandedPanelId !== undefined; } -export class RemovePanelAction extends Action { +export class RemovePanelAction implements IAction { public readonly type = REMOVE_PANEL_ACTION; - constructor() { - super(REMOVE_PANEL_ACTION); + public readonly id = REMOVE_PANEL_ACTION; + public order = 5; - this.order = 5; - } + constructor() {} public getDisplayName() { return i18n.translate('embeddableApi.panel.removePanel.displayName', { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_header.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_header.tsx index 4f3d7bb0b6763..8f4a9b1097044 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_header.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/panel/panel_header/panel_header.tsx @@ -20,8 +20,8 @@ import { i18n } from '@kbn/i18n'; import { EuiContextMenuPanelDescriptor, EuiBadge, EuiIcon, EuiToolTip } from '@elastic/eui'; import classNames from 'classnames'; import React from 'react'; +import { IAction } from 'src/plugins/ui_actions/public'; import { PanelOptionsMenu } from './panel_options_menu'; -import { Action } from '../../actions'; import { IEmbeddable } from '../../embeddables'; import { VisualizeEmbeddable } from '../../../../../../../kibana/public/visualize/embeddable/visualize_embeddable'; import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../../kibana/public/visualize/embeddable/constants'; @@ -32,11 +32,11 @@ export interface PanelHeaderProps { hidePanelTitles: boolean; getActionContextMenuPanel: () => Promise; closeContextMenu: boolean; - badges: Action[]; + badges: IAction[]; embeddable: IEmbeddable; } -function renderBadges(badges: Action[], embeddable: IEmbeddable) { +function renderBadges(badges: IAction[], embeddable: IEmbeddable) { return badges.map(badge => ( { - public readonly type = EDIT_MODE_ACTION; - constructor() { - super(EDIT_MODE_ACTION); - } - - getDisplayName() { - return `I should only show up in edit mode`; - } - - async isCompatible(context: ActionContext) { - return context.embeddable.getInput().viewMode === ViewMode.EDIT; - } - - async execute() { - return; - } +export function createEditModeAction() { + return createAction({ + type: EDIT_MODE_ACTION, + getDisplayName: () => 'I only show up in edit mode', + isCompatible: async context => context.embeddable.getInput().viewMode === ViewMode.EDIT, + execute: async () => {}, + }); } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/index.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/index.ts index d4bf52bd87483..d1c58885f78f6 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/index.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/index.ts @@ -18,8 +18,5 @@ */ export * from './edit_mode_action'; -export * from './get_message_modal'; -export * from './hello_world_action'; -export * from './restricted_action'; export * from './say_hello_action'; export * from './send_message_action'; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/say_hello_action.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/say_hello_action.tsx index 1a884148498aa..69626dc31bab7 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/say_hello_action.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/say_hello_action.tsx @@ -17,9 +17,11 @@ * under the License. */ -import { Action } from '../../actions'; +import { + IAction, + IncompatibleActionError, +} from '../../../../../../../../../plugins/ui_actions/public'; import { EmbeddableInput, Embeddable, EmbeddableOutput, IEmbeddable } from '../../embeddables'; -import { IncompatibleActionError } from '../../errors'; export const SAY_HELLO_ACTION = 'SAY_HELLO_ACTION'; @@ -41,14 +43,15 @@ interface ActionContext { message?: string; } -export class SayHelloAction extends Action { +export class SayHelloAction implements IAction { public readonly type = SAY_HELLO_ACTION; + public readonly id = SAY_HELLO_ACTION; + private sayHello: (name: string) => void; // Taking in a function, instead of always directly interacting with the dom, // can make testing the execute part of the action easier. constructor(sayHello: (name: string) => void) { - super(SAY_HELLO_ACTION); this.sayHello = sayHello; } @@ -56,6 +59,10 @@ export class SayHelloAction extends Action { return 'Say hello'; } + getIconType() { + return undefined; + } + // Can use typescript generics to get compiler time warnings for immediate feedback if // the context is not compatible. async isCompatible(context: ActionContext) { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/send_message_action.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/send_message_action.tsx index 0f54f80398f20..23f4029b9fcde 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/send_message_action.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/send_message_action.tsx @@ -18,7 +18,10 @@ */ import React from 'react'; import { EuiFlyoutBody } from '@elastic/eui'; -import { Action, IncompatibleActionError } from '../..'; +import { + createAction, + IncompatibleActionError, +} from '../../../../../../../../../plugins/ui_actions/public'; import { Embeddable, EmbeddableInput } from '../../embeddables'; import { GetMessageModal } from './get_message_modal'; import { FullNameEmbeddableOutput, hasFullNameOutput } from './say_hello_action'; @@ -30,41 +33,35 @@ interface ActionContext { embeddable: Embeddable; message: string; } -export class SendMessageAction extends Action { - public readonly type = SEND_MESSAGE_ACTION; - constructor(private readonly overlays: CoreStart['overlays']) { - super(SEND_MESSAGE_ACTION); - } +const isCompatible = async (context: ActionContext) => hasFullNameOutput(context.embeddable); - getDisplayName() { - return 'Send message'; - } - - async isCompatible(context: ActionContext) { - return hasFullNameOutput(context.embeddable); - } - - async sendMessage(context: ActionContext, message: string) { +export function createSendMessageAction(overlays: CoreStart['overlays']) { + const sendMessage = async (context: ActionContext, message: string) => { const greeting = `Hello, ${context.embeddable.getOutput().fullName}`; const content = message ? `${greeting}. ${message}` : greeting; - this.overlays.openFlyout({content}); - } + overlays.openFlyout({content}); + }; - async execute(context: ActionContext) { - if (!(await this.isCompatible(context))) { - throw new IncompatibleActionError(); - } + return createAction({ + type: SEND_MESSAGE_ACTION, + getDisplayName: () => 'Send message', + isCompatible, + execute: async context => { + if (!(await isCompatible(context))) { + throw new IncompatibleActionError(); + } - const modal = this.overlays.openModal( - modal.close()} - onDone={message => { - modal.close(); - this.sendMessage(context, message); - }} - /> - ); - } + const modal = overlays.openModal( + modal.close()} + onDone={message => { + modal.close(); + sendMessage(context, message); + }} + /> + ); + }, + }); } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card.tsx index 7d337ce195c4f..a83364f22021a 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card.tsx @@ -28,12 +28,12 @@ import { import { Subscription } from 'rxjs'; import { EuiButton } from '@elastic/eui'; import * as Rx from 'rxjs'; +import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; import { ContactCardEmbeddable, CONTACT_USER_TRIGGER } from './contact_card_embeddable'; -import { ExecuteTriggerActions } from '../../../types'; interface Props { embeddable: ContactCardEmbeddable; - execTrigger: ExecuteTriggerActions; + execTrigger: TExecuteTriggerActions; } interface State { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable.tsx index b11772cca0d70..de1befd1bdc1b 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable.tsx @@ -19,11 +19,11 @@ import React from 'react'; import ReactDom from 'react-dom'; import { Subscription } from 'rxjs'; +import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; import { Container } from '../../../containers'; import { EmbeddableOutput, Embeddable, EmbeddableInput } from '../../../embeddables'; import { CONTACT_CARD_EMBEDDABLE } from './contact_card_embeddable_factory'; import { ContactCardEmbeddableComponent } from './contact_card'; -import { ExecuteTriggerActions } from '../../../types'; export interface ContactCardEmbeddableInput extends EmbeddableInput { firstName: string; @@ -37,7 +37,7 @@ export interface ContactCardEmbeddableOutput extends EmbeddableOutput { } export interface ContactCardEmbeddableOptions { - execAction: ExecuteTriggerActions; + execAction: TExecuteTriggerActions; } function getFullName(input: ContactCardEmbeddableInput) { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx index 38ecbccf854bc..a4e3218560bdc 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx @@ -19,12 +19,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; + import { EmbeddableFactory } from '../../../embeddables'; import { Container } from '../../../containers'; import { ContactCardEmbeddable, ContactCardEmbeddableInput } from './contact_card_embeddable'; import { ContactCardInitializer } from './contact_card_initializer'; import { EmbeddableFactoryOptions } from '../../../embeddables/embeddable_factory'; -import { ExecuteTriggerActions } from '../../../types'; import { CoreStart } from '../../../../../../../../../../core/public'; export const CONTACT_CARD_EMBEDDABLE = 'CONTACT_CARD_EMBEDDABLE'; @@ -34,7 +35,7 @@ export class ContactCardEmbeddableFactory extends EmbeddableFactory, - private readonly execTrigger: ExecuteTriggerActions, + private readonly execTrigger: TExecuteTriggerActions, private readonly overlays: CoreStart['overlays'] ) { super(options); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/slow_contact_card_embeddable_factory.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/slow_contact_card_embeddable_factory.ts index b77e5b31efc84..84806ff9cfde5 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/slow_contact_card_embeddable_factory.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/contact_card/slow_contact_card_embeddable_factory.ts @@ -17,13 +17,13 @@ * under the License. */ +import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; import { Container, EmbeddableFactory } from '../../..'; import { ContactCardEmbeddable, ContactCardEmbeddableInput } from './contact_card_embeddable'; import { CONTACT_CARD_EMBEDDABLE } from './contact_card_embeddable_factory'; -import { ExecuteTriggerActions } from '../../../types'; interface SlowContactCardEmbeddableFactoryOptions { - execAction: ExecuteTriggerActions; + execAction: TExecuteTriggerActions; loadTickCount?: number; } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/filterable_container.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/filterable_container.tsx index 7e3464aef099a..aaa9ed323eee0 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/filterable_container.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/filterable_container.tsx @@ -54,6 +54,7 @@ export class FilterableContainer extends Container< public getInheritedInput() { return { filters: this.input.filters, + viewMode: this.input.viewMode, }; } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/hello_world_container.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/hello_world_container.tsx index 366082e770eb2..2a7406f49ad7e 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/hello_world_container.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/hello_world_container.tsx @@ -20,13 +20,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; import { CoreStart } from 'src/core/public'; +import { TGetActionsCompatibleWithTrigger } from 'src/plugins/ui_actions/public'; import { Container, ViewMode, ContainerInput } from '../..'; import { HelloWorldContainerComponent } from './hello_world_container_component'; -import { - GetEmbeddableFactory, - GetActionsCompatibleWithTrigger, - GetEmbeddableFactories, -} from '../../types'; +import { GetEmbeddableFactory, GetEmbeddableFactories } from '../../types'; import { Start as InspectorStartContract } from '../../../../../../../../../plugins/inspector/public'; export const HELLO_WORLD_CONTAINER = 'HELLO_WORLD_CONTAINER'; @@ -48,7 +45,7 @@ interface HelloWorldContainerInput extends ContainerInput { } interface HelloWorldContainerOptions { - getActions: GetActionsCompatibleWithTrigger; + getActions: TGetActionsCompatibleWithTrigger; getEmbeddableFactory: GetEmbeddableFactory; getAllEmbeddableFactories: GetEmbeddableFactories; overlays: CoreStart['overlays']; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/hello_world_container_component.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/hello_world_container_component.tsx index 61d0e875726c1..9f09d7058744f 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/hello_world_container_component.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/hello_world_container_component.tsx @@ -21,17 +21,14 @@ import { Subscription } from 'rxjs'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { CoreStart } from 'src/core/public'; +import { TGetActionsCompatibleWithTrigger } from 'src/plugins/ui_actions/public'; import { IContainer, PanelState, EmbeddableChildPanel } from '../..'; -import { - GetActionsCompatibleWithTrigger, - GetEmbeddableFactory, - GetEmbeddableFactories, -} from '../../types'; +import { GetEmbeddableFactory, GetEmbeddableFactories } from '../../types'; import { Start as InspectorStartContract } from '../../../../../../../../../plugins/inspector/public'; interface Props { container: IContainer; - getActions: GetActionsCompatibleWithTrigger; + getActions: TGetActionsCompatibleWithTrigger; getEmbeddableFactory: GetEmbeddableFactory; getAllEmbeddableFactories: GetEmbeddableFactories; overlays: CoreStart['overlays']; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/types.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/types.ts index 925afe07ff539..5088bddaa78d7 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/types.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/types.ts @@ -17,7 +17,6 @@ * under the License. */ -import { Action } from './actions'; import { EmbeddableFactory } from './embeddables/embeddable_factory'; import { Adapters } from '../../../../../../../plugins/inspector/public'; @@ -53,10 +52,5 @@ export interface SavedObjectMetaData { showSavedObject?(savedObject: any): any; } -export type ExecuteTriggerActions = (triggerId: string, actionContext: A) => Promise; -export type GetActionsCompatibleWithTrigger = ( - triggerId: string, - context: C -) => Promise; export type GetEmbeddableFactory = (id: string) => EmbeddableFactory | undefined; export type GetEmbeddableFactories = () => IterableIterator; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/mocks.ts index fb2507690550a..c24f310665ae1 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/mocks.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/mocks.ts @@ -20,39 +20,33 @@ import { Plugin } from '.'; import { coreMock } from '../../../../../../core/public/mocks'; +// eslint-disable-next-line +import { uiActionsPluginMock } from '../../../../../../plugins/ui_actions/public/mocks'; + export type Setup = jest.Mocked>; export type Start = jest.Mocked>; const createSetupContract = (): Setup => { const setupContract: Setup = { - attachAction: jest.fn(), - registerAction: jest.fn(), registerEmbeddableFactory: jest.fn(), - registerTrigger: jest.fn(), }; return setupContract; }; const createStartContract = (): Start => { const startContract: Start = { - attachAction: jest.fn(), - registerAction: jest.fn(), registerEmbeddableFactory: jest.fn(), - registerTrigger: jest.fn(), - detachAction: jest.fn(), - executeTriggerActions: jest.fn(), getEmbeddableFactories: jest.fn(), getEmbeddableFactory: jest.fn(), - getTrigger: jest.fn(), - getTriggerActions: jest.fn(), - getTriggerCompatibleActions: jest.fn(), }; return startContract; }; const createInstance = () => { const plugin = new Plugin({} as any); - const setup = plugin.setup(coreMock.createSetup()); + const setup = plugin.setup(coreMock.createSetup(), { + uiActions: uiActionsPluginMock.createSetupContract(), + }); const doStart = () => plugin.start(coreMock.createStart()); return { plugin, diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/plugin.ts index c210165719bee..0e4a34da40c75 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/plugin.ts @@ -17,39 +17,37 @@ * under the License. */ +import { IUiActionsSetup } from 'src/plugins/ui_actions/public'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, } from '../../../../../../core/public'; -import { TriggerRegistry, ActionRegistry, EmbeddableFactoryRegistry } from './types'; +import { EmbeddableFactoryRegistry } from './types'; import { createApi, EmbeddableApi } from './api'; import { bootstrap } from './bootstrap'; +export interface IEmbeddableSetupDependencies { + uiActions: IUiActionsSetup; +} + export class EmbeddablePublicPlugin implements Plugin { - private readonly triggers: TriggerRegistry = new Map(); - private readonly actions: ActionRegistry = new Map(); private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map(); private api!: EmbeddableApi; constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup) { + public setup(core: CoreSetup, { uiActions }: IEmbeddableSetupDependencies) { ({ api: this.api } = createApi({ - actions: this.actions, embeddableFactories: this.embeddableFactories, - triggers: this.triggers, })); - bootstrap(this.api); + bootstrap(uiActions); - const { registerTrigger, registerAction, registerEmbeddableFactory, attachAction } = this.api; + const { registerEmbeddableFactory } = this.api; return { - registerTrigger, - registerAction, registerEmbeddableFactory, - attachAction, }; } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/apply_filter_action.test.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/apply_filter_action.test.ts index 6e5d2182b7063..3273b335b5074 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/apply_filter_action.test.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/apply_filter_action.test.ts @@ -18,7 +18,7 @@ */ import { testPlugin } from './test_plugin'; -import { ApplyFilterAction, EmbeddableOutput, isErrorEmbeddable } from '../lib'; +import { EmbeddableOutput, isErrorEmbeddable, createFilterAction } from '../lib'; import { FilterableContainer, FilterableContainerInput, @@ -44,7 +44,7 @@ test('ApplyFilterAction applies the filter to the root of the container tree', a api.registerEmbeddableFactory(factory1.type, factory1); api.registerEmbeddableFactory(factory2.type, factory2); - const applyFilterAction = new ApplyFilterAction(); + const applyFilterAction = createFilterAction(); const root = new FilterableContainer( { id: 'root', panels: {}, filters: [] }, @@ -97,11 +97,11 @@ test('ApplyFilterAction is incompatible if the root container does not accept a const api = doStart(); const inspector = inspectorPluginMock.createStartContract(); - const applyFilterAction = new ApplyFilterAction(); + const applyFilterAction = createFilterAction(); const parent = new HelloWorldContainer( { id: 'root', panels: {} }, { - getActions: api.getTriggerCompatibleActions, + getActions: () => Promise.resolve([]), getEmbeddableFactory: api.getEmbeddableFactory, getAllEmbeddableFactories: api.getEmbeddableFactories, overlays: coreStart.overlays, @@ -136,11 +136,11 @@ test('trying to execute on incompatible context throws an error ', async () => { const factory = new FilterableEmbeddableFactory(); api.registerEmbeddableFactory(factory.type, factory); - const applyFilterAction = new ApplyFilterAction(); + const applyFilterAction = createFilterAction(); const parent = new HelloWorldContainer( { id: 'root', panels: {} }, { - getActions: api.getTriggerCompatibleActions, + getActions: () => Promise.resolve([]), getEmbeddableFactory: api.getEmbeddableFactory, getAllEmbeddableFactories: api.getEmbeddableFactories, overlays: coreStart.overlays, @@ -161,13 +161,12 @@ test('trying to execute on incompatible context throws an error ', async () => { } async function check() { - // @ts-ignore - await applyFilterAction.execute({ embeddable }); + await applyFilterAction.execute({ embeddable } as any); } await expect(check()).rejects.toThrow(Error); }); test('gets title', async () => { - const applyFilterAction = new ApplyFilterAction(); - expect(applyFilterAction.getDisplayName()).toBeDefined(); + const applyFilterAction = createFilterAction(); + expect(applyFilterAction.getDisplayName({} as any)).toBeDefined(); }); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/container.test.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/container.test.ts index 5e6cda9198d27..8cfd627e34f87 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/container.test.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/container.test.ts @@ -53,12 +53,12 @@ async function creatHelloWorldContainerAndEmbeddable( ) { const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); - const { setup, doStart } = testPlugin(coreSetup, coreStart); + const { setup, doStart, uiActions } = testPlugin(coreSetup, coreStart); const start = doStart(); const filterableFactory = new FilterableEmbeddableFactory(); const slowContactCardFactory = new SlowContactCardEmbeddableFactory({ - execAction: start.executeTriggerActions, + execAction: uiActions.executeTriggerActions, }); const helloWorldFactory = new HelloWorldEmbeddableFactory(); @@ -67,7 +67,7 @@ async function creatHelloWorldContainerAndEmbeddable( setup.registerEmbeddableFactory(helloWorldFactory.type, helloWorldFactory); const container = new HelloWorldContainer(containerInput, { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getEmbeddableFactory: start.getEmbeddableFactory, getAllEmbeddableFactories: start.getEmbeddableFactories, overlays: coreStart.overlays, @@ -85,7 +85,7 @@ async function creatHelloWorldContainerAndEmbeddable( throw new Error('Error adding embeddable'); } - return { container, embeddable, coreSetup, coreStart, setup, start }; + return { container, embeddable, coreSetup, coreStart, setup, start, uiActions }; } test('Container initializes embeddables', async done => { @@ -128,7 +128,7 @@ test('Container.addNewEmbeddable', async () => { }); test('Container.removeEmbeddable removes and cleans up', async done => { - const { start, coreStart } = await creatHelloWorldContainerAndEmbeddable(); + const { start, coreStart, uiActions } = await creatHelloWorldContainerAndEmbeddable(); const container = new HelloWorldContainer( { id: 'hello', @@ -140,7 +140,7 @@ test('Container.removeEmbeddable removes and cleans up', async done => { }, }, { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getEmbeddableFactory: start.getEmbeddableFactory, getAllEmbeddableFactories: start.getEmbeddableFactories, overlays: coreStart.overlays, @@ -291,7 +291,13 @@ test('Container view mode change propagates to children', async () => { }); test(`Container updates its state when a child's input is updated`, async done => { - const { container, embeddable, start, coreStart } = await creatHelloWorldContainerAndEmbeddable( + const { + container, + embeddable, + start, + coreStart, + uiActions, + } = await creatHelloWorldContainerAndEmbeddable( { id: 'hello', panels: {}, viewMode: ViewMode.VIEW }, { id: '123', @@ -314,7 +320,7 @@ test(`Container updates its state when a child's input is updated`, async done = // with "Dr.", not the default the embeddable was first added with. Makes sure changed input // is preserved with the container. const containerClone = new HelloWorldContainer(container.getInput(), { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getAllEmbeddableFactories: start.getEmbeddableFactories, getEmbeddableFactory: start.getEmbeddableFactory, notifications: coreStart.notifications, @@ -554,7 +560,7 @@ test('Panel added to input state', async () => { test('Container changes made directly after adding a new embeddable are propagated', async done => { const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); - const { doStart } = testPlugin(coreSetup, coreStart); + const { doStart, uiActions } = testPlugin(coreSetup, coreStart); const start = doStart(); const container = new HelloWorldContainer( @@ -564,7 +570,7 @@ test('Container changes made directly after adding a new embeddable are propagat viewMode: ViewMode.EDIT, }, { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getEmbeddableFactory: start.getEmbeddableFactory, getAllEmbeddableFactories: start.getEmbeddableFactories, overlays: coreStart.overlays, @@ -576,7 +582,7 @@ test('Container changes made directly after adding a new embeddable are propagat const factory = new SlowContactCardEmbeddableFactory({ loadTickCount: 3, - execAction: start.executeTriggerActions, + execAction: uiActions.executeTriggerActions, }); start.registerEmbeddableFactory(factory.type, factory); @@ -685,7 +691,10 @@ test('untilEmbeddableLoaded() throws an error if there is no such child panel in }); test('untilEmbeddableLoaded() throws an error if there is no such child panel in the container - 2', async () => { - const { doStart, coreStart } = testPlugin(coreMock.createSetup(), coreMock.createStart()); + const { doStart, coreStart, uiActions } = testPlugin( + coreMock.createSetup(), + coreMock.createStart() + ); const start = doStart(); const container = new HelloWorldContainer( { @@ -693,7 +702,7 @@ test('untilEmbeddableLoaded() throws an error if there is no such child panel in panels: {}, }, { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getEmbeddableFactory: start.getEmbeddableFactory, getAllEmbeddableFactories: start.getEmbeddableFactories, overlays: coreStart.overlays, @@ -709,7 +718,10 @@ test('untilEmbeddableLoaded() throws an error if there is no such child panel in }); test('untilEmbeddableLoaded() resolves if child is loaded in the container', async done => { - const { setup, doStart, coreStart } = testPlugin(coreMock.createSetup(), coreMock.createStart()); + const { setup, doStart, coreStart, uiActions } = testPlugin( + coreMock.createSetup(), + coreMock.createStart() + ); const factory = new HelloWorldEmbeddableFactory(); setup.registerEmbeddableFactory(factory.type, factory); const start = doStart(); @@ -724,7 +736,7 @@ test('untilEmbeddableLoaded() resolves if child is loaded in the container', asy }, }, { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getEmbeddableFactory: start.getEmbeddableFactory, getAllEmbeddableFactories: start.getEmbeddableFactories, overlays: coreStart.overlays, @@ -741,11 +753,14 @@ test('untilEmbeddableLoaded() resolves if child is loaded in the container', asy }); test('untilEmbeddableLoaded rejects with an error if child is subsequently removed', async done => { - const { doStart, coreStart } = testPlugin(coreMock.createSetup(), coreMock.createStart()); + const { doStart, coreStart, uiActions } = testPlugin( + coreMock.createSetup(), + coreMock.createStart() + ); const start = doStart(); const factory = new SlowContactCardEmbeddableFactory({ loadTickCount: 3, - execAction: start.executeTriggerActions, + execAction: uiActions.executeTriggerActions, }); start.registerEmbeddableFactory(factory.type, factory); const container = new HelloWorldContainer( @@ -759,7 +774,7 @@ test('untilEmbeddableLoaded rejects with an error if child is subsequently remov }, }, { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getEmbeddableFactory: start.getEmbeddableFactory, getAllEmbeddableFactories: start.getEmbeddableFactories, overlays: coreStart.overlays, @@ -778,11 +793,14 @@ test('untilEmbeddableLoaded rejects with an error if child is subsequently remov }); test('adding a panel then subsequently removing it before its loaded removes the panel', async done => { - const { doStart, coreStart } = testPlugin(coreMock.createSetup(), coreMock.createStart()); + const { doStart, coreStart, uiActions } = testPlugin( + coreMock.createSetup(), + coreMock.createStart() + ); const start = doStart(); const factory = new SlowContactCardEmbeddableFactory({ loadTickCount: 1, - execAction: start.executeTriggerActions, + execAction: uiActions.executeTriggerActions, }); start.registerEmbeddableFactory(factory.type, factory); const container = new HelloWorldContainer( @@ -796,7 +814,7 @@ test('adding a panel then subsequently removing it before its loaded removes the }, }, { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getEmbeddableFactory: start.getEmbeddableFactory, getAllEmbeddableFactories: start.getEmbeddableFactories, overlays: coreStart.overlays, diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/customize_panel_modal.test.tsx b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/customize_panel_modal.test.tsx index 3de6c2a2c1497..c3375d9de13bc 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/customize_panel_modal.test.tsx +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/customize_panel_modal.test.tsx @@ -42,12 +42,15 @@ let container: Container; let embeddable: ContactCardEmbeddable; beforeEach(async () => { - const { doStart, coreStart } = testPlugin(coreMock.createSetup(), coreMock.createStart()); + const { doStart, coreStart, uiActions } = testPlugin( + coreMock.createSetup(), + coreMock.createStart() + ); api = doStart(); const contactCardFactory = new ContactCardEmbeddableFactory( {}, - api.executeTriggerActions, + uiActions.executeTriggerActions, {} as any ); api.registerEmbeddableFactory(contactCardFactory.type, contactCardFactory); @@ -55,7 +58,7 @@ beforeEach(async () => { container = new HelloWorldContainer( { id: '123', panels: {} }, { - getActions: api.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getEmbeddableFactory: api.getEmbeddableFactory, getAllEmbeddableFactories: api.getEmbeddableFactories, overlays: coreStart.overlays, diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/explicit_input.test.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/explicit_input.test.ts index 30a0670bb294d..08009c165cbce 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/explicit_input.test.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/explicit_input.test.ts @@ -35,12 +35,16 @@ import { HelloWorldContainer } from '../lib/test_samples/embeddables/hello_world // eslint-disable-next-line import { coreMock } from '../../../../../../../core/public/mocks'; -const { setup, doStart, coreStart } = testPlugin(coreMock.createSetup(), coreMock.createStart()); +const { setup, doStart, coreStart, uiActions } = testPlugin( + coreMock.createSetup(), + coreMock.createStart() +); const start = doStart(); + setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); const factory = new SlowContactCardEmbeddableFactory({ loadTickCount: 2, - execAction: start.executeTriggerActions, + execAction: uiActions.executeTriggerActions, }); setup.registerEmbeddableFactory(CONTACT_CARD_EMBEDDABLE, factory); setup.registerEmbeddableFactory(HELLO_WORLD_EMBEDDABLE_TYPE, new HelloWorldEmbeddableFactory()); @@ -76,7 +80,7 @@ test('Explicit embeddable input mapped to undefined with no inherited value will const container = new HelloWorldContainer( { id: 'hello', panels: {} }, { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getAllEmbeddableFactories: start.getEmbeddableFactories, getEmbeddableFactory: start.getEmbeddableFactory, notifications: coreStart.notifications, @@ -124,7 +128,7 @@ test('Explicit input tests in async situations', (done: () => void) => { }, }, { - getActions: start.getTriggerCompatibleActions, + getActions: uiActions.getTriggerCompatibleActions, getAllEmbeddableFactories: start.getEmbeddableFactories, getEmbeddableFactory: start.getEmbeddableFactory, notifications: coreStart.notifications, diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/test_plugin.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/test_plugin.ts index 108fd5511c39c..5b50bddefcdb7 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/test_plugin.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/test_plugin.ts @@ -18,6 +18,9 @@ */ import { CoreSetup, CoreStart } from 'src/core/public'; +// eslint-disable-next-line +import { uiActionsTestPlugin } from 'src/plugins/ui_actions/public/tests'; +import { IUiActionsApi } from 'src/plugins/ui_actions/public'; import { EmbeddablePublicPlugin } from '../plugin'; export interface TestPluginReturn { @@ -26,15 +29,17 @@ export interface TestPluginReturn { coreStart: CoreStart; setup: ReturnType; doStart: (anotherCoreStart?: CoreStart) => ReturnType; + uiActions: IUiActionsApi; } export const testPlugin = ( coreSetup: CoreSetup = {} as CoreSetup, coreStart: CoreStart = {} as CoreStart ): TestPluginReturn => { + const uiActions = uiActionsTestPlugin(coreSetup, coreStart); const initializerContext = {} as any; const plugin = new EmbeddablePublicPlugin(initializerContext); - const setup = plugin.setup(coreSetup); + const setup = plugin.setup(coreSetup, { uiActions: uiActions.setup }); return { plugin, @@ -45,5 +50,6 @@ export const testPlugin = ( const start = plugin.start(anotherCoreStart); return start; }, + uiActions: uiActions.doStart(coreStart), }; }; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/types.ts b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/types.ts index 545ca557b4cd8..7a879c389f3c4 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/types.ts +++ b/src/legacy/core_plugins/embeddable_api/public/np_ready/public/types.ts @@ -17,10 +17,6 @@ * under the License. */ -import { Action } from './lib/actions'; import { EmbeddableFactory } from './lib/embeddables'; -import { Trigger } from './lib/types'; -export type TriggerRegistry = Map; -export type ActionRegistry = Map; export type EmbeddableFactoryRegistry = Map; diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts index e307d5da7ebe8..adee1c2c5eb54 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts @@ -37,12 +37,12 @@ import { i18n } from '@kbn/i18n'; import { toastNotifications } from 'ui/notify'; import { timefilter, getTime } from 'ui/timefilter'; import { TimeRange } from 'src/plugins/data/public'; +import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; import { Query, onlyDisabledFiltersChanged } from '../../../../data/public'; import { APPLY_FILTER_TRIGGER, Embeddable, Container, - ExecuteTriggerActions, } from '../../../../embeddable_api/public/np_ready/public'; import * as columnActions from '../doc_table/actions/columns'; import { SavedSearch } from '../types'; @@ -124,7 +124,7 @@ export class SearchEmbeddable extends Embeddable queryFilter, }: SearchEmbeddableConfig, initialInput: SearchInput, - private readonly executeTriggerActions: ExecuteTriggerActions, + private readonly executeTriggerActions: TExecuteTriggerActions, parent?: Container ) { super( diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts index 982e56731bacb..ca106711e3c07 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable_factory.ts @@ -19,18 +19,19 @@ import '../doc_table'; import { capabilities } from 'ui/capabilities'; +import { npStart } from 'ui/new_platform'; import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; import { IPrivate } from 'ui/private'; import { TimeRange } from 'src/plugins/data/public'; import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter'; +import { TExecuteTriggerActions } from 'src/plugins/ui_actions/public'; import { EmbeddableFactory, ErrorEmbeddable, Container, - ExecuteTriggerActions, } from '../../../../embeddable_api/public/np_ready/public'; -import { setup, start } from '../../../../embeddable_api/public/np_ready/public/legacy'; +import { setup } from '../../../../embeddable_api/public/np_ready/public/legacy'; import { SavedSearchLoader } from '../types'; import { SearchEmbeddable, SEARCH_EMBEDDABLE_TYPE } from './search_embeddable'; import { SearchInput, SearchOutput } from './types'; @@ -42,7 +43,7 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< > { public readonly type = SEARCH_EMBEDDABLE_TYPE; - constructor(private readonly executeTriggerActions: ExecuteTriggerActions) { + constructor(private readonly executeTriggerActions: TExecuteTriggerActions) { super({ savedObjectMetaData: { name: i18n.translate('kbn.discover.savedSearch.savedObjectName', { @@ -110,5 +111,5 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< } } -const factory = new SearchEmbeddableFactory(start.executeTriggerActions); +const factory = new SearchEmbeddableFactory(npStart.plugins.uiActions.executeTriggerActions); setup.registerEmbeddableFactory(factory.type, factory); diff --git a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts index 48d5dc178ff31..147cbed6b1ea1 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable.ts @@ -36,8 +36,6 @@ import { EmbeddableOutput, Embeddable, Container, - APPLY_FILTER_TRIGGER, - Trigger, } from '../../../../embeddable_api/public/np_ready/public'; import { Query, onlyDisabledFiltersChanged } from '../../../../data/public'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; @@ -130,10 +128,6 @@ export class VisualizeEmbeddable extends Embeddable ({ data: dataPluginMock.createStartContract(), inspector: inspectorPluginMock.createStartContract(), expressions: expressionsPluginMock.createStartContract(), + uiActions: uiActionsPluginMock.createStartContract(), }), }; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 4c8ee74de6d9d..b8a6a44f9624e 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -39,6 +39,11 @@ export const npSetup = { }, }, }, + uiActions: { + attachAction: sinon.fake(), + registerAction: sinon.fake(), + registerTrigger: sinon.fake(), + }, }, }; @@ -60,6 +65,16 @@ export const npStart = { close: () => Promise.resolve(undefined), }), }, + uiActions: { + attachAction: sinon.fake(), + registerAction: sinon.fake(), + registerTrigger: sinon.fake(), + detachAction: sinon.fake(), + executeTriggerActions: sinon.fake(), + getTrigger: sinon.fake(), + getTriggerActions: sinon.fake(), + getTriggerCompatibleActions: sinon.fake(), + }, }, }; diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index 080c648d77720..703fd85f6f82f 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { IUiActionsStart, IUiActionsSetup } from 'src/plugins/ui_actions/public'; import { LegacyCoreSetup, LegacyCoreStart } from '../../../../core/public'; import { Plugin as DataPlugin } from '../../../../plugins/data/public'; import { Plugin as ExpressionsPlugin } from '../../../../plugins/expressions/public'; @@ -28,12 +29,14 @@ export interface PluginsSetup { data: ReturnType; expressions: ReturnType; inspector: InspectorSetup; + uiActions: IUiActionsSetup; } export interface PluginsStart { data: ReturnType; expressions: ReturnType; inspector: InspectorStart; + uiActions: IUiActionsStart; } export const npSetup = { diff --git a/src/plugins/ui_actions/README.md b/src/plugins/ui_actions/README.md new file mode 100644 index 0000000000000..02942b7d5b406 --- /dev/null +++ b/src/plugins/ui_actions/README.md @@ -0,0 +1,10 @@ +# UI Actions + +An API for: + - creating custom functionality (`actions`) + - creating custom user interaction events (`triggers`) + - attaching and detaching `actions` to `triggers`. + - emitting `trigger` events + - executing `actions` attached to a given `trigger`. + - exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger. + diff --git a/src/plugins/ui_actions/kibana.json b/src/plugins/ui_actions/kibana.json new file mode 100644 index 0000000000000..44ecbbfa68408 --- /dev/null +++ b/src/plugins/ui_actions/kibana.json @@ -0,0 +1,6 @@ +{ + "id": "uiActions", + "version": "kibana", + "server": false, + "ui": true +} diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/action.test.ts b/src/plugins/ui_actions/public/actions/action.test.ts similarity index 56% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/action.test.ts rename to src/plugins/ui_actions/public/actions/action.test.ts index 036c6668790be..e1a789ae1cc45 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/action.test.ts +++ b/src/plugins/ui_actions/public/actions/action.test.ts @@ -17,21 +17,17 @@ * under the License. */ -import { SayHelloAction } from '../test_samples/actions/say_hello_action'; -import { HelloWorldAction } from '../test_samples/actions/hello_world_action'; -import { EmptyEmbeddable } from '../test_samples/embeddables/empty_embeddable'; +import { createSayHelloAction } from '../tests/test_samples/say_hello_action'; -test('SayHelloAction is not compatible with not matching embeddables', async () => { - const sayHelloAction = new SayHelloAction(() => {}); - const emptyEmbeddable = new EmptyEmbeddable({ id: '234' }); +test('SayHelloAction is not compatible with not matching context', async () => { + const sayHelloAction = createSayHelloAction((() => {}) as any); - const isCompatible = await sayHelloAction.isCompatible({ embeddable: emptyEmbeddable as any }); + const isCompatible = await sayHelloAction.isCompatible({} as any); expect(isCompatible).toBe(false); }); test('HelloWorldAction inherits isCompatible from base action', async () => { - const helloWorldAction = new HelloWorldAction({} as any); - const emptyEmbeddable = new EmptyEmbeddable({ id: '234' }); - const isCompatible = await helloWorldAction.isCompatible({ embeddable: emptyEmbeddable }); + const helloWorldAction = createSayHelloAction({} as any); + const isCompatible = await helloWorldAction.isCompatible({ name: 'Sue' }); expect(isCompatible).toBe(true); }); diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/actions/edit_mode_action.tsx b/src/plugins/ui_actions/public/actions/create_action.ts similarity index 59% rename from test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/actions/edit_mode_action.tsx rename to src/plugins/ui_actions/public/actions/create_action.ts index a57d6444b7716..3ac7b8cbbdec1 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/actions/edit_mode_action.tsx +++ b/src/plugins/ui_actions/public/actions/create_action.ts @@ -17,10 +17,20 @@ * under the License. */ -import { CONTEXT_MENU_TRIGGER } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { setup } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; -import { EditModeAction } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/edit_mode_action'; +import { IAction } from './i_action'; -const editModeAction = new EditModeAction(); -setup.registerAction(editModeAction); -setup.attachAction(CONTEXT_MENU_TRIGGER, editModeAction.id); +export function createAction( + action: { type: string; execute: IAction['execute'] } & Partial< + IAction + > +): IAction { + return { + getIconType: () => undefined, + order: 0, + id: action.type, + isCompatible: () => Promise.resolve(true), + getDisplayName: () => '', + getHref: () => undefined, + ...action, + }; +} diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/action.ts b/src/plugins/ui_actions/public/actions/i_action.ts similarity index 70% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/action.ts rename to src/plugins/ui_actions/public/actions/i_action.ts index 5569a3938bc72..20fdda9033f6a 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions/action.ts +++ b/src/plugins/ui_actions/public/actions/i_action.ts @@ -17,46 +17,41 @@ * under the License. */ -export abstract class Action { +export interface IAction { /** * Determined the order when there is more than one action matched to a trigger. * Higher numbers are displayed first. */ - public order: number = 0; + order?: number; - public abstract readonly type: string; - constructor(public readonly id: string) {} + id: string; + + readonly type: string; /** * Optional EUI icon type that can be displayed along with the title. */ - public getIconType(context: ActionContext): string | undefined { - return undefined; - } + getIconType(context: ActionContext): string | undefined; /** * Returns a title to be displayed to the user. * @param context */ - public abstract getDisplayName(context: ActionContext): string; + getDisplayName(context: ActionContext): string; /** * Returns a promise that resolves to true if this action is compatible given the context, * otherwise resolves to false. */ - public async isCompatible(context: ActionContext): Promise { - return true; - } + isCompatible(context: ActionContext): Promise; /** * If this returns something truthy, this is used in addition to the `execute` method when clicked. */ - public getHref(context: ActionContext): string | undefined { - return undefined; - } + getHref?(context: ActionContext): string | undefined; /** * Executes the action. */ - public abstract async execute(context: ActionContext): Promise; + execute(context: ActionContext): Promise; } diff --git a/src/plugins/ui_actions/public/actions/index.ts b/src/plugins/ui_actions/public/actions/index.ts new file mode 100644 index 0000000000000..67bdbed9e6785 --- /dev/null +++ b/src/plugins/ui_actions/public/actions/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { IAction } from './i_action'; +export { createAction } from './create_action'; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/register_action.ts b/src/plugins/ui_actions/public/actions/register_action.ts similarity index 87% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/register_action.ts rename to src/plugins/ui_actions/public/actions/register_action.ts index 89d378f1299f4..c8d5eddac9873 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/register_action.ts +++ b/src/plugins/ui_actions/public/actions/register_action.ts @@ -17,11 +17,11 @@ * under the License. */ -import { EmbeddableApiPure } from './types'; +import { IUiActionsApiPure } from '../types'; -export const registerAction: EmbeddableApiPure['registerAction'] = ({ actions }) => action => { +export const registerAction: IUiActionsApiPure['registerAction'] = ({ actions }) => action => { if (actions.has(action.id)) { - throw new Error(`Action [action.id = ${action.id}] already registered in Embeddables API.`); + throw new Error(`Action [action.id = ${action.id}] already registered.`); } actions.set(action.id, action); diff --git a/src/plugins/ui_actions/public/api.ts b/src/plugins/ui_actions/public/api.ts new file mode 100644 index 0000000000000..39580efd9e272 --- /dev/null +++ b/src/plugins/ui_actions/public/api.ts @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + IUiActionsApi, + IUiActionsDependenciesInternal, + IUiActionsDependencies, + IUiActionsApiPure, +} from './types'; +import { attachAction } from './triggers/attach_action'; +import { detachAction } from './triggers/detach_action'; +import { executeTriggerActions } from './triggers/execute_trigger_actions'; +import { getTrigger } from './triggers/get_trigger'; +import { getTriggerActions } from './triggers/get_trigger_actions'; +import { getTriggerCompatibleActions } from './triggers/get_trigger_compatible_actions'; +import { registerAction } from './actions/register_action'; +import { registerTrigger } from './triggers/register_trigger'; + +export const pureApi: IUiActionsApiPure = { + attachAction, + detachAction, + executeTriggerActions, + getTrigger, + getTriggerActions, + getTriggerCompatibleActions, + registerAction, + registerTrigger, +}; + +export const createApi = (deps: IUiActionsDependencies) => { + const partialApi: Partial = {}; + const depsInternal: IUiActionsDependenciesInternal = { ...deps, api: partialApi }; + for (const [key, fn] of Object.entries(pureApi)) { + (partialApi as any)[key] = fn(depsInternal); + } + Object.freeze(partialApi); + const api = partialApi as IUiActionsApi; + return { api, depsInternal }; +}; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/context_menu_actions/build_eui_context_menu_panels.ts b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.ts similarity index 92% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/context_menu_actions/build_eui_context_menu_panels.ts rename to src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.ts index 5b1998834a2ed..8de447f5acf9c 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/context_menu_actions/build_eui_context_menu_panels.ts +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.ts @@ -20,7 +20,7 @@ import { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { Action } from '../actions'; +import { IAction } from '../actions'; /** * Transforms an array of Actions to the shape EuiContextMenuPanel expects. @@ -30,7 +30,7 @@ export async function buildContextMenuForActions({ actionContext, closeMenu, }: { - actions: Array>; + actions: Array>; actionContext: A; closeMenu: () => void; }): Promise { @@ -42,7 +42,7 @@ export async function buildContextMenuForActions({ return { id: 'mainMenu', - title: i18n.translate('embeddableApi.actionPanel.title', { + title: i18n.translate('uiActions.actionPanel.title', { defaultMessage: 'Options', }), items: menuItems, @@ -57,7 +57,7 @@ async function buildEuiContextMenuPanelItems({ actionContext, closeMenu, }: { - actions: Array>; + actions: Array>; actionContext: A; closeMenu: () => void; }) { @@ -93,7 +93,7 @@ function convertPanelActionToContextMenuItem({ actionContext, closeMenu, }: { - action: Action; + action: IAction; actionContext: A; closeMenu: () => void; }): EuiContextMenuPanelItemDescriptor { @@ -109,7 +109,7 @@ function convertPanelActionToContextMenuItem({ closeMenu(); }; - if (action.getHref(actionContext)) { + if (action.getHref && action.getHref(actionContext)) { menuPanelItem.href = action.getHref(actionContext); } diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/context_menu_actions/index.ts b/src/plugins/ui_actions/public/context_menu/index.ts similarity index 100% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/context_menu_actions/index.ts rename to src/plugins/ui_actions/public/context_menu/index.ts diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/context_menu_actions/open_context_menu.tsx b/src/plugins/ui_actions/public/context_menu/open_context_menu.tsx similarity index 100% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/context_menu_actions/open_context_menu.tsx rename to src/plugins/ui_actions/public/context_menu/open_context_menu.tsx diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts new file mode 100644 index 0000000000000..f0d21cade422d --- /dev/null +++ b/src/plugins/ui_actions/public/index.ts @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { PluginInitializerContext } from '../../../core/public'; +import { UiActionsPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new UiActionsPlugin(initializerContext); +} + +export { IUiActionsSetup, IUiActionsStart } from './plugin'; +export { + IAction, + ITrigger, + IUiActionsApi, + TGetActionsCompatibleWithTrigger, + TExecuteTriggerActions, +} from './types'; +export { createAction } from './actions'; +export { buildContextMenuForActions } from './context_menu'; +export { IncompatibleActionError } from './triggers'; diff --git a/src/plugins/ui_actions/public/mocks.ts b/src/plugins/ui_actions/public/mocks.ts new file mode 100644 index 0000000000000..2c497dde19468 --- /dev/null +++ b/src/plugins/ui_actions/public/mocks.ts @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IUiActionsSetup, IUiActionsStart } from '.'; +import { plugin as pluginInitializer } from '.'; +// eslint-disable-next-line +import { coreMock } from '../../../core/public/mocks'; + +export type Setup = jest.Mocked; +export type Start = jest.Mocked; + +const createSetupContract = (): Setup => { + const setupContract: Setup = { + attachAction: jest.fn(), + registerAction: jest.fn(), + registerTrigger: jest.fn(), + }; + return setupContract; +}; + +const createStartContract = (): Start => { + const startContract: Start = { + attachAction: jest.fn(), + registerAction: jest.fn(), + registerTrigger: jest.fn(), + detachAction: jest.fn(), + executeTriggerActions: jest.fn(), + getTrigger: jest.fn(), + getTriggerActions: jest.fn((id: string) => []), + getTriggerCompatibleActions: jest.fn(), + }; + + return startContract; +}; + +const createPlugin = async () => { + const pluginInitializerContext = coreMock.createPluginInitializerContext(); + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createStart(); + const plugin = pluginInitializer(pluginInitializerContext); + const setup = await plugin.setup(coreSetup); + + return { + pluginInitializerContext, + coreSetup, + coreStart, + plugin, + setup, + doStart: async () => await plugin.start(coreStart), + }; +}; + +export const uiActionsPluginMock = { + createSetupContract, + createStartContract, + createPlugin, +}; diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts new file mode 100644 index 0000000000000..403aecc004d86 --- /dev/null +++ b/src/plugins/ui_actions/public/plugin.ts @@ -0,0 +1,54 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreStart, PluginInitializerContext, CoreSetup, Plugin } from 'src/core/public'; +import { IUiActionsApi, IActionRegistry, ITriggerRegistry } from './types'; +import { createApi } from './api'; + +export interface IUiActionsSetup { + attachAction: IUiActionsApi['attachAction']; + registerAction: IUiActionsApi['registerAction']; + registerTrigger: IUiActionsApi['registerTrigger']; +} + +export type IUiActionsStart = IUiActionsApi; + +export class UiActionsPlugin implements Plugin { + private readonly triggers: ITriggerRegistry = new Map(); + private readonly actions: IActionRegistry = new Map(); + private api!: IUiActionsApi; + + constructor(initializerContext: PluginInitializerContext) { + this.api = createApi({ triggers: this.triggers, actions: this.actions }).api; + } + + public setup(core: CoreSetup): IUiActionsSetup { + return { + registerTrigger: this.api.registerTrigger, + registerAction: this.api.registerAction, + attachAction: this.api.attachAction, + }; + } + + public start(core: CoreStart): IUiActionsStart { + return this.api; + } + + public stop() {} +} diff --git a/src/plugins/ui_actions/public/tests/helpers.ts b/src/plugins/ui_actions/public/tests/helpers.ts new file mode 100644 index 0000000000000..abbc846736cd0 --- /dev/null +++ b/src/plugins/ui_actions/public/tests/helpers.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IUiActionsDependencies } from '../types'; + +export const createDeps = (): IUiActionsDependencies => { + const deps: IUiActionsDependencies = { + actions: new Map(), + triggers: new Map(), + }; + return deps; +}; diff --git a/src/plugins/ui_actions/public/tests/index.ts b/src/plugins/ui_actions/public/tests/index.ts new file mode 100644 index 0000000000000..6f5610a7beb64 --- /dev/null +++ b/src/plugins/ui_actions/public/tests/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { uiActionsTestPlugin } from './test_plugin'; diff --git a/src/plugins/ui_actions/public/tests/test_plugin.ts b/src/plugins/ui_actions/public/tests/test_plugin.ts new file mode 100644 index 0000000000000..d1995262ce514 --- /dev/null +++ b/src/plugins/ui_actions/public/tests/test_plugin.ts @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreSetup, CoreStart } from 'src/core/public'; +import { UiActionsPlugin, IUiActionsSetup, IUiActionsStart } from '../plugin'; + +export interface IUiActionsTestPluginReturn { + plugin: UiActionsPlugin; + coreSetup: CoreSetup; + coreStart: CoreStart; + setup: IUiActionsSetup; + doStart: (anotherCoreStart?: CoreStart) => IUiActionsStart; +} + +export const uiActionsTestPlugin = ( + coreSetup: CoreSetup = {} as CoreSetup, + coreStart: CoreStart = {} as CoreStart +): IUiActionsTestPluginReturn => { + const initializerContext = {} as any; + const plugin = new UiActionsPlugin(initializerContext); + const setup = plugin.setup(coreSetup); + + return { + plugin, + coreSetup, + coreStart, + setup, + doStart: (anotherCoreStart: CoreStart = coreStart) => { + const start = plugin.start(anotherCoreStart); + return start; + }, + }; +}; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/hello_world_action.tsx b/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx similarity index 58% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/hello_world_action.tsx rename to src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx index 356679b5e4501..88a0c4ca08145 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/hello_world_action.tsx +++ b/src/plugins/ui_actions/public/tests/test_samples/hello_world_action.tsx @@ -19,30 +19,23 @@ import React from 'react'; import { EuiFlyout } from '@elastic/eui'; -import { CoreStart } from '../../../../../../../../../core/public'; -import { Action } from '../..'; +import { CoreStart } from 'src/core/public'; +import { createAction, IAction } from '../../actions'; export const HELLO_WORLD_ACTION_ID = 'HELLO_WORLD_ACTION_ID'; -export class HelloWorldAction extends Action { - public readonly type = HELLO_WORLD_ACTION_ID; - - constructor(private readonly overlays: CoreStart['overlays']) { - super(HELLO_WORLD_ACTION_ID); - } - - public getDisplayName() { - return 'Hello World Action!'; - } - - public async execute() { - const flyoutSession = this.overlays.openFlyout( - flyoutSession && flyoutSession.close()}> - Hello World, I am a hello world action! - , - { - 'data-test-subj': 'helloWorldAction', - } - ); - } +export function createHelloWorldAction(overlays: CoreStart['overlays']): IAction { + return createAction({ + type: HELLO_WORLD_ACTION_ID, + execute: async () => { + const flyoutSession = overlays.openFlyout( + flyoutSession && flyoutSession.close()}> + Hello World, I am a hello world action! + , + { + 'data-test-subj': 'helloWorldAction', + } + ); + }, + }); } diff --git a/src/plugins/ui_actions/public/tests/test_samples/index.ts b/src/plugins/ui_actions/public/tests/test_samples/index.ts new file mode 100644 index 0000000000000..40301d629aa41 --- /dev/null +++ b/src/plugins/ui_actions/public/tests/test_samples/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +export { createRestrictedAction } from './restricted_action'; +export { createSayHelloAction } from './say_hello_action'; +export { createHelloWorldAction } from './hello_world_action'; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/restricted_action.ts b/src/plugins/ui_actions/public/tests/test_samples/restricted_action.ts similarity index 63% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/restricted_action.ts rename to src/plugins/ui_actions/public/tests/test_samples/restricted_action.ts index 16aede6da18d1..2e863b43e0917 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/actions/restricted_action.ts +++ b/src/plugins/ui_actions/public/tests/test_samples/restricted_action.ts @@ -17,26 +17,14 @@ * under the License. */ -import { Action } from '../../actions'; +import { IAction, createAction } from '../../actions'; export const RESTRICTED_ACTION = 'RESTRICTED_ACTION'; -export class RestrictedAction extends Action { - public readonly type = RESTRICTED_ACTION; - - private isCompatibleFn: (context: A) => boolean; - constructor(isCompatible: (context: A) => boolean) { - super(RESTRICTED_ACTION); - this.isCompatibleFn = isCompatible; - } - - getDisplayName() { - return `I am only sometimes compatible`; - } - - async isCompatible(context: A) { - return this.isCompatibleFn(context); - } - - async execute() {} +export function createRestrictedAction(isCompatibleIn: (context: C) => boolean): IAction { + return createAction({ + type: RESTRICTED_ACTION, + isCompatible: async context => isCompatibleIn(context), + execute: async () => {}, + }); } diff --git a/src/plugins/ui_actions/public/tests/test_samples/say_hello_action.tsx b/src/plugins/ui_actions/public/tests/test_samples/say_hello_action.tsx new file mode 100644 index 0000000000000..3c4ecfb6e7c8a --- /dev/null +++ b/src/plugins/ui_actions/public/tests/test_samples/say_hello_action.tsx @@ -0,0 +1,43 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { EuiFlyout } from '@elastic/eui'; +import { CoreStart } from 'src/core/public'; +import { IAction, createAction } from '../../actions'; + +export const SAY_HELLO_ACTION = 'SAY_HELLO_ACTION'; + +export function createSayHelloAction(overlays: CoreStart['overlays']): IAction<{ name: string }> { + return createAction<{ name: string }>({ + type: SAY_HELLO_ACTION, + getDisplayName: ({ name }) => `Hello, ${name}`, + isCompatible: async ({ name }) => name !== undefined, + execute: async context => { + const flyoutSession = overlays.openFlyout( + flyoutSession && flyoutSession.close()}> + this.getDisplayName(context) + , + { + 'data-test-subj': 'sayHelloAction', + } + ); + }, + }); +} diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/attach_action.ts b/src/plugins/ui_actions/public/triggers/attach_action.ts similarity index 91% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/attach_action.ts rename to src/plugins/ui_actions/public/triggers/attach_action.ts index f2f22eeb10213..17793d46c5a42 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/attach_action.ts +++ b/src/plugins/ui_actions/public/triggers/attach_action.ts @@ -17,9 +17,9 @@ * under the License. */ -import { EmbeddableApiPure } from './types'; +import { IUiActionsApiPure } from '../types'; -export const attachAction: EmbeddableApiPure['attachAction'] = ({ triggers }) => ( +export const attachAction: IUiActionsApiPure['attachAction'] = ({ triggers }) => ( triggerId, actionId ) => { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/detach_action.ts b/src/plugins/ui_actions/public/triggers/detach_action.ts similarity index 91% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/detach_action.ts rename to src/plugins/ui_actions/public/triggers/detach_action.ts index 6a3f5fa11d947..cb9bf685cdc00 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/detach_action.ts +++ b/src/plugins/ui_actions/public/triggers/detach_action.ts @@ -17,9 +17,9 @@ * under the License. */ -import { EmbeddableApiPure } from './types'; +import { IUiActionsApiPure } from '../types'; -export const detachAction: EmbeddableApiPure['detachAction'] = ({ triggers }) => ( +export const detachAction: IUiActionsApiPure['detachAction'] = ({ triggers }) => ( triggerId, actionId ) => { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/execute_trigger_actions.test.ts b/src/plugins/ui_actions/public/triggers/execute_trigger_actions.test.ts similarity index 51% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/execute_trigger_actions.test.ts rename to src/plugins/ui_actions/public/triggers/execute_trigger_actions.test.ts index 1b5a80af52987..f96a11d5f8cfe 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/execute_trigger_actions.test.ts +++ b/src/plugins/ui_actions/public/triggers/execute_trigger_actions.test.ts @@ -17,49 +17,33 @@ * under the License. */ -import { testPlugin, TestPluginReturn } from './test_plugin'; -import { of } from './helpers'; -import { Action, openContextMenu, IEmbeddable } from '../lib'; -import { - ContactCardEmbeddable, - CONTACT_USER_TRIGGER, -} from '../lib/test_samples/embeddables/contact_card/contact_card_embeddable'; -import { SEND_MESSAGE_ACTION } from '../lib/test_samples/actions/send_message_action'; +import { IAction, createAction } from '../actions'; +import { openContextMenu } from '../context_menu'; +import { IUiActionsTestPluginReturn, uiActionsTestPlugin } from '../tests/test_plugin'; -jest.mock('../lib/context_menu_actions'); +jest.mock('../context_menu'); const executeFn = jest.fn(); const openContextMenuSpy = (openContextMenu as any) as jest.SpyInstance; -class TestAction extends Action { - public readonly type = 'testAction'; - public checkCompatibility: (context: A) => boolean; +const CONTACT_USER_TRIGGER = 'CONTACT_USER_TRIGGER'; - constructor(id: string, checkCompatibility: (context: A) => boolean) { - super(id); - this.checkCompatibility = checkCompatibility; - } - - public getDisplayName() { - return 'test'; - } - - async isCompatible(context: A) { - return this.checkCompatibility(context); - } - - async execute(context: unknown) { - executeFn(context); - } +function createTestAction(id: string, checkCompatibility: (context: A) => boolean): IAction { + return createAction({ + type: 'testAction', + id, + isCompatible: context => Promise.resolve(checkCompatibility(context)), + execute: context => executeFn(context), + }); } -let embeddables: TestPluginReturn; +let uiActions: IUiActionsTestPluginReturn; const reset = () => { - embeddables = testPlugin(); + uiActions = uiActionsTestPlugin(); - embeddables.setup.registerTrigger({ + uiActions.setup.registerTrigger({ id: CONTACT_USER_TRIGGER, - actionIds: [SEND_MESSAGE_ACTION], + actionIds: ['SEND_MESSAGE_ACTION'], }); executeFn.mockReset(); @@ -68,23 +52,17 @@ const reset = () => { beforeEach(reset); test('executes a single action mapped to a trigger', async () => { - const { setup, doStart } = embeddables; + const { setup, doStart } = uiActions; const trigger = { id: 'MY-TRIGGER', title: 'My trigger', actionIds: ['test1'], }; - const action = new TestAction('test1', () => true); + const action = createTestAction('test1', () => true); setup.registerTrigger(trigger); setup.registerAction(action); - const context = { - embeddable: new ContactCardEmbeddable( - { id: '123', firstName: 'Stacey', lastName: 'G' }, - { execAction: (() => null) as any } - ), - triggerContext: {}, - }; + const context = {}; const start = doStart(); await start.executeTriggerActions('MY-TRIGGER', context); @@ -93,7 +71,7 @@ test('executes a single action mapped to a trigger', async () => { }); test('throws an error if there are no compatible actions to execute', async () => { - const { setup, doStart } = embeddables; + const { setup, doStart } = uiActions; const trigger = { id: 'MY-TRIGGER', title: 'My trigger', @@ -101,48 +79,27 @@ test('throws an error if there are no compatible actions to execute', async () = }; setup.registerTrigger(trigger); - const context = { - embeddable: new ContactCardEmbeddable( - { id: '123', firstName: 'Stacey', lastName: 'G' }, - { execAction: (() => null) as any } - ), - triggerContext: {}, - }; + const context = {}; const start = doStart(); - const [, error] = await of(start.executeTriggerActions('MY-TRIGGER', context)); - - expect(error).toBeInstanceOf(Error); - expect(error.message).toMatchInlineSnapshot( - `"No compatible actions found to execute for trigger [triggerId = MY-TRIGGER]."` + await expect(start.executeTriggerActions('MY-TRIGGER', context)).rejects.toMatchObject( + new Error('No compatible actions found to execute for trigger [triggerId = MY-TRIGGER].') ); }); test('does not execute an incompatible action', async () => { - const { setup, doStart } = embeddables; + const { setup, doStart } = uiActions; const trigger = { id: 'MY-TRIGGER', title: 'My trigger', actionIds: ['test1'], }; - const action = new TestAction<{ embeddable: IEmbeddable }>( - 'test1', - ({ embeddable }) => embeddable.id === 'executeme' - ); - const embeddable = new ContactCardEmbeddable( - { - id: 'executeme', - firstName: 'Stacey', - lastName: 'G', - }, - {} as any - ); + const action = createTestAction<{ name: string }>('test1', ({ name }) => name === 'executeme'); setup.registerTrigger(trigger); setup.registerAction(action); const start = doStart(); const context = { - embeddable, - triggerContext: {}, + name: 'executeme', }; await start.executeTriggerActions('MY-TRIGGER', context); @@ -150,22 +107,14 @@ test('does not execute an incompatible action', async () => { }); test('shows a context menu when more than one action is mapped to a trigger', async () => { - const { setup, doStart } = embeddables; + const { setup, doStart } = uiActions; const trigger = { id: 'MY-TRIGGER', title: 'My trigger', actionIds: ['test1', 'test2'], }; - const action1 = new TestAction('test1', () => true); - const action2 = new TestAction('test2', () => true); - const embeddable = new ContactCardEmbeddable( - { - id: 'executeme', - firstName: 'Stacey', - lastName: 'G', - }, - {} as any - ); + const action1 = createTestAction('test1', () => true); + const action2 = createTestAction('test2', () => true); setup.registerTrigger(trigger); setup.registerAction(action1); setup.registerAction(action2); @@ -173,10 +122,7 @@ test('shows a context menu when more than one action is mapped to a trigger', as expect(openContextMenu).toHaveBeenCalledTimes(0); const start = doStart(); - const context = { - embeddable, - triggerContext: {}, - }; + const context = {}; await start.executeTriggerActions('MY-TRIGGER', context); expect(executeFn).toBeCalledTimes(0); @@ -184,16 +130,14 @@ test('shows a context menu when more than one action is mapped to a trigger', as }); test('passes whole action context to isCompatible()', async () => { - const { setup, doStart } = embeddables; + const { setup, doStart } = uiActions; const trigger = { id: 'MY-TRIGGER', title: 'My trigger', actionIds: ['test'], }; - const action = new TestAction<{ triggerContext: any }>('test', ({ triggerContext }) => { - expect(triggerContext).toEqual({ - foo: 'bar', - }); + const action = createTestAction<{ foo: string }>('test', ({ foo }) => { + expect(foo).toEqual('bar'); return true; }); @@ -201,11 +145,6 @@ test('passes whole action context to isCompatible()', async () => { setup.registerAction(action); const start = doStart(); - const context = { - embeddable: {} as any, - triggerContext: { - foo: 'bar', - }, - }; + const context = { foo: 'bar' }; await start.executeTriggerActions('MY-TRIGGER', context); }); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/execute_trigger_actions.ts b/src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts similarity index 79% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/execute_trigger_actions.ts rename to src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts index b7bf7e33e7c9d..ab938eeb9cffd 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/execute_trigger_actions.ts +++ b/src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts @@ -17,11 +17,12 @@ * under the License. */ -import { EmbeddableApiPure } from './types'; -import { Action, buildContextMenuForActions, openContextMenu } from '../lib'; +import { IUiActionsApiPure } from '../types'; +import { buildContextMenuForActions, openContextMenu } from '../context_menu'; +import { IAction } from '../actions'; -const executeSingleAction = async (action: Action, actionContext: A) => { - const href = action.getHref(actionContext); +const executeSingleAction = async (action: IAction, actionContext: A) => { + const href = action.getHref && action.getHref(actionContext); // TODO: Do we need a `getHref()` special case? if (href) { @@ -32,7 +33,7 @@ const executeSingleAction = async (action: Action, actionC await action.execute(actionContext); }; -export const executeTriggerActions: EmbeddableApiPure['executeTriggerActions'] = ({ +export const executeTriggerActions: IUiActionsApiPure['executeTriggerActions'] = ({ api, }) => async (triggerId, actionContext) => { const actions = await api.getTriggerCompatibleActions!(triggerId, actionContext); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/get_trigger.test.ts b/src/plugins/ui_actions/public/triggers/get_trigger.test.ts similarity index 80% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/get_trigger.test.ts rename to src/plugins/ui_actions/public/triggers/get_trigger.test.ts index adeba8ff76776..88dd5a8990c9d 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/tests/get_trigger.test.ts +++ b/src/plugins/ui_actions/public/triggers/get_trigger.test.ts @@ -17,9 +17,8 @@ * under the License. */ -import { createApi } from '..'; -import { createDeps } from './helpers'; -import { expectError } from '../../tests/helpers'; +import { createApi } from '../api'; +import { createDeps } from '../tests/helpers'; test('can get Trigger from registry', () => { const deps = createDeps(); @@ -45,8 +44,5 @@ test('throws if trigger does not exist', () => { const deps = createDeps(); const { api } = createApi(deps); - const error = expectError(() => api.getTrigger('foo')); - - expect(error).toBeInstanceOf(Error); - expect(error.message).toMatchInlineSnapshot(`"Trigger [triggerId = foo] does not exist."`); + expect(() => api.getTrigger('foo')).toThrowError('Trigger [triggerId = foo] does not exist.'); }); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/get_trigger.ts b/src/plugins/ui_actions/public/triggers/get_trigger.ts similarity index 89% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/get_trigger.ts rename to src/plugins/ui_actions/public/triggers/get_trigger.ts index b3cd086f4a958..d5e2b25ce6c19 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/get_trigger.ts +++ b/src/plugins/ui_actions/public/triggers/get_trigger.ts @@ -17,9 +17,9 @@ * under the License. */ -import { EmbeddableApiPure } from './types'; +import { IUiActionsApiPure } from '../types'; -export const getTrigger: EmbeddableApiPure['getTrigger'] = ({ triggers }) => id => { +export const getTrigger: IUiActionsApiPure['getTrigger'] = ({ triggers }) => id => { const trigger = triggers.get(id); if (!trigger) { diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/get_trigger_actions.test.ts b/src/plugins/ui_actions/public/triggers/get_trigger_actions.test.ts similarity index 88% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/get_trigger_actions.test.ts rename to src/plugins/ui_actions/public/triggers/get_trigger_actions.test.ts index 17c663659f701..8288bf9686fad 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/get_trigger_actions.test.ts +++ b/src/plugins/ui_actions/public/triggers/get_trigger_actions.test.ts @@ -17,22 +17,22 @@ * under the License. */ -import { testPlugin } from './test_plugin'; -import { Action } from '../lib'; +import { IAction } from '../actions'; +import { uiActionsTestPlugin } from '../tests/test_plugin'; -const action1 = ({ +const action1: IAction = { id: 'action1', order: 1, type: 'type1', -} as any) as Action; -const action2 = ({ +} as any; +const action2: IAction = { id: 'action2', order: 2, type: 'type2', -} as any) as Action; +} as any; test('returns actions set on trigger', () => { - const { setup, doStart } = testPlugin(); + const { setup, doStart } = uiActionsTestPlugin(); setup.registerAction(action1); setup.registerAction(action2); setup.registerTrigger({ diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/get_trigger_actions.ts b/src/plugins/ui_actions/public/triggers/get_trigger_actions.ts similarity index 84% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/get_trigger_actions.ts rename to src/plugins/ui_actions/public/triggers/get_trigger_actions.ts index 0167993a11c52..31b401a863663 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/get_trigger_actions.ts +++ b/src/plugins/ui_actions/public/triggers/get_trigger_actions.ts @@ -17,13 +17,13 @@ * under the License. */ -import { EmbeddableApiPure } from './types'; -import { Action } from '../lib'; +import { IUiActionsApiPure } from '../types'; +import { IAction } from '../actions'; -export const getTriggerActions: EmbeddableApiPure['getTriggerActions'] = ({ +export const getTriggerActions: IUiActionsApiPure['getTriggerActions'] = ({ api, actions, }) => id => { const trigger = api.getTrigger!(id); - return trigger.actionIds.map(actionId => actions.get(actionId)).filter(Boolean) as Action[]; + return trigger.actionIds.map(actionId => actions.get(actionId)).filter(Boolean) as IAction[]; }; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/get_trigger_compatible_actions.test.ts b/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.test.ts similarity index 58% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/get_trigger_compatible_actions.test.ts rename to src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.test.ts index f70bfcb080ae9..ea89ba328f406 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/get_trigger_compatible_actions.test.ts +++ b/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.test.ts @@ -17,27 +17,29 @@ * under the License. */ -import { testPlugin, TestPluginReturn } from './test_plugin'; -import { HelloWorldAction } from '../lib/test_samples/actions/hello_world_action'; -import { SayHelloAction } from '../lib/test_samples/actions/say_hello_action'; -import { RestrictedAction } from '../lib/test_samples/actions/restricted_action'; -import { EmptyEmbeddable } from '../lib/test_samples/embeddables/empty_embeddable'; -import { CONTEXT_MENU_TRIGGER, IEmbeddable } from '../lib'; -import { of } from './helpers'; - -let action: SayHelloAction; -let embeddables: TestPluginReturn; +import { createSayHelloAction } from '../tests/test_samples/say_hello_action'; +import { IUiActionsTestPluginReturn, uiActionsTestPlugin } from '../tests/test_plugin'; +import { createRestrictedAction, createHelloWorldAction } from '../tests/test_samples'; +import { IAction } from '../actions'; + +let action: IAction<{ name: string }>; +let uiActions: IUiActionsTestPluginReturn; beforeEach(() => { - embeddables = testPlugin(); - action = new SayHelloAction(() => {}); + uiActions = uiActionsTestPlugin(); + action = createSayHelloAction({} as any); - embeddables.setup.registerAction(action); - embeddables.setup.attachAction(CONTEXT_MENU_TRIGGER, action.id); + uiActions.setup.registerAction(action); + uiActions.setup.registerTrigger({ + id: 'trigger', + title: 'trigger', + actionIds: [], + }); + uiActions.setup.attachAction('trigger', action.id); }); test('can register and get actions', async () => { - const { setup, plugin } = embeddables; - const helloWorldAction = new HelloWorldAction({} as any); + const { setup, plugin } = uiActions; + const helloWorldAction = createHelloWorldAction({} as any); const length = (plugin as any).actions.size; setup.registerAction(helloWorldAction); @@ -48,9 +50,8 @@ test('can register and get actions', async () => { }); test('getTriggerCompatibleActions returns attached actions', async () => { - const { setup, doStart } = embeddables; - const embeddable = new EmptyEmbeddable({ id: '123' }); - const helloWorldAction = new HelloWorldAction({} as any); + const { setup, doStart } = uiActions; + const helloWorldAction = createHelloWorldAction({} as any); setup.registerAction(helloWorldAction); @@ -63,25 +64,20 @@ test('getTriggerCompatibleActions returns attached actions', async () => { setup.attachAction('MY-TRIGGER', helloWorldAction.id); const start = doStart(); - const actions = await start.getTriggerCompatibleActions('MY-TRIGGER', { - embeddable, - }); + const actions = await start.getTriggerCompatibleActions('MY-TRIGGER', {}); expect(actions.length).toBe(1); expect(actions[0].id).toBe(helloWorldAction.id); }); test('filters out actions not applicable based on the context', async () => { - const { setup, doStart } = embeddables; - const restrictedAction = new RestrictedAction<{ embeddable: IEmbeddable }>(context => { - return context.embeddable.id === 'accept'; + const { setup, doStart } = uiActions; + const restrictedAction = createRestrictedAction<{ accept: boolean }>(context => { + return context.accept; }); setup.registerAction(restrictedAction); - const acceptEmbeddable = new EmptyEmbeddable({ id: 'accept' }); - const rejectEmbeddable = new EmptyEmbeddable({ id: 'reject' }); - const testTrigger = { id: 'MY-TRIGGER', title: 'My trigger', @@ -91,36 +87,26 @@ test('filters out actions not applicable based on the context', async () => { setup.registerTrigger(testTrigger); const start = doStart(); - let actions = await start.getTriggerCompatibleActions(testTrigger.id, { - embeddable: acceptEmbeddable, - }); + let actions = await start.getTriggerCompatibleActions(testTrigger.id, { accept: true }); expect(actions.length).toBe(1); - actions = await start.getTriggerCompatibleActions(testTrigger.id, { - embeddable: rejectEmbeddable, - }); + actions = await start.getTriggerCompatibleActions(testTrigger.id, { accept: false }); expect(actions.length).toBe(0); }); test(`throws an error with an invalid trigger ID`, async () => { - const { doStart } = embeddables; + const { doStart } = uiActions; const start = doStart(); - const [, error] = await of( - start.getTriggerCompatibleActions('I do not exist', { - embeddable: new EmptyEmbeddable({ id: 'empty' }), - }) - ); - await expect(error).toBeInstanceOf(Error); - await expect(error.message).toMatchInlineSnapshot( - `"Trigger [triggerId = I do not exist] does not exist."` + await expect(start.getTriggerCompatibleActions('I do not exist', {})).rejects.toMatchObject( + new Error('Trigger [triggerId = I do not exist] does not exist.') ); }); test(`with a trigger mapping that maps to an non-existing action returns empty list`, async () => { - const { setup, doStart } = embeddables; + const { setup, doStart } = uiActions; const testTrigger = { id: '123', title: '123', @@ -129,9 +115,7 @@ test(`with a trigger mapping that maps to an non-existing action returns empty l setup.registerTrigger(testTrigger); const start = doStart(); - const actions = await start.getTriggerCompatibleActions(testTrigger.id, { - embeddable: new EmptyEmbeddable({ id: 'empty' }), - }); + const actions = await start.getTriggerCompatibleActions(testTrigger.id, {}); expect(actions).toEqual([]); }); diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/get_trigger_compatible_actions.ts b/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts similarity index 85% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/get_trigger_compatible_actions.ts rename to src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts index 106ef5b3f0d26..7843b9284eb02 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/get_trigger_compatible_actions.ts +++ b/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts @@ -17,15 +17,15 @@ * under the License. */ -import { EmbeddableApiPure } from './types'; -import { Action } from '../lib'; +import { IUiActionsApiPure } from '../types'; +import { IAction } from '../actions/i_action'; -export const getTriggerCompatibleActions: EmbeddableApiPure['getTriggerCompatibleActions'] = ({ +export const getTriggerCompatibleActions: IUiActionsApiPure['getTriggerCompatibleActions'] = ({ api, }) => async (triggerId, context) => { const actions = api.getTriggerActions!(triggerId); const isCompatibles = await Promise.all(actions.map(action => action.isCompatible(context))); - return actions.reduce( + return actions.reduce( (acc, action, i) => (isCompatibles[i] ? [...acc, action] : acc), [] ); diff --git a/src/plugins/ui_actions/public/triggers/i_trigger.ts b/src/plugins/ui_actions/public/triggers/i_trigger.ts new file mode 100644 index 0000000000000..61284dc39e525 --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/i_trigger.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export interface ITrigger { + id: string; + title?: string; + description?: string; + actionIds: string[]; +} diff --git a/src/plugins/ui_actions/public/triggers/incompatible_action_error.ts b/src/plugins/ui_actions/public/triggers/incompatible_action_error.ts new file mode 100644 index 0000000000000..e6cffd771207c --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/incompatible_action_error.ts @@ -0,0 +1,31 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { i18n } from '@kbn/i18n'; + +export class IncompatibleActionError extends Error { + code = 'INCOMPATIBLE_ACTION'; + + constructor() { + super( + i18n.translate('uiActions.errors.incompatibleAction', { + defaultMessage: 'Action is incompatible', + }) + ); + } +} diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts new file mode 100644 index 0000000000000..3006a5428f45e --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { IncompatibleActionError } from './incompatible_action_error'; diff --git a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/register_trigger.ts b/src/plugins/ui_actions/public/triggers/register_trigger.ts similarity index 87% rename from src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/register_trigger.ts rename to src/plugins/ui_actions/public/triggers/register_trigger.ts index 3aa63a065c94e..252513a779d2c 100644 --- a/src/legacy/core_plugins/embeddable_api/public/np_ready/public/api/register_trigger.ts +++ b/src/plugins/ui_actions/public/triggers/register_trigger.ts @@ -17,11 +17,11 @@ * under the License. */ -import { EmbeddableApiPure } from './types'; +import { IUiActionsApiPure } from '../types'; -export const registerTrigger: EmbeddableApiPure['registerTrigger'] = ({ triggers }) => trigger => { +export const registerTrigger: IUiActionsApiPure['registerTrigger'] = ({ triggers }) => trigger => { if (triggers.has(trigger.id)) { - throw new Error(`Trigger [trigger.id = ${trigger.id}] already registered in Embeddables API.`); + throw new Error(`Trigger [trigger.id = ${trigger.id}] already registered.`); } triggers.set(trigger.id, trigger); diff --git a/src/plugins/ui_actions/public/triggers/registry.test.ts b/src/plugins/ui_actions/public/triggers/registry.test.ts new file mode 100644 index 0000000000000..6edb2b19a95e4 --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/registry.test.ts @@ -0,0 +1,149 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { createApi } from '../api'; +import { createDeps } from '../tests/helpers'; + +const HELLO_WORLD_ACTION_ID = 'HELLO_WORLD_ACTION_ID'; + +test('can register trigger', () => { + const deps = createDeps(); + const { api } = createApi(deps); + + api.registerTrigger({ + actionIds: [], + description: 'foo', + id: 'bar', + title: 'baz', + }); + + expect(deps.triggers.get('bar')).toEqual({ + actionIds: [], + description: 'foo', + id: 'bar', + title: 'baz', + }); +}); + +test('can register action', () => { + const deps = createDeps(); + const { api } = createApi(deps); + + api.registerAction({ + id: HELLO_WORLD_ACTION_ID, + order: 13, + } as any); + + expect(deps.actions.get(HELLO_WORLD_ACTION_ID)).toMatchObject({ + id: HELLO_WORLD_ACTION_ID, + order: 13, + }); +}); + +test('can attach an action to a trigger', () => { + const deps = createDeps(); + const { api } = createApi(deps); + const trigger = { + id: 'MY-TRIGGER', + actionIds: [], + }; + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + expect(trigger.actionIds).toEqual([]); + + api.registerTrigger(trigger); + api.registerAction(action); + api.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); + + expect(trigger.actionIds).toEqual([HELLO_WORLD_ACTION_ID]); +}); + +test('can detach an action to a trigger', () => { + const deps = createDeps(); + const { api } = createApi(deps); + const trigger = { + id: 'MY-TRIGGER', + actionIds: [], + }; + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + expect(trigger.actionIds).toEqual([]); + + api.registerTrigger(trigger); + api.registerAction(action); + api.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); + api.detachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); + + expect(trigger.actionIds).toEqual([]); +}); + +test('detaching an invalid action from a trigger throws an error', async () => { + const { api } = createApi({ actions: new Map(), triggers: new Map() }); + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + api.registerAction(action); + expect(() => api.detachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError( + 'No trigger [triggerId = i do not exist] exists, for detaching action [actionId = HELLO_WORLD_ACTION_ID].' + ); +}); + +test('attaching an invalid action to a trigger throws an error', async () => { + const { api } = createApi({ actions: new Map(), triggers: new Map() }); + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + api.registerAction(action); + expect(() => api.attachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError( + 'No trigger [triggerId = i do not exist] exists, for attaching action [actionId = HELLO_WORLD_ACTION_ID].' + ); +}); + +test('cannot register another action with the same ID', async () => { + const { api } = createApi({ actions: new Map(), triggers: new Map() }); + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + api.registerAction(action); + expect(() => api.registerAction(action)).toThrowError( + 'Action [action.id = HELLO_WORLD_ACTION_ID] already registered.' + ); +}); + +test('cannot register another trigger with the same ID', async () => { + const { api } = createApi({ actions: new Map(), triggers: new Map() }); + const trigger = { id: 'MY-TRIGGER' } as any; + + api.registerTrigger(trigger); + expect(() => api.registerTrigger(trigger)).toThrowError( + 'Trigger [trigger.id = MY-TRIGGER] already registered.' + ); +}); diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts new file mode 100644 index 0000000000000..d7395b1b5ccf7 --- /dev/null +++ b/src/plugins/ui_actions/public/types.ts @@ -0,0 +1,58 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IAction } from './actions/i_action'; +import { ITrigger } from './triggers/i_trigger'; + +export { IAction } from './actions'; +export { ITrigger } from './triggers/i_trigger'; + +export type TExecuteTriggerActions = (triggerId: string, actionContext: A) => Promise; + +export type TGetActionsCompatibleWithTrigger = ( + triggerId: string, + context: C +) => Promise; + +export interface IUiActionsApi { + attachAction: (triggerId: string, actionId: string) => void; + detachAction: (triggerId: string, actionId: string) => void; + executeTriggerActions: TExecuteTriggerActions; + getTrigger: (id: string) => ITrigger; + getTriggerActions: (id: string) => IAction[]; + getTriggerCompatibleActions: (triggerId: string, context: C) => Promise>>; + registerAction: (action: IAction) => void; + registerTrigger: (trigger: ITrigger) => void; +} + +export interface IUiActionsDependencies { + actions: IActionRegistry; + triggers: ITriggerRegistry; +} + +export interface IUiActionsDependenciesInternal extends IUiActionsDependencies { + api: Readonly>; +} + +export type IUiActionsApiPure = { + [K in keyof IUiActionsApi]: (deps: IUiActionsDependenciesInternal) => IUiActionsApi[K]; +}; + +export type ITriggerRegistry = Map; +export type IActionRegistry = Map; diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx index 13df6bce6089c..c0ed2b027f0e4 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx @@ -20,17 +20,17 @@ import { EuiTab } from '@elastic/eui'; import React, { Component } from 'react'; import { CoreStart } from 'src/core/public'; import { - GetActionsCompatibleWithTrigger, GetEmbeddableFactory, GetEmbeddableFactories, } from 'src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { TGetActionsCompatibleWithTrigger } from '../../../../../../../../src/plugins/ui_actions/public'; import { ContactCardEmbeddableExample } from './hello_world_embeddable_example'; import { HelloWorldContainerExample } from './hello_world_container_example'; import { DashboardContainerExample } from './dashboard_container_example'; import { Start as InspectorStartContract } from '../../../../../../../../src/plugins/inspector/public'; export interface AppProps { - getActions: GetActionsCompatibleWithTrigger; + getActions: TGetActionsCompatibleWithTrigger; getEmbeddableFactory: GetEmbeddableFactory; getAllEmbeddableFactories: GetEmbeddableFactories; overlays: CoreStart['overlays']; diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx index c1bf9da3c5827..528b98c70a6da 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx @@ -29,7 +29,6 @@ import { ViewMode, isErrorEmbeddable, EmbeddablePanel, - GetActionsCompatibleWithTrigger, GetEmbeddableFactory, GetEmbeddableFactories, } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; @@ -37,9 +36,10 @@ import { import { CoreStart } from '../../../../../../../../src/core/public'; import { dashboardInput } from './dashboard_input'; import { Start as InspectorStartContract } from '../../../../../../../../src/plugins/inspector/public'; +import { TGetActionsCompatibleWithTrigger } from '../../../../../../../../src/plugins/ui_actions/public'; interface Props { - getActions: GetActionsCompatibleWithTrigger; + getActions: TGetActionsCompatibleWithTrigger; getEmbeddableFactory: GetEmbeddableFactory; getAllEmbeddableFactories: GetEmbeddableFactories; overlays: CoreStart['overlays']; diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/hello_world_container_example.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/hello_world_container_example.tsx index 70df793ee97fc..eb72879eb8f8e 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/hello_world_container_example.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/hello_world_container_example.tsx @@ -20,11 +20,11 @@ import React from 'react'; import { Subscription } from 'rxjs'; import { EuiFieldText, EuiFormRow } from '@elastic/eui'; +import { TGetActionsCompatibleWithTrigger } from '../../../../../../../../src/plugins/ui_actions/public'; import { CoreStart } from '../../../../../../../../src/core/public'; import { EmbeddablePanel, GetEmbeddableFactory, - GetActionsCompatibleWithTrigger, GetEmbeddableFactories, } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { HelloWorldContainer } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples/embeddables/hello_world_container'; @@ -33,7 +33,7 @@ import { HELLO_WORLD_EMBEDDABLE_TYPE } from '../../../../../../../../src/legacy/ import { Start as InspectorStartContract } from '../../../../../../../../src/plugins/inspector/public'; interface Props { - getActions: GetActionsCompatibleWithTrigger; + getActions: TGetActionsCompatibleWithTrigger; getEmbeddableFactory: GetEmbeddableFactory; getAllEmbeddableFactories: GetEmbeddableFactories; overlays: CoreStart['overlays']; diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/hello_world_embeddable_example.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/hello_world_embeddable_example.tsx index 80bc2eeaf2778..853ceb0322ea6 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/hello_world_embeddable_example.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/hello_world_embeddable_example.tsx @@ -18,9 +18,9 @@ */ import React from 'react'; +import { TGetActionsCompatibleWithTrigger } from '../../../../../../../../src/plugins/ui_actions/public'; import { EmbeddablePanel, - GetActionsCompatibleWithTrigger, GetEmbeddableFactory, GetEmbeddableFactories, } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; @@ -29,7 +29,7 @@ import { CoreStart } from '../../../../../../../../src/core/public'; import { Start as InspectorStartContract } from '../../../../../../../../src/plugins/inspector/public'; interface Props { - getActions: GetActionsCompatibleWithTrigger; + getActions: TGetActionsCompatibleWithTrigger; getEmbeddableFactory: GetEmbeddableFactory; getAllEmbeddableFactories: GetEmbeddableFactories; overlays: CoreStart['overlays']; diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/legacy.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/legacy.ts index 0d1db5d0e239c..1635cda5a7e31 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/legacy.ts +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/legacy.ts @@ -66,6 +66,7 @@ uiRoutes.when('/', { export const start = pluginInstance.start(npStart.core, { embeddable: embeddableStart, inspector: npStart.plugins.inspector, + uiActions: npStart.plugins.uiActions, __LEGACY: { SavedObjectFinder, ExitFullScreenButton, diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx index a7b0248d0deaf..dbccdba3b6383 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx @@ -19,6 +19,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { IUiActionsStart } from '../../../../../../../src/plugins/ui_actions/public'; +import { createHelloWorldAction } from '../../../../../../../src/plugins/ui_actions/public/tests/test_samples'; import { Start as InspectorStartContract, @@ -30,12 +32,10 @@ import { Plugin as EmbeddablePlugin, CONTEXT_MENU_TRIGGER } from './embeddable_a const REACT_ROOT_ID = 'embeddableExplorerRoot'; import { - HelloWorldAction, SayHelloAction, - SendMessageAction, + createSendMessageAction, HelloWorldEmbeddableFactory, ContactCardEmbeddableFactory, - HELLO_WORLD_ACTION_ID, } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/test_samples'; import { App } from './app'; @@ -50,6 +50,7 @@ export interface SetupDependencies { interface StartDependencies { embeddable: ReturnType; + uiActions: IUiActionsStart; inspector: InspectorStartContract; __LEGACY: { SavedObjectFinder: React.ComponentType; @@ -67,21 +68,21 @@ export class EmbeddableExplorerPublicPlugin public setup(core: CoreSetup, setupDeps: SetupDependencies): EmbeddableExplorerSetup {} public start(core: CoreStart, plugins: StartDependencies): EmbeddableExplorerStart { - const helloWorldAction = new HelloWorldAction(core.overlays); + const helloWorldAction = createHelloWorldAction(core.overlays); const sayHelloAction = new SayHelloAction(alert); - const sendMessageAction = new SendMessageAction(core.overlays); + const sendMessageAction = createSendMessageAction(core.overlays); const helloWorldEmbeddableFactory = new HelloWorldEmbeddableFactory(); const contactCardEmbeddableFactory = new ContactCardEmbeddableFactory( {}, - plugins.embeddable.executeTriggerActions, + plugins.uiActions.executeTriggerActions, core.overlays ); - plugins.embeddable.registerAction(helloWorldAction); - plugins.embeddable.registerAction(sayHelloAction); - plugins.embeddable.registerAction(sendMessageAction); + plugins.uiActions.registerAction(helloWorldAction); + plugins.uiActions.registerAction(sayHelloAction); + plugins.uiActions.registerAction(sendMessageAction); - plugins.embeddable.attachAction(CONTEXT_MENU_TRIGGER, HELLO_WORLD_ACTION_ID); + plugins.uiActions.attachAction(CONTEXT_MENU_TRIGGER, helloWorldAction.id); plugins.embeddable.registerEmbeddableFactory( helloWorldEmbeddableFactory.type, @@ -96,7 +97,7 @@ export class EmbeddableExplorerPublicPlugin const root = document.getElementById(REACT_ROOT_ID); ReactDOM.render( { - public readonly type = 'samplePanelAction'; - - constructor() { - super('samplePanelAction'); - } - - public getDisplayName() { - return 'Sample Panel Action'; - } - - public execute = async ({ embeddable }: ActionContext) => { - if (!embeddable) { - return; - } - npStart.core.overlays.openFlyout( - - - -

{embeddable.getTitle()}

-
-
- -

This is a sample action

-
-
, - { - 'data-test-subj': 'samplePanelActionFlyout', +function createSamplePanelAction() { + return createAction({ + type: 'samplePanelAction', + getDisplayName: () => 'Sample Panel Action', + execute: async ({ embeddable }) => { + if (!embeddable) { + return; } - ); - }; + npStart.core.overlays.openFlyout( + + + +

{embeddable.getTitle()}

+
+
+ +

This is a sample action

+
+
, + { + 'data-test-subj': 'samplePanelActionFlyout', + } + ); + }, + }); } -const action = new SamplePanelAction(); -setup.registerAction(action); -setup.attachAction(CONTEXT_MENU_TRIGGER, action.id); +const action = createSamplePanelAction(); +npSetup.plugins.uiActions.registerAction(action); +npSetup.plugins.uiActions.attachAction(CONTEXT_MENU_TRIGGER, action.id); diff --git a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/public/sample_panel_link.ts b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/public/sample_panel_link.ts index 75897d1b794b4..c020559cfb792 100644 --- a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/public/sample_panel_link.ts +++ b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/public/sample_panel_link.ts @@ -16,32 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -import { - Action, - CONTEXT_MENU_TRIGGER, -} from '../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { setup } from '../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; +import { npStart } from 'ui/new_platform'; +import { IAction, createAction } from '../../../../../src/plugins/ui_actions/public'; +import { CONTEXT_MENU_TRIGGER } from '../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -class SamplePanelLink extends Action { - public readonly type = 'samplePanelLink'; +export const createSamplePanelLink = (): IAction => + createAction({ + type: 'samplePanelLink', + getDisplayName: () => 'Sample panel Link', + execute: async () => {}, + getHref: () => 'https://example.com/kibana/test', + }); - constructor() { - super('samplePanelLink'); - } - - public getDisplayName() { - return 'Sample panel Link'; - } - - public async execute() { - return; - } - - public getHref = () => { - return 'https://example.com/kibana/test'; - }; -} - -const action = new SamplePanelLink(); -setup.registerAction(action); -setup.attachAction(CONTEXT_MENU_TRIGGER, action.id); +const action = createSamplePanelLink(); +npStart.plugins.uiActions.registerAction(action); +npStart.plugins.uiActions.attachAction(CONTEXT_MENU_TRIGGER, action.id); diff --git a/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/custom_time_range_action.tsx b/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/custom_time_range_action.tsx index 83675af94a5a8..f98e18fbf0e9e 100644 --- a/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/custom_time_range_action.tsx +++ b/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/custom_time_range_action.tsx @@ -6,15 +6,17 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; +import { + IAction, + IncompatibleActionError, +} from '../../../../../../../src/plugins/ui_actions/public'; import { TimeRange } from '../../../../../../../src/plugins/data/public'; import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../../../src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable'; import { VisualizeEmbeddable } from '../../../../../../../src/legacy/core_plugins/kibana/public/visualize/embeddable/visualize_embeddable'; import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../../../src/legacy/core_plugins/kibana/public/visualize/embeddable/constants'; import { - Action, IEmbeddable, - IncompatibleActionError, Embeddable, EmbeddableInput, } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; @@ -44,11 +46,13 @@ interface ActionContext { embeddable: Embeddable; } -export class CustomTimeRangeAction extends Action { +export class CustomTimeRangeAction implements IAction { public readonly type = CUSTOM_TIME_RANGE; private openModal: OpenModal; private dateFormat?: string; private commonlyUsedRanges: CommonlyUsedRange[]; + public readonly id = CUSTOM_TIME_RANGE; + public order = 7; constructor({ openModal, @@ -59,8 +63,6 @@ export class CustomTimeRangeAction extends Action { dateFormat: string; commonlyUsedRanges: CommonlyUsedRange[]; }) { - super(CUSTOM_TIME_RANGE); - this.order = 7; this.openModal = openModal; this.dateFormat = dateFormat; this.commonlyUsedRanges = commonlyUsedRanges; diff --git a/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/custom_time_range_badge.tsx b/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/custom_time_range_badge.tsx index 70cd74eb79d0c..29d8711297873 100644 --- a/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/custom_time_range_badge.tsx +++ b/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/custom_time_range_badge.tsx @@ -7,11 +7,13 @@ import React from 'react'; import { prettyDuration, commonDurationRanges } from '@elastic/eui'; +import { + IAction, + IncompatibleActionError, +} from '../../../../../../../src/plugins/ui_actions/public'; import { TimeRange } from '../../../../../../../src/plugins/data/public'; import { - Action, IEmbeddable, - IncompatibleActionError, Embeddable, EmbeddableInput, } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; @@ -36,8 +38,10 @@ interface ActionContext { embeddable: Embeddable; } -export class CustomTimeRangeBadge extends Action { +export class CustomTimeRangeBadge implements IAction { public readonly type = CUSTOM_TIME_RANGE_BADGE; + public readonly id = CUSTOM_TIME_RANGE_BADGE; + public order = 7; private openModal: OpenModal; private dateFormat: string; private commonlyUsedRanges: CommonlyUsedRange[]; @@ -51,8 +55,6 @@ export class CustomTimeRangeBadge extends Action { dateFormat: string; commonlyUsedRanges: CommonlyUsedRange[]; }) { - super(CUSTOM_TIME_RANGE_BADGE); - this.order = 7; this.openModal = openModal; this.dateFormat = dateFormat; this.commonlyUsedRanges = commonlyUsedRanges; diff --git a/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/legacy.ts b/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/legacy.ts index 18a2a4181040f..65580d25d08ce 100644 --- a/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/legacy.ts +++ b/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/legacy.ts @@ -21,4 +21,5 @@ export const setup = pluginInstance.setup(npSetup.core, { }); export const start = pluginInstance.start(npStart.core, { embeddable: embeddableStart, + uiActions: npStart.plugins.uiActions, }); diff --git a/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/plugin.ts b/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/plugin.ts index afd3ccc93f21c..871d9f9536285 100644 --- a/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/plugin.ts +++ b/x-pack/legacy/plugins/advanced_ui_actions/public/np_ready/public/plugin.ts @@ -5,6 +5,7 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { IUiActionsStart } from 'src/plugins/ui_actions/public'; import { Plugin as EmbeddablePlugin, CONTEXT_MENU_TRIGGER, @@ -21,6 +22,7 @@ interface SetupDependencies { interface StartDependencies { embeddable: ReturnType; + uiActions: IUiActionsStart; } export type Setup = void; @@ -32,7 +34,7 @@ export class AdvancedUiActionsPublicPlugin public setup(core: CoreSetup, { embeddable }: SetupDependencies): Setup {} - public start(core: CoreStart, { embeddable }: StartDependencies): Start { + public start(core: CoreStart, { embeddable, uiActions }: StartDependencies): Start { const dateFormat = core.uiSettings.get('dateFormat') as string; const commonlyUsedRanges = core.uiSettings.get('timepicker:quickRanges') as CommonlyUsedRange[]; const timeRangeAction = new CustomTimeRangeAction({ @@ -40,16 +42,16 @@ export class AdvancedUiActionsPublicPlugin dateFormat, commonlyUsedRanges, }); - embeddable.registerAction(timeRangeAction); - embeddable.attachAction(CONTEXT_MENU_TRIGGER, timeRangeAction.id); + uiActions.registerAction(timeRangeAction); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, timeRangeAction.id); const timeRangeBadge = new CustomTimeRangeBadge({ openModal: core.overlays.openModal, dateFormat, commonlyUsedRanges, }); - embeddable.registerAction(timeRangeBadge); - embeddable.attachAction(PANEL_BADGE_TRIGGER, timeRangeBadge.id); + uiActions.registerAction(timeRangeBadge); + uiActions.attachAction(PANEL_BADGE_TRIGGER, timeRangeBadge.id); } public stop() {} diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable.tsx b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable.tsx index cf47ebab09c65..8df365d661aef 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable.tsx +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable.tsx @@ -39,7 +39,7 @@ const renderEmbeddable = (embeddableObject: IEmbeddable, domNode: HTMLElement) = { - start.executeTriggerActions(APPLY_FILTER_TRIGGER, { + npStart.plugins.uiActions.executeTriggerActions(APPLY_FILTER_TRIGGER, { embeddable: this, filters, }); diff --git a/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index c1812cb40cf23..fd33bc79eeaf8 100644 --- a/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; import dateMath from '@elastic/datemath'; import { i18n } from '@kbn/i18n'; import moment from 'moment-timezone'; @@ -11,16 +10,15 @@ import moment from 'moment-timezone'; import { kfetch } from 'ui/kfetch'; import { toastNotifications } from 'ui/notify'; import chrome from 'ui/chrome'; -import { EuiIcon } from '@elastic/eui'; + +import { npSetup } from 'ui/new_platform'; +import { IAction, IncompatibleActionError } from '../../../../../../src/plugins/ui_actions/public'; import { - Action, ViewMode, - IncompatibleActionError, IEmbeddable, CONTEXT_MENU_TRIGGER, } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { setup } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { ISearchEmbeddable, SEARCH_EMBEDDABLE_TYPE, @@ -41,18 +39,17 @@ interface ActionContext { embeddable: ISearchEmbeddable; } -class GetCsvReportPanelAction extends Action { +class GetCsvReportPanelAction implements IAction { private isDownloading: boolean; public readonly type = CSV_REPORTING_ACTION; + public readonly id = CSV_REPORTING_ACTION; constructor() { - super(CSV_REPORTING_ACTION); - this.isDownloading = false; } - public getIcon() { - return ; + public getIconType() { + return 'document'; } public getDisplayName() { @@ -179,5 +176,5 @@ class GetCsvReportPanelAction extends Action { } const action = new GetCsvReportPanelAction(); -setup.registerAction(action); -setup.attachAction(CONTEXT_MENU_TRIGGER, action.id); +npSetup.plugins.uiActions.registerAction(action); +npSetup.plugins.uiActions.attachAction(CONTEXT_MENU_TRIGGER, action.id); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.test.tsx index 731cc034f9778..0c17874073d55 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.test.tsx @@ -13,7 +13,6 @@ import { } from './apply_siem_filter_action'; // @ts-ignore Missing type defs as maps moves to Typescript import { MAP_SAVED_OBJECT_TYPE } from '../../../../../maps/common/constants'; -import { Action } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions'; import { expectError } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/tests/helpers'; import { EmbeddableInput, @@ -36,11 +35,6 @@ describe('ApplySiemFilterAction', () => { applyFilterQueryFromKueryExpression = jest.fn(expression => {}); }); - test('it is an instance of Action', () => { - const action = new ApplySiemFilterAction({ applyFilterQueryFromKueryExpression }); - expect(action).toBeInstanceOf(Action); - }); - test('it has APPLY_SIEM_FILTER_ACTION_ID type and id', () => { const action = new ApplySiemFilterAction({ applyFilterQueryFromKueryExpression }); expect(action.id).toBe('APPLY_SIEM_FILTER_ACTION_ID'); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.tsx index 961ae1207df27..92bcfcdd8ab15 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.tsx @@ -7,9 +7,9 @@ import { Filter } from '@kbn/es-query'; import { getOr } from 'lodash/fp'; import { i18n } from '@kbn/i18n'; +import { IAction } from 'src/plugins/ui_actions/public'; // @ts-ignore Missing type defs as maps moves to Typescript import { MAP_SAVED_OBJECT_TYPE } from '../../../../../maps/common/constants'; -import { Action } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/actions'; import { IEmbeddable } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/lib/embeddables'; export const APPLY_SIEM_FILTER_ACTION_ID = 'APPLY_SIEM_FILTER_ACTION_ID'; @@ -19,16 +19,16 @@ interface ActionContext { filters: Filter[]; } -export class ApplySiemFilterAction extends Action { +export class ApplySiemFilterAction implements IAction { public readonly type = APPLY_SIEM_FILTER_ACTION_ID; private readonly applyFilterQueryFromKueryExpression: (expression: string) => void; + public id = APPLY_SIEM_FILTER_ACTION_ID; constructor({ applyFilterQueryFromKueryExpression, }: { applyFilterQueryFromKueryExpression: (filterQuery: string) => void; }) { - super(APPLY_SIEM_FILTER_ACTION_ID); this.applyFilterQueryFromKueryExpression = applyFilterQueryFromKueryExpression; } @@ -38,6 +38,10 @@ export class ApplySiemFilterAction extends Action { }); } + public getIconType() { + return undefined; + } + public async isCompatible(context: ActionContext): Promise { return context.embeddable.type === MAP_SAVED_OBJECT_TYPE && context.filters !== undefined; } diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx index efed190825673..83253f6e4de4b 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx @@ -17,6 +17,9 @@ jest.mock('ui/new_platform', () => ({ getKibanaVersion: () => '8.0.0', }, }, + plugins: { + uiActions: require('../../../../../../../src/plugins/ui_actions/public/mocks').uiActionsPluginMock.createSetupContract(), + }, }, npSetup: { core: { @@ -24,6 +27,9 @@ jest.mock('ui/new_platform', () => ({ get$: () => 'world', }, }, + plugins: { + uiActions: require('../../../../../../../src/plugins/ui_actions/public/mocks').uiActionsPluginMock.createStartContract(), + }, }, })); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx index 370dafa4a384b..86696503dbda3 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx @@ -134,7 +134,7 @@ export const EmbeddedMap = React.memo( ({ start: { - getTriggerActions: jest.fn(() => []), - registerAction: jest.fn(), - attachAction: jest.fn(), - detachAction: jest.fn(), getEmbeddableFactory: () => ({ createFromState: () => ({ reload: jest.fn(), @@ -55,8 +52,8 @@ describe('embedded_map_helpers', () => { test('attaches SIEM_FILTER_ACTION, and detaches extra UI actions', () => { const applyFilterMock = jest.fn(); setupEmbeddablesAPI(applyFilterMock); - expect(start.registerAction).toHaveBeenCalledTimes(1); - expect(start.detachAction).toHaveBeenCalledTimes(3); + expect(npStart.plugins.uiActions.registerAction).toHaveBeenCalledTimes(1); + expect(npStart.plugins.uiActions.detachAction).toHaveBeenCalledTimes(3); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index a6cc9f9b8b00b..cb1fa30fce26e 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -5,6 +5,7 @@ */ import uuid from 'uuid'; +import { npStart } from 'ui/new_platform'; import { ActionToaster, AppToast } from '../toasters'; import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { @@ -61,18 +62,18 @@ export const setupEmbeddablesAPI = ( applyFilterQueryFromKueryExpression: (expression: string) => void ) => { try { - const actions = start.getTriggerActions(APPLY_FILTER_TRIGGER); + const actions = npStart.plugins.uiActions.getTriggerActions(APPLY_FILTER_TRIGGER); const actionLoaded = actions.some(a => a.id === APPLY_SIEM_FILTER_ACTION_ID); if (!actionLoaded) { const siemFilterAction = new ApplySiemFilterAction({ applyFilterQueryFromKueryExpression, }); - start.registerAction(siemFilterAction); - start.attachAction(APPLY_FILTER_TRIGGER, siemFilterAction.id); + npStart.plugins.uiActions.registerAction(siemFilterAction); + npStart.plugins.uiActions.attachAction(APPLY_FILTER_TRIGGER, siemFilterAction.id); - start.detachAction(CONTEXT_MENU_TRIGGER, 'CUSTOM_TIME_RANGE'); - start.detachAction(PANEL_BADGE_TRIGGER, 'CUSTOM_TIME_RANGE_BADGE'); - start.detachAction(APPLY_FILTER_TRIGGER, APPLY_FILTER_ACTION); + npStart.plugins.uiActions.detachAction(CONTEXT_MENU_TRIGGER, 'CUSTOM_TIME_RANGE'); + npStart.plugins.uiActions.detachAction(PANEL_BADGE_TRIGGER, 'CUSTOM_TIME_RANGE_BADGE'); + npStart.plugins.uiActions.detachAction(APPLY_FILTER_TRIGGER, APPLY_FILTER_ACTION); } } catch (e) { throw e; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.test.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.test.tsx index 8bf91f1e5700f..3351048e1cdea 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.test.tsx @@ -17,6 +17,7 @@ import { TestProviders } from '../../mock'; import { MockedProvider } from 'react-apollo/test-utils'; import { cloneDeep } from 'lodash/fp'; +jest.mock('ui/new_platform'); jest.mock('../../lib/settings/use_kibana_ui_setting'); jest.mock('ui/documentation_links', () => ({ diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0d55d10a94f3c..d3bff6356e4c1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -484,8 +484,6 @@ "common.ui.management.editIndexPattern.createIndex.defaultTypeName": "インデックスパターン", "common.ui.management.nav.menu": "管理メニュー", "common.ui.modals.cancelButtonLabel": "キャンセル", - "core.ui.overlays.banner.attentionTitle": "注意", - "core.ui.overlays.banner.closeButtonLabel": "閉じる", "common.ui.notify.fatalError.errorStatusMessage": "エラー {errStatus} {errStatusText}: {errMessage}", "common.ui.notify.fatalError.unavailableServerErrorMessage": "HTTP リクエストが接続に失敗しました。Kibana サーバーが実行されていて、ご使用のブラウザの接続が正常に動作していることを確認するか、システム管理者にお問い合わせください。", "common.ui.notify.toaster.errorMessage": "エラー: {errorMessage}\n {errorStack}", @@ -595,47 +593,8 @@ "common.ui.directives.fieldNameIcons.geoShapeFieldAriaLabel": "地理情報図形", "common.ui.vis.editors.agg.errorsAriaLabel": "集約にエラーがあります", "common.ui.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr}).構成されている最高値は {max} です。", - "kibana-react.exitFullScreenButton.exitFullScreenModeButtonAreaLabel": "全画面モードを終了", - "kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "全画面を終了", - "kibana-react.exitFullScreenButton.fullScreenModeDescription": "ESC キーで全画面モードを終了します。", - "inspector.closeButton": "インスペクターを閉じる", - "inspector.reqTimestampDescription": "リクエストの開始が記録された時刻です", - "inspector.reqTimestampKey": "リクエストのタイムスタンプ", - "inspector.title": "インスペクター", - "inspector.view": "{viewName} を表示", - "inspector.data.dataDescriptionTooltip": "ビジュアライゼーションの元のデータを表示", - "inspector.data.dataTitle": "データ", - "inspector.data.downloadCSVButtonLabel": "CSV をダウンロード", - "inspector.data.downloadCSVToggleButtonLabel": "CSV をダウンロード", - "inspector.data.filterForValueButtonAriaLabel": "値でフィルタリング", - "inspector.data.filterForValueButtonTooltip": "値でフィルタリング", - "inspector.data.filterOutValueButtonAriaLabel": "値を除外", - "inspector.data.filterOutValueButtonTooltip": "値を除外", - "inspector.data.formattedCSVButtonLabel": "フォーマット済み CSV", - "inspector.data.formattedCSVButtonTooltip": "データを表形式でダウンロード", - "inspector.data.gatheringDataLabel": "データを収集中", - "inspector.data.noDataAvailableDescription": "エレメントがデータを提供しませんでした。", - "inspector.data.noDataAvailableTitle": "利用可能なデータがありません", - "inspector.data.rawCSVButtonLabel": "CSV", - "inspector.data.rawCSVButtonTooltip": "日付をタイムスタンプとしてなど、提供されたデータをそのままダウンロードします", - "inspector.requests.descriptionRowIconAriaLabel": "説明", - "inspector.requests.failedLabel": " (失敗)", - "inspector.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText": "エレメントが (まだ) リクエストを記録していません。", - "inspector.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText": "これは通常、データを取得する必要がないか、エレメントがまだデータの取得を開始していないことを意味します。", - "inspector.requests.noRequestsLoggedTitle": "リクエストが記録されていません", - "inspector.requests.requestFailedTooltipTitle": "リクエストに失敗しました", - "inspector.requests.requestInProgressAriaLabel": "リクエスト進行中", - "inspector.requests.requestLabel": "リクエスト", - "inspector.requests.requestsDescriptionTooltip": "データを収集したリクエストを表示します", - "inspector.requests.requestsTitle": "リクエスト", - "inspector.requests.requestSucceededTooltipTitle": "リクエスト成功", - "inspector.requests.requestTabLabel": "リクエスト", - "inspector.requests.requestTimeLabel": "{requestTime}ms", - "inspector.requests.requestTooltipDescription": "リクエストの合計所要時間です。", - "inspector.requests.requestWasMadeDescription": "{requestsCount, plural, one {# リクエストが} other {# リクエストが} } 行われました{failedRequests}", - "inspector.requests.requestWasMadeDescription.requestHadFailureText": "、{failedCount} 件に失敗がありました", - "inspector.requests.responseTabLabel": "応答", - "inspector.requests.statisticsTabLabel": "統計", + "core.ui.overlays.banner.attentionTitle": "注意", + "core.ui.overlays.banner.closeButtonLabel": "閉じる", "core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel": "ホームページに移動", "core.ui.chrome.headerGlobalNav.helpMenuButtonAriaLabel": "ヘルプメニュー", "core.ui.chrome.headerGlobalNav.helpMenuGoToDocumentation": "ドキュメントに移動", @@ -715,6 +674,47 @@ "core.euiSuperUpdateButton.refreshButtonLabel": "更新", "core.euiSuperUpdateButton.updateButtonLabel": "更新", "core.euiSuperUpdateButton.updatingButtonLabel": "更新中", + "kibana-react.exitFullScreenButton.exitFullScreenModeButtonAreaLabel": "全画面モードを終了", + "kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "全画面を終了", + "kibana-react.exitFullScreenButton.fullScreenModeDescription": "ESC キーで全画面モードを終了します。", + "inspector.closeButton": "インスペクターを閉じる", + "inspector.reqTimestampDescription": "リクエストの開始が記録された時刻です", + "inspector.reqTimestampKey": "リクエストのタイムスタンプ", + "inspector.title": "インスペクター", + "inspector.view": "{viewName} を表示", + "inspector.data.dataDescriptionTooltip": "ビジュアライゼーションの元のデータを表示", + "inspector.data.dataTitle": "データ", + "inspector.data.downloadCSVButtonLabel": "CSV をダウンロード", + "inspector.data.downloadCSVToggleButtonLabel": "CSV をダウンロード", + "inspector.data.filterForValueButtonAriaLabel": "値でフィルタリング", + "inspector.data.filterForValueButtonTooltip": "値でフィルタリング", + "inspector.data.filterOutValueButtonAriaLabel": "値を除外", + "inspector.data.filterOutValueButtonTooltip": "値を除外", + "inspector.data.formattedCSVButtonLabel": "フォーマット済み CSV", + "inspector.data.formattedCSVButtonTooltip": "データを表形式でダウンロード", + "inspector.data.gatheringDataLabel": "データを収集中", + "inspector.data.noDataAvailableDescription": "エレメントがデータを提供しませんでした。", + "inspector.data.noDataAvailableTitle": "利用可能なデータがありません", + "inspector.data.rawCSVButtonLabel": "CSV", + "inspector.data.rawCSVButtonTooltip": "日付をタイムスタンプとしてなど、提供されたデータをそのままダウンロードします", + "inspector.requests.descriptionRowIconAriaLabel": "説明", + "inspector.requests.failedLabel": " (失敗)", + "inspector.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText": "エレメントが (まだ) リクエストを記録していません。", + "inspector.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText": "これは通常、データを取得する必要がないか、エレメントがまだデータの取得を開始していないことを意味します。", + "inspector.requests.noRequestsLoggedTitle": "リクエストが記録されていません", + "inspector.requests.requestFailedTooltipTitle": "リクエストに失敗しました", + "inspector.requests.requestInProgressAriaLabel": "リクエスト進行中", + "inspector.requests.requestLabel": "リクエスト", + "inspector.requests.requestsDescriptionTooltip": "データを収集したリクエストを表示します", + "inspector.requests.requestsTitle": "リクエスト", + "inspector.requests.requestSucceededTooltipTitle": "リクエスト成功", + "inspector.requests.requestTabLabel": "リクエスト", + "inspector.requests.requestTimeLabel": "{requestTime}ms", + "inspector.requests.requestTooltipDescription": "リクエストの合計所要時間です。", + "inspector.requests.requestWasMadeDescription": "{requestsCount, plural, one {# リクエストが} other {# リクエストが} } 行われました{failedRequests}", + "inspector.requests.requestWasMadeDescription.requestHadFailureText": "、{failedCount} 件に失敗がありました", + "inspector.requests.responseTabLabel": "応答", + "inspector.requests.statisticsTabLabel": "統計", "console.autocomplete.addMethodMetaText": "メソド", "console.consoleDisplayName": "コンソール", "console.helpPage.keyboardCommands.autoIndentDescription": "現在のリクエストを自動インデントします", @@ -875,7 +875,6 @@ "data.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel": "{savedQueryName} の説明", "data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel": "選択されたクエリボタン {savedQueryName} を保存しました。変更を破棄するには押してください。", "data.search.searchBar.savedQueryPopoverTitleText": "保存されたクエリ", - "embeddableApi.actionPanel.title": "オプション", "embeddableApi.actions.applyFilterActionTitle": "現在のビューにフィルターを適用", "embeddableApi.addPanel.createNew": "新規 {factoryName} を作成", "embeddableApi.addPanel.createNewDefaultOption": "新規作成...", @@ -893,7 +892,6 @@ "embeddableApi.customizeTitle.optionsMenuForm.panelTitleInputAriaLabel": "このインプットへの変更は直ちに適用されます。Enter を押して閉じます。", "embeddableApi.customizeTitle.optionsMenuForm.resetCustomDashboardButtonLabel": "タイトルをリセット", "embeddableApi.errors.embeddableFactoryNotFound": "{type} を読み込めません。Elasticsearch と Kibana のデフォルトのディストリビューションを適切なライセンスでアップグレードしてください。", - "embeddableApi.errors.incompatibleAction": "操作に互換性がありません", "embeddableApi.errors.paneldoesNotExist": "パネルが見つかりません", "embeddableApi.panel.dashboardPanelAriaLabel": "ダッシュボードパネル: {title}", "embeddableApi.panel.editPanel.displayName": "{value} を編集", @@ -11601,4 +11599,4 @@ "xpack.fileUpload.fileParser.noFileProvided": "エラー、ファイルが提供されていません", "xpack.fileUpload.jsonIndexFilePicker.errorGettingIndexName": "インデックス名の取得中にエラーが発生: {errorMessage}" } -} +} \ No newline at end of file diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 29fe28ca16629..0be8eaab4bb04 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -484,8 +484,6 @@ "common.ui.management.editIndexPattern.createIndex.defaultTypeName": "索引模式", "common.ui.management.nav.menu": "管理菜单", "common.ui.modals.cancelButtonLabel": "取消", - "core.ui.overlays.banner.attentionTitle": "注意", - "core.ui.overlays.banner.closeButtonLabel": "关闭", "common.ui.notify.fatalError.errorStatusMessage": "错误 {errStatus} {errStatusText}:{errMessage}", "common.ui.notify.fatalError.unavailableServerErrorMessage": "HTTP 请求无法连接。请检查 Kibana 服务器是否正在运行以及您的浏览器是否具有有效的连接,或请联系您的系统管理员。", "common.ui.notify.toaster.errorMessage": "错误:{errorMessage}\n {errorStack}", @@ -596,47 +594,8 @@ "common.ui.directives.fieldNameIcons.geoShapeFieldAriaLabel": "几何形状", "common.ui.vis.editors.agg.errorsAriaLabel": "聚合有错误", "common.ui.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。", - "kibana-react.exitFullScreenButton.exitFullScreenModeButtonAreaLabel": "退出全屏模式", - "kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "退出全屏", - "kibana-react.exitFullScreenButton.fullScreenModeDescription": "在全屏模式下,按 ESC 键可退出。", - "inspector.closeButton": "关闭检查器", - "inspector.reqTimestampDescription": "记录请求启动的时间", - "inspector.reqTimestampKey": "请求时间戳", - "inspector.title": "检查器", - "inspector.view": "视图:{viewName}", - "inspector.data.dataDescriptionTooltip": "查看可视化后面的数据", - "inspector.data.dataTitle": "数据", - "inspector.data.downloadCSVButtonLabel": "下载 CSV", - "inspector.data.downloadCSVToggleButtonLabel": "下载 CSV", - "inspector.data.filterForValueButtonAriaLabel": "筛留值", - "inspector.data.filterForValueButtonTooltip": "筛留值", - "inspector.data.filterOutValueButtonAriaLabel": "筛除值", - "inspector.data.filterOutValueButtonTooltip": "筛除值", - "inspector.data.formattedCSVButtonLabel": "格式化 CSV", - "inspector.data.formattedCSVButtonTooltip": "以表格式下载数据", - "inspector.data.gatheringDataLabel": "正在收集数据", - "inspector.data.noDataAvailableDescription": "该元素未提供任何数据。", - "inspector.data.noDataAvailableTitle": "没有可用数据", - "inspector.data.rawCSVButtonLabel": "原始 CSV", - "inspector.data.rawCSVButtonTooltip": "按原样下载数据,例如将日期作为时间戳下载", - "inspector.requests.descriptionRowIconAriaLabel": "描述", - "inspector.requests.failedLabel": " (失败)", - "inspector.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText": "该元素尚未记录任何请求。", - "inspector.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText": "这通常表示无需提取任何数据,或该元素尚未开始提取数据。", - "inspector.requests.noRequestsLoggedTitle": "未记录任何请求", - "inspector.requests.requestFailedTooltipTitle": "请求失败", - "inspector.requests.requestInProgressAriaLabel": "请求进行中", - "inspector.requests.requestLabel": "请求:", - "inspector.requests.requestsDescriptionTooltip": "查看已收集数据的请求", - "inspector.requests.requestsTitle": "请求", - "inspector.requests.requestSucceededTooltipTitle": "请求成功", - "inspector.requests.requestTabLabel": "请求", - "inspector.requests.requestTimeLabel": "{requestTime}ms", - "inspector.requests.requestTooltipDescription": "请求所花费的总时间。", - "inspector.requests.requestWasMadeDescription": "{requestsCount, plural, one {# 个请求已} other {# 个请求已} }发出{failedRequests}", - "inspector.requests.requestWasMadeDescription.requestHadFailureText": ",{failedCount} 个失败", - "inspector.requests.responseTabLabel": "响应", - "inspector.requests.statisticsTabLabel": "统计信息", + "core.ui.overlays.banner.attentionTitle": "注意", + "core.ui.overlays.banner.closeButtonLabel": "关闭", "core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel": "前往主页", "core.ui.chrome.headerGlobalNav.helpMenuButtonAriaLabel": "帮助菜单", "core.ui.chrome.headerGlobalNav.helpMenuGoToDocumentation": "前往文档", @@ -716,6 +675,47 @@ "core.euiSuperUpdateButton.refreshButtonLabel": "刷新", "core.euiSuperUpdateButton.updateButtonLabel": "更新", "core.euiSuperUpdateButton.updatingButtonLabel": "正在更新", + "kibana-react.exitFullScreenButton.exitFullScreenModeButtonAreaLabel": "退出全屏模式", + "kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "退出全屏", + "kibana-react.exitFullScreenButton.fullScreenModeDescription": "在全屏模式下,按 ESC 键可退出。", + "inspector.closeButton": "关闭检查器", + "inspector.reqTimestampDescription": "记录请求启动的时间", + "inspector.reqTimestampKey": "请求时间戳", + "inspector.title": "检查器", + "inspector.view": "视图:{viewName}", + "inspector.data.dataDescriptionTooltip": "查看可视化后面的数据", + "inspector.data.dataTitle": "数据", + "inspector.data.downloadCSVButtonLabel": "下载 CSV", + "inspector.data.downloadCSVToggleButtonLabel": "下载 CSV", + "inspector.data.filterForValueButtonAriaLabel": "筛留值", + "inspector.data.filterForValueButtonTooltip": "筛留值", + "inspector.data.filterOutValueButtonAriaLabel": "筛除值", + "inspector.data.filterOutValueButtonTooltip": "筛除值", + "inspector.data.formattedCSVButtonLabel": "格式化 CSV", + "inspector.data.formattedCSVButtonTooltip": "以表格式下载数据", + "inspector.data.gatheringDataLabel": "正在收集数据", + "inspector.data.noDataAvailableDescription": "该元素未提供任何数据。", + "inspector.data.noDataAvailableTitle": "没有可用数据", + "inspector.data.rawCSVButtonLabel": "原始 CSV", + "inspector.data.rawCSVButtonTooltip": "按原样下载数据,例如将日期作为时间戳下载", + "inspector.requests.descriptionRowIconAriaLabel": "描述", + "inspector.requests.failedLabel": " (失败)", + "inspector.requests.noRequestsLoggedDescription.elementHasNotLoggedAnyRequestsText": "该元素尚未记录任何请求。", + "inspector.requests.noRequestsLoggedDescription.whatDoesItUsuallyMeanText": "这通常表示无需提取任何数据,或该元素尚未开始提取数据。", + "inspector.requests.noRequestsLoggedTitle": "未记录任何请求", + "inspector.requests.requestFailedTooltipTitle": "请求失败", + "inspector.requests.requestInProgressAriaLabel": "请求进行中", + "inspector.requests.requestLabel": "请求:", + "inspector.requests.requestsDescriptionTooltip": "查看已收集数据的请求", + "inspector.requests.requestsTitle": "请求", + "inspector.requests.requestSucceededTooltipTitle": "请求成功", + "inspector.requests.requestTabLabel": "请求", + "inspector.requests.requestTimeLabel": "{requestTime}ms", + "inspector.requests.requestTooltipDescription": "请求所花费的总时间。", + "inspector.requests.requestWasMadeDescription": "{requestsCount, plural, one {# 个请求已} other {# 个请求已} }发出{failedRequests}", + "inspector.requests.requestWasMadeDescription.requestHadFailureText": ",{failedCount} 个失败", + "inspector.requests.responseTabLabel": "响应", + "inspector.requests.statisticsTabLabel": "统计信息", "console.autocomplete.addMethodMetaText": "方法", "console.consoleDisplayName": "Console", "console.helpPage.keyboardCommands.autoIndentDescription": "自动缩进当前请求", @@ -876,7 +876,6 @@ "data.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel": "{savedQueryName} 描述", "data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel": "已保存查询按钮已选择 {savedQueryName}。按下可清除任何更改。", "data.search.searchBar.savedQueryPopoverTitleText": "已保存查询", - "embeddableApi.actionPanel.title": "选项", "embeddableApi.actions.applyFilterActionTitle": "将筛选应用于当前视图", "embeddableApi.addPanel.createNew": "创建新的{factoryName}", "embeddableApi.addPanel.createNewDefaultOption": "创建新的......", @@ -894,7 +893,6 @@ "embeddableApi.customizeTitle.optionsMenuForm.panelTitleInputAriaLabel": "对此输入的更改将立即应用。按 enter 键可退出。", "embeddableApi.customizeTitle.optionsMenuForm.resetCustomDashboardButtonLabel": "重置标题", "embeddableApi.errors.embeddableFactoryNotFound": "{type} 无法加载。请升级到具有适当许可的默认 Elasticsearch 和 Kibana 分发。", - "embeddableApi.errors.incompatibleAction": "操作不兼容", "embeddableApi.errors.paneldoesNotExist": "未找到面板", "embeddableApi.panel.dashboardPanelAriaLabel": "仪表板面板:{title}", "embeddableApi.panel.editPanel.displayName": "编辑 {value}", @@ -11603,4 +11601,4 @@ "xpack.fileUpload.fileParser.noFileProvided": "错误,未提供任何文件", "xpack.fileUpload.jsonIndexFilePicker.errorGettingIndexName": "检索索引名称时出错:{errorMessage}" } -} +} \ No newline at end of file