mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* Improve types so emitting the wrong context shape complains, as does using a trigger id that has not been added to the trigger context mapping. * remove unneccessary code
This commit is contained in:
parent
1dfb5386e1
commit
f0eaad05d7
35 changed files with 279 additions and 220 deletions
|
@ -24,11 +24,16 @@ import { toMountPoint } from '../../../src/plugins/kibana_react/public';
|
|||
|
||||
export const HELLO_WORLD_ACTION_TYPE = 'HELLO_WORLD_ACTION_TYPE';
|
||||
|
||||
export const createHelloWorldAction = (openModal: OverlayStart['openModal']) =>
|
||||
createAction<{}>({
|
||||
interface StartServices {
|
||||
openModal: OverlayStart['openModal'];
|
||||
}
|
||||
|
||||
export const createHelloWorldAction = (getStartServices: () => Promise<StartServices>) =>
|
||||
createAction({
|
||||
type: HELLO_WORLD_ACTION_TYPE,
|
||||
getDisplayName: () => 'Hello World!',
|
||||
execute: async () => {
|
||||
const { openModal } = await getStartServices();
|
||||
const overlay = openModal(
|
||||
toMountPoint(
|
||||
<EuiModalBody>
|
||||
|
|
|
@ -17,30 +17,34 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public';
|
||||
import { UiActionsSetup, UiActionsStart } from '../../../src/plugins/ui_actions/public';
|
||||
import { createHelloWorldAction, HELLO_WORLD_ACTION_TYPE } from './hello_world_action';
|
||||
import { helloWorldTrigger } from './hello_world_trigger';
|
||||
import { Plugin, CoreSetup } from '../../../src/core/public';
|
||||
import { UiActionsSetup } from '../../../src/plugins/ui_actions/public';
|
||||
import { createHelloWorldAction } from './hello_world_action';
|
||||
import { helloWorldTrigger, HELLO_WORLD_TRIGGER_ID } from './hello_world_trigger';
|
||||
|
||||
interface UiActionExamplesSetupDependencies {
|
||||
uiActions: UiActionsSetup;
|
||||
}
|
||||
|
||||
interface UiActionExamplesStartDependencies {
|
||||
uiActions: UiActionsStart;
|
||||
declare module '../../../src/plugins/ui_actions/public' {
|
||||
export interface TriggerContextMapping {
|
||||
[HELLO_WORLD_TRIGGER_ID]: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class UiActionExamplesPlugin
|
||||
implements
|
||||
Plugin<void, void, UiActionExamplesSetupDependencies, UiActionExamplesStartDependencies> {
|
||||
implements Plugin<void, void, UiActionExamplesSetupDependencies> {
|
||||
public setup(core: CoreSetup, { uiActions }: UiActionExamplesSetupDependencies) {
|
||||
uiActions.registerTrigger(helloWorldTrigger);
|
||||
uiActions.attachAction(helloWorldTrigger.id, HELLO_WORLD_ACTION_TYPE);
|
||||
}
|
||||
|
||||
public start(coreStart: CoreStart, deps: UiActionExamplesStartDependencies) {
|
||||
deps.uiActions.registerAction(createHelloWorldAction(coreStart.overlays.openModal));
|
||||
|
||||
const helloWorldAction = createHelloWorldAction(async () => ({
|
||||
openModal: (await core.getStartServices())[0].overlays.openModal,
|
||||
}));
|
||||
|
||||
uiActions.registerAction(helloWorldAction);
|
||||
uiActions.attachAction(helloWorldTrigger.id, helloWorldAction.id);
|
||||
}
|
||||
|
||||
public start() {}
|
||||
public stop() {}
|
||||
}
|
||||
|
|
|
@ -34,16 +34,18 @@ export const EDIT_USER_ACTION = 'EDIT_USER_ACTION';
|
|||
export const PHONE_USER_ACTION = 'PHONE_USER_ACTION';
|
||||
export const SHOWCASE_PLUGGABILITY_ACTION = 'SHOWCASE_PLUGGABILITY_ACTION';
|
||||
|
||||
export const showcasePluggability = createAction<{}>({
|
||||
export const showcasePluggability = createAction({
|
||||
type: SHOWCASE_PLUGGABILITY_ACTION,
|
||||
getDisplayName: () => 'This is pluggable! Any plugin can inject their actions here.',
|
||||
execute: async ({}) => alert("Isn't that cool?!"),
|
||||
execute: async () => alert("Isn't that cool?!"),
|
||||
});
|
||||
|
||||
export const makePhoneCallAction = createAction<{ phone: string }>({
|
||||
export type PhoneContext = string;
|
||||
|
||||
export const makePhoneCallAction = createAction<PhoneContext>({
|
||||
type: CALL_PHONE_NUMBER_ACTION,
|
||||
getDisplayName: () => 'Call phone number',
|
||||
execute: async ({ phone }) => alert(`Pretend calling ${phone}...`),
|
||||
execute: async phone => alert(`Pretend calling ${phone}...`),
|
||||
});
|
||||
|
||||
export const lookUpWeatherAction = createAction<{ country: string }>({
|
||||
|
@ -55,11 +57,13 @@ export const lookUpWeatherAction = createAction<{ country: string }>({
|
|||
},
|
||||
});
|
||||
|
||||
export const viewInMapsAction = createAction<{ country: string }>({
|
||||
export type CountryContext = string;
|
||||
|
||||
export const viewInMapsAction = createAction<CountryContext>({
|
||||
type: VIEW_IN_MAPS_ACTION,
|
||||
getIconType: () => 'popout',
|
||||
getDisplayName: () => 'View in maps',
|
||||
execute: async ({ country }) => {
|
||||
execute: async country => {
|
||||
window.open(`https://www.google.com/maps/place/${country}`, '_blank');
|
||||
},
|
||||
});
|
||||
|
@ -110,11 +114,13 @@ export const createEditUserAction = (getOpenModal: () => Promise<OverlayStart['o
|
|||
},
|
||||
});
|
||||
|
||||
export interface UserContext {
|
||||
user: User;
|
||||
update: (user: User) => void;
|
||||
}
|
||||
|
||||
export const createPhoneUserAction = (getUiActionsApi: () => Promise<UiActionsStart>) =>
|
||||
createAction<{
|
||||
user: User;
|
||||
update: (user: User) => void;
|
||||
}>({
|
||||
createAction<UserContext>({
|
||||
type: PHONE_USER_ACTION,
|
||||
getDisplayName: () => 'Call phone number',
|
||||
isCompatible: async ({ user }) => user.phone !== undefined,
|
||||
|
@ -126,6 +132,8 @@ export const createPhoneUserAction = (getUiActionsApi: () => Promise<UiActionsSt
|
|||
// to the phone number trigger.
|
||||
// TODO: we need to figure out the best way to handle these nested actions however, since
|
||||
// we don't want multiple context menu's to pop up.
|
||||
(await getUiActionsApi()).executeTriggerActions(PHONE_TRIGGER, { phone: user.phone });
|
||||
if (user.phone !== undefined) {
|
||||
(await getUiActionsApi()).executeTriggerActions(PHONE_TRIGGER, user.phone);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -60,7 +60,7 @@ const ActionsExplorer = ({ uiActionsApi, openModal }: Props) => {
|
|||
</EuiText>
|
||||
<EuiButton
|
||||
data-test-subj="emitHelloWorldTrigger"
|
||||
onClick={() => uiActionsApi.executeTriggerActions(HELLO_WORLD_TRIGGER_ID, {})}
|
||||
onClick={() => uiActionsApi.executeTriggerActions(HELLO_WORLD_TRIGGER_ID, undefined)}
|
||||
>
|
||||
Say hello world!
|
||||
</EuiButton>
|
||||
|
|
|
@ -35,6 +35,9 @@ import {
|
|||
makePhoneCallAction,
|
||||
showcasePluggability,
|
||||
SHOWCASE_PLUGGABILITY_ACTION,
|
||||
UserContext,
|
||||
CountryContext,
|
||||
PhoneContext,
|
||||
} from './actions/actions';
|
||||
|
||||
interface StartDeps {
|
||||
|
@ -45,6 +48,14 @@ interface SetupDeps {
|
|||
uiActions: UiActionsSetup;
|
||||
}
|
||||
|
||||
declare module '../../../src/plugins/ui_actions/public' {
|
||||
export interface TriggerContextMapping {
|
||||
[USER_TRIGGER]: UserContext;
|
||||
[COUNTRY_TRIGGER]: CountryContext;
|
||||
[PHONE_TRIGGER]: PhoneContext;
|
||||
}
|
||||
}
|
||||
|
||||
export class UiActionsExplorerPlugin implements Plugin<void, void, {}, StartDeps> {
|
||||
public setup(core: CoreSetup<{ uiActions: UiActionsStart }>, deps: SetupDeps) {
|
||||
deps.uiActions.registerTrigger({
|
||||
|
|
|
@ -47,9 +47,7 @@ const createRowData = (
|
|||
<Fragment>
|
||||
<EuiButtonEmpty
|
||||
onClick={() => {
|
||||
uiActionsApi.executeTriggerActions(COUNTRY_TRIGGER, {
|
||||
country: user.countryOfResidence,
|
||||
});
|
||||
uiActionsApi.executeTriggerActions(COUNTRY_TRIGGER, user.countryOfResidence);
|
||||
}}
|
||||
>
|
||||
{user.countryOfResidence}
|
||||
|
@ -59,10 +57,9 @@ const createRowData = (
|
|||
phone: (
|
||||
<Fragment>
|
||||
<EuiButtonEmpty
|
||||
disabled={user.phone === undefined}
|
||||
onClick={() => {
|
||||
uiActionsApi.executeTriggerActions(PHONE_TRIGGER, {
|
||||
phone: user.phone,
|
||||
});
|
||||
uiActionsApi.executeTriggerActions(PHONE_TRIGGER, user.phone!);
|
||||
}}
|
||||
>
|
||||
{user.phone}
|
||||
|
|
|
@ -20,7 +20,7 @@ import _ from 'lodash';
|
|||
import * as Rx from 'rxjs';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExecuteTriggerActions } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsStart } from 'src/plugins/ui_actions/public';
|
||||
import { RequestAdapter, Adapters } from '../../../../../../../plugins/inspector/public';
|
||||
import {
|
||||
esFilters,
|
||||
|
@ -110,7 +110,7 @@ export class SearchEmbeddable extends Embeddable<SearchInput, SearchOutput>
|
|||
filterManager,
|
||||
}: SearchEmbeddableConfig,
|
||||
initialInput: SearchInput,
|
||||
private readonly executeTriggerActions: ExecuteTriggerActions,
|
||||
private readonly executeTriggerActions: UiActionsStart['executeTriggerActions'],
|
||||
parent?: Container
|
||||
) {
|
||||
super(
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import { auto } from 'angular';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExecuteTriggerActions } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsStart } from 'src/plugins/ui_actions/public';
|
||||
import { getServices } from '../../kibana_services';
|
||||
import {
|
||||
EmbeddableFactory,
|
||||
|
@ -43,7 +43,7 @@ export class SearchEmbeddableFactory extends EmbeddableFactory<
|
|||
public isEditable: () => boolean;
|
||||
|
||||
constructor(
|
||||
private readonly executeTriggerActions: ExecuteTriggerActions,
|
||||
private readonly executeTriggerActions: UiActionsStart['executeTriggerActions'],
|
||||
getInjector: () => Promise<auto.IInjectorService>,
|
||||
isEditable: () => boolean
|
||||
) {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { UiActionsSetup } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsSetup } from '../../ui_actions/public';
|
||||
import { Filter } from '../../data/public';
|
||||
import {
|
||||
applyFilterTrigger,
|
||||
|
@ -27,6 +27,7 @@ import {
|
|||
valueClickTrigger,
|
||||
EmbeddableVisTriggerContext,
|
||||
IEmbeddable,
|
||||
EmbeddableContext,
|
||||
APPLY_FILTER_TRIGGER,
|
||||
VALUE_CLICK_TRIGGER,
|
||||
SELECT_RANGE_TRIGGER,
|
||||
|
@ -42,8 +43,8 @@ declare module '../../ui_actions/public' {
|
|||
embeddable: IEmbeddable;
|
||||
filters: Filter[];
|
||||
};
|
||||
[CONTEXT_MENU_TRIGGER]: object;
|
||||
[PANEL_BADGE_TRIGGER]: object;
|
||||
[CONTEXT_MENU_TRIGGER]: EmbeddableContext;
|
||||
[PANEL_BADGE_TRIGGER]: EmbeddableContext;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import React from 'react';
|
|||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { GetActionsCompatibleWithTrigger } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsService } from 'src/plugins/ui_actions/public';
|
||||
|
||||
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
|
||||
import { ErrorEmbeddable, IEmbeddable } from '../embeddables';
|
||||
|
@ -35,7 +35,7 @@ export interface EmbeddableChildPanelProps {
|
|||
embeddableId: string;
|
||||
className?: string;
|
||||
container: IContainer;
|
||||
getActions: GetActionsCompatibleWithTrigger;
|
||||
getActions: UiActionsService['getTriggerCompatibleActions'];
|
||||
getEmbeddableFactory: GetEmbeddableFactory;
|
||||
getAllEmbeddableFactories: GetEmbeddableFactories;
|
||||
overlays: CoreStart['overlays'];
|
||||
|
|
|
@ -44,7 +44,7 @@ import {
|
|||
import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks';
|
||||
import { EuiBadge } from '@elastic/eui';
|
||||
|
||||
const actionRegistry = new Map<string, Action>();
|
||||
const actionRegistry = new Map<string, Action<object | undefined | string | number>>();
|
||||
const triggerRegistry = new Map<string, Trigger>();
|
||||
const embeddableFactories = new Map<string, EmbeddableFactory>();
|
||||
const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id);
|
||||
|
|
|
@ -20,12 +20,12 @@ import { EuiContextMenuPanelDescriptor, EuiPanel, htmlIdGenerator } from '@elast
|
|||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { buildContextMenuForActions, GetActionsCompatibleWithTrigger, Action } from '../ui_actions';
|
||||
import { buildContextMenuForActions, UiActionsService, Action } from '../ui_actions';
|
||||
import { CoreStart, OverlayStart } from '../../../../../core/public';
|
||||
import { toMountPoint } from '../../../../kibana_react/public';
|
||||
|
||||
import { Start as InspectorStartContract } from '../inspector';
|
||||
import { CONTEXT_MENU_TRIGGER, PANEL_BADGE_TRIGGER } from '../triggers';
|
||||
import { CONTEXT_MENU_TRIGGER, PANEL_BADGE_TRIGGER, EmbeddableContext } from '../triggers';
|
||||
import { IEmbeddable } from '../embeddables/i_embeddable';
|
||||
import { ViewMode, GetEmbeddableFactory, GetEmbeddableFactories } from '../types';
|
||||
|
||||
|
@ -39,7 +39,7 @@ import { CustomizePanelModal } from './panel_header/panel_actions/customize_titl
|
|||
|
||||
interface Props {
|
||||
embeddable: IEmbeddable<any, any>;
|
||||
getActions: GetActionsCompatibleWithTrigger;
|
||||
getActions: UiActionsService['getTriggerCompatibleActions'];
|
||||
getEmbeddableFactory: GetEmbeddableFactory;
|
||||
getAllEmbeddableFactories: GetEmbeddableFactories;
|
||||
overlays: CoreStart['overlays'];
|
||||
|
@ -55,7 +55,7 @@ interface State {
|
|||
viewMode: ViewMode;
|
||||
hidePanelTitles: boolean;
|
||||
closeContextMenu: boolean;
|
||||
badges: Action[];
|
||||
badges: Array<Action<EmbeddableContext>>;
|
||||
}
|
||||
|
||||
export class EmbeddablePanel extends React.Component<Props, State> {
|
||||
|
@ -87,7 +87,7 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
private async refreshBadges() {
|
||||
let badges: Action[] = await this.props.getActions(PANEL_BADGE_TRIGGER, {
|
||||
let badges = await this.props.getActions(PANEL_BADGE_TRIGGER, {
|
||||
embeddable: this.props.embeddable,
|
||||
});
|
||||
if (!this.mounted) return;
|
||||
|
@ -231,7 +231,7 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
|
||||
// These actions are exposed on the context menu for every embeddable, they bypass the trigger
|
||||
// registry.
|
||||
const extraActions: Array<Action<{ embeddable: IEmbeddable }>> = [
|
||||
const extraActions: Array<Action<EmbeddableContext>> = [
|
||||
new CustomizePanelTitleAction(createGetUserData(this.props.overlays)),
|
||||
new AddPanelAction(
|
||||
this.props.getEmbeddableFactory,
|
||||
|
@ -245,11 +245,13 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
new EditPanelAction(this.props.getEmbeddableFactory),
|
||||
];
|
||||
|
||||
const sorted = actions.concat(extraActions).sort((a: Action, b: Action) => {
|
||||
const bOrder = b.order || 0;
|
||||
const aOrder = a.order || 0;
|
||||
return bOrder - aOrder;
|
||||
});
|
||||
const sorted = actions
|
||||
.concat(extraActions)
|
||||
.sort((a: Action<EmbeddableContext>, b: Action<EmbeddableContext>) => {
|
||||
const bOrder = b.order || 0;
|
||||
const aOrder = a.order || 0;
|
||||
return bOrder - aOrder;
|
||||
});
|
||||
|
||||
return await buildContextMenuForActions({
|
||||
actions: sorted,
|
||||
|
|
|
@ -29,6 +29,7 @@ import React from 'react';
|
|||
import { Action } from 'src/plugins/ui_actions/public';
|
||||
import { PanelOptionsMenu } from './panel_options_menu';
|
||||
import { IEmbeddable } from '../../embeddables';
|
||||
import { EmbeddableContext } from '../../triggers';
|
||||
|
||||
export interface PanelHeaderProps {
|
||||
title?: string;
|
||||
|
@ -36,12 +37,12 @@ export interface PanelHeaderProps {
|
|||
hidePanelTitles: boolean;
|
||||
getActionContextMenuPanel: () => Promise<EuiContextMenuPanelDescriptor>;
|
||||
closeContextMenu: boolean;
|
||||
badges: Action[];
|
||||
badges: Array<Action<EmbeddableContext>>;
|
||||
embeddable: IEmbeddable;
|
||||
headerId?: string;
|
||||
}
|
||||
|
||||
function renderBadges(badges: Action[], embeddable: IEmbeddable) {
|
||||
function renderBadges(badges: Array<Action<EmbeddableContext>>, embeddable: IEmbeddable) {
|
||||
return badges.map(badge => (
|
||||
<EuiBadge
|
||||
key={badge.id}
|
||||
|
|
|
@ -19,16 +19,12 @@
|
|||
|
||||
import { createAction } from '../../ui_actions';
|
||||
import { ViewMode } from '../../types';
|
||||
import { IEmbeddable } from '../../embeddables';
|
||||
import { EmbeddableContext } from '../../triggers';
|
||||
|
||||
export const EDIT_MODE_ACTION = 'EDIT_MODE_ACTION';
|
||||
|
||||
interface ActionContext {
|
||||
embeddable: IEmbeddable;
|
||||
}
|
||||
|
||||
export function createEditModeAction() {
|
||||
return createAction<ActionContext>({
|
||||
return createAction<EmbeddableContext>({
|
||||
type: EDIT_MODE_ACTION,
|
||||
getDisplayName: () => 'I only show up in edit mode',
|
||||
isCompatible: async context => context.embeddable.getInput().viewMode === ViewMode.EDIT,
|
||||
|
|
|
@ -22,12 +22,19 @@ import { EuiCard, EuiFlexItem, EuiFlexGroup, EuiFormRow } from '@elastic/eui';
|
|||
import { Subscription } from 'rxjs';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import * as Rx from 'rxjs';
|
||||
import { ExecuteTriggerActions } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsStart } from '../../../../../../ui_actions/public';
|
||||
import { ContactCardEmbeddable, CONTACT_USER_TRIGGER } from './contact_card_embeddable';
|
||||
import { EmbeddableContext } from '../../../triggers';
|
||||
|
||||
declare module '../../../../../../ui_actions/public' {
|
||||
export interface TriggerContextMapping {
|
||||
[CONTACT_USER_TRIGGER]: EmbeddableContext;
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
embeddable: ContactCardEmbeddable;
|
||||
execTrigger: ExecuteTriggerActions;
|
||||
execTrigger: UiActionsStart['executeTriggerActions'];
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -72,7 +79,6 @@ export class ContactCardEmbeddableComponent extends React.Component<Props, State
|
|||
emitContactTrigger = () => {
|
||||
this.props.execTrigger(CONTACT_USER_TRIGGER, {
|
||||
embeddable: this.props.embeddable,
|
||||
triggerContext: {},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import React from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ExecuteTriggerActions } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsStart } 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';
|
||||
|
@ -37,7 +37,7 @@ export interface ContactCardEmbeddableOutput extends EmbeddableOutput {
|
|||
}
|
||||
|
||||
export interface ContactCardEmbeddableOptions {
|
||||
execAction: ExecuteTriggerActions;
|
||||
execAction: UiActionsStart['executeTriggerActions'];
|
||||
}
|
||||
|
||||
function getFullName(input: ContactCardEmbeddableInput) {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExecuteTriggerActions } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsStart } from 'src/plugins/ui_actions/public';
|
||||
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { toMountPoint } from '../../../../../../kibana_react/public';
|
||||
|
@ -36,7 +36,7 @@ export class ContactCardEmbeddableFactory extends EmbeddableFactory<ContactCardE
|
|||
|
||||
constructor(
|
||||
options: EmbeddableFactoryOptions<any>,
|
||||
private readonly execTrigger: ExecuteTriggerActions,
|
||||
private readonly execTrigger: UiActionsStart['executeTriggerActions'],
|
||||
private readonly overlays: CoreStart['overlays']
|
||||
) {
|
||||
super(options);
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ExecuteTriggerActions } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsStart } 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';
|
||||
|
||||
interface SlowContactCardEmbeddableFactoryOptions {
|
||||
execAction: ExecuteTriggerActions;
|
||||
execAction: UiActionsStart['executeTriggerActions'];
|
||||
loadTickCount?: number;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { GetActionsCompatibleWithTrigger } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsService } from 'src/plugins/ui_actions/public';
|
||||
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
|
||||
import { Container, ViewMode, ContainerInput } from '../..';
|
||||
import { HelloWorldContainerComponent } from './hello_world_container_component';
|
||||
|
@ -45,7 +45,7 @@ interface HelloWorldContainerInput extends ContainerInput {
|
|||
}
|
||||
|
||||
interface HelloWorldContainerOptions {
|
||||
getActions: GetActionsCompatibleWithTrigger;
|
||||
getActions: UiActionsService['getTriggerCompatibleActions'];
|
||||
getEmbeddableFactory: GetEmbeddableFactory;
|
||||
getAllEmbeddableFactories: GetEmbeddableFactories;
|
||||
overlays: CoreStart['overlays'];
|
||||
|
|
|
@ -21,14 +21,14 @@ import { Subscription } from 'rxjs';
|
|||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { GetActionsCompatibleWithTrigger } from 'src/plugins/ui_actions/public';
|
||||
import { UiActionsService } from 'src/plugins/ui_actions/public';
|
||||
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
|
||||
import { IContainer, PanelState, EmbeddableChildPanel } from '../..';
|
||||
import { GetEmbeddableFactory, GetEmbeddableFactories } from '../../types';
|
||||
|
||||
interface Props {
|
||||
container: IContainer;
|
||||
getActions: GetActionsCompatibleWithTrigger;
|
||||
getActions: UiActionsService['getTriggerCompatibleActions'];
|
||||
getEmbeddableFactory: GetEmbeddableFactory;
|
||||
getAllEmbeddableFactories: GetEmbeddableFactories;
|
||||
overlays: CoreStart['overlays'];
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
import { Trigger } from '../../../../ui_actions/public';
|
||||
import { IEmbeddable } from '..';
|
||||
|
||||
export interface EmbeddableContext {
|
||||
embeddable: IEmbeddable;
|
||||
}
|
||||
|
||||
export interface EmbeddableVisTriggerContext {
|
||||
embeddable: IEmbeddable;
|
||||
timeFieldName: string;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import { UiComponent } from 'src/plugins/kibana_utils/common';
|
||||
|
||||
export interface Action<ActionContext extends {} = {}> {
|
||||
export interface Action<Context = undefined> {
|
||||
/**
|
||||
* Determined the order when there is more than one action matched to a trigger.
|
||||
* Higher numbers are displayed first.
|
||||
|
@ -33,33 +33,33 @@ export interface Action<ActionContext extends {} = {}> {
|
|||
/**
|
||||
* Optional EUI icon type that can be displayed along with the title.
|
||||
*/
|
||||
getIconType(context: ActionContext): string | undefined;
|
||||
getIconType(context: Context): string | undefined;
|
||||
|
||||
/**
|
||||
* Returns a title to be displayed to the user.
|
||||
* @param context
|
||||
*/
|
||||
getDisplayName(context: ActionContext): string;
|
||||
getDisplayName(context: Context): string;
|
||||
|
||||
/**
|
||||
* `UiComponent` to render when displaying this action as a context menu item.
|
||||
* If not provided, `getDisplayName` will be used instead.
|
||||
*/
|
||||
MenuItem?: UiComponent<{ context: ActionContext }>;
|
||||
MenuItem?: UiComponent<{ context: Context }>;
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves to true if this action is compatible given the context,
|
||||
* otherwise resolves to false.
|
||||
*/
|
||||
isCompatible(context: ActionContext): Promise<boolean>;
|
||||
isCompatible(context: Context): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* If this returns something truthy, this is used in addition to the `execute` method when clicked.
|
||||
*/
|
||||
getHref?(context: ActionContext): string | undefined;
|
||||
getHref?(context: Context): string | undefined;
|
||||
|
||||
/**
|
||||
* Executes the action.
|
||||
*/
|
||||
execute(context: ActionContext): Promise<void>;
|
||||
execute(context: Context): Promise<void>;
|
||||
}
|
||||
|
|
|
@ -19,11 +19,9 @@
|
|||
|
||||
import { Action } from './action';
|
||||
|
||||
export function createAction<ActionContext extends {} = {}>(
|
||||
action: { type: string; execute: Action<ActionContext>['execute'] } & Partial<
|
||||
Action<ActionContext>
|
||||
>
|
||||
): Action<ActionContext> {
|
||||
export function createAction<Context = undefined>(
|
||||
action: { type: string; execute: Action<Context>['execute'] } & Partial<Action<Context>>
|
||||
): Action<Context> {
|
||||
return {
|
||||
getIconType: () => undefined,
|
||||
order: 0,
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
import { PluginInitializerContext } from '../../../core/public';
|
||||
import { UiActionsPlugin } from './plugin';
|
||||
import { UiActionsService } from './service';
|
||||
|
||||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new UiActionsPlugin(initializerContext);
|
||||
|
@ -30,20 +29,4 @@ export { UiActionsServiceParams, UiActionsService } from './service';
|
|||
export { Action, createAction, IncompatibleActionError } from './actions';
|
||||
export { buildContextMenuForActions } from './context_menu';
|
||||
export { Trigger, TriggerContext } from './triggers';
|
||||
export { TriggerContextMapping } from './types';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Use `UiActionsStart['getTriggerCompatibleActions']` or
|
||||
* `UiActionsService['getTriggerCompatibleActions']` instead.
|
||||
*/
|
||||
export type GetActionsCompatibleWithTrigger = UiActionsService['getTriggerCompatibleActions'];
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Use `UiActionsStart['executeTriggerActions']` or
|
||||
* `UiActionsService['executeTriggerActions']` instead.
|
||||
*/
|
||||
export type ExecuteTriggerActions = UiActionsService['executeTriggerActions'];
|
||||
export { TriggerContextMapping, TriggerId } from './types';
|
||||
|
|
|
@ -21,6 +21,7 @@ import { CoreSetup, CoreStart } from 'src/core/public';
|
|||
import { UiActionsSetup, UiActionsStart } from '.';
|
||||
import { plugin as pluginInitializer } from '.';
|
||||
import { coreMock } from '../../../core/public/mocks';
|
||||
import { TriggerId } from './types';
|
||||
|
||||
export type Setup = jest.Mocked<UiActionsSetup>;
|
||||
export type Start = jest.Mocked<UiActionsStart>;
|
||||
|
@ -43,7 +44,7 @@ const createStartContract = (): Start => {
|
|||
detachAction: jest.fn(),
|
||||
executeTriggerActions: jest.fn(),
|
||||
getTrigger: jest.fn(),
|
||||
getTriggerActions: jest.fn((id: string) => []),
|
||||
getTriggerActions: jest.fn((id: TriggerId) => []),
|
||||
getTriggerCompatibleActions: jest.fn(),
|
||||
clear: jest.fn(),
|
||||
fork: jest.fn(),
|
||||
|
|
|
@ -20,9 +20,16 @@
|
|||
import { UiActionsService } from './ui_actions_service';
|
||||
import { Action } from '../actions';
|
||||
import { createRestrictedAction, createHelloWorldAction } from '../tests/test_samples';
|
||||
import { ActionRegistry, TriggerRegistry } from '../types';
|
||||
import { ActionRegistry, TriggerRegistry, TriggerId } from '../types';
|
||||
import { Trigger } from '../triggers';
|
||||
|
||||
// I tried redeclaring the module in here to extend the `TriggerContextMapping` but
|
||||
// that seems to overwrite all other plugins extending it, I suspect because it's inside
|
||||
// the main plugin.
|
||||
const FOO_TRIGGER: TriggerId = 'FOO_TRIGGER' as TriggerId;
|
||||
const BAR_TRIGGER: TriggerId = 'BAR_TRIGGER' as TriggerId;
|
||||
const MY_TRIGGER: TriggerId = 'MY_TRIGGER' as TriggerId;
|
||||
|
||||
const testAction1: Action = {
|
||||
id: 'action1',
|
||||
order: 1,
|
||||
|
@ -52,7 +59,7 @@ describe('UiActionsService', () => {
|
|||
test('can register a trigger', () => {
|
||||
const service = new UiActionsService();
|
||||
service.registerTrigger({
|
||||
id: 'test',
|
||||
id: BAR_TRIGGER,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -62,15 +69,15 @@ describe('UiActionsService', () => {
|
|||
const service = new UiActionsService();
|
||||
service.registerTrigger({
|
||||
description: 'foo',
|
||||
id: 'bar',
|
||||
id: BAR_TRIGGER,
|
||||
title: 'baz',
|
||||
});
|
||||
|
||||
const trigger = service.getTrigger('bar');
|
||||
const trigger = service.getTrigger(BAR_TRIGGER);
|
||||
|
||||
expect(trigger).toMatchObject({
|
||||
description: 'foo',
|
||||
id: 'bar',
|
||||
id: BAR_TRIGGER,
|
||||
title: 'baz',
|
||||
});
|
||||
});
|
||||
|
@ -78,8 +85,8 @@ describe('UiActionsService', () => {
|
|||
test('throws if trigger does not exist', () => {
|
||||
const service = new UiActionsService();
|
||||
|
||||
expect(() => service.getTrigger('foo')).toThrowError(
|
||||
'Trigger [triggerId = foo] does not exist.'
|
||||
expect(() => service.getTrigger(FOO_TRIGGER)).toThrowError(
|
||||
'Trigger [triggerId = FOO_TRIGGER] does not exist.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -125,22 +132,22 @@ describe('UiActionsService', () => {
|
|||
service.registerAction(action2);
|
||||
service.registerTrigger({
|
||||
description: 'foo',
|
||||
id: 'trigger',
|
||||
id: FOO_TRIGGER,
|
||||
title: 'baz',
|
||||
});
|
||||
|
||||
const list0 = service.getTriggerActions('trigger');
|
||||
const list0 = service.getTriggerActions(FOO_TRIGGER);
|
||||
|
||||
expect(list0).toHaveLength(0);
|
||||
|
||||
service.attachAction('trigger', 'action1');
|
||||
const list1 = service.getTriggerActions('trigger');
|
||||
service.attachAction(FOO_TRIGGER, 'action1');
|
||||
const list1 = service.getTriggerActions(FOO_TRIGGER);
|
||||
|
||||
expect(list1).toHaveLength(1);
|
||||
expect(list1).toEqual([action1]);
|
||||
|
||||
service.attachAction('trigger', 'action2');
|
||||
const list2 = service.getTriggerActions('trigger');
|
||||
service.attachAction(FOO_TRIGGER, 'action2');
|
||||
const list2 = service.getTriggerActions(FOO_TRIGGER);
|
||||
|
||||
expect(list2).toHaveLength(2);
|
||||
expect(!!list2.find(({ id }: any) => id === 'action1')).toBe(true);
|
||||
|
@ -168,13 +175,15 @@ describe('UiActionsService', () => {
|
|||
service.registerAction(helloWorldAction);
|
||||
|
||||
const testTrigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: MY_TRIGGER,
|
||||
title: 'My trigger',
|
||||
};
|
||||
service.registerTrigger(testTrigger);
|
||||
service.attachAction('MY-TRIGGER', helloWorldAction.id);
|
||||
service.attachAction(MY_TRIGGER, helloWorldAction.id);
|
||||
|
||||
const compatibleActions = await service.getTriggerCompatibleActions('MY-TRIGGER', {});
|
||||
const compatibleActions = await service.getTriggerCompatibleActions(MY_TRIGGER, {
|
||||
hi: 'there',
|
||||
});
|
||||
|
||||
expect(compatibleActions.length).toBe(1);
|
||||
expect(compatibleActions[0].id).toBe(helloWorldAction.id);
|
||||
|
@ -189,7 +198,7 @@ describe('UiActionsService', () => {
|
|||
service.registerAction(restrictedAction);
|
||||
|
||||
const testTrigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: MY_TRIGGER,
|
||||
title: 'My trigger',
|
||||
};
|
||||
|
||||
|
@ -212,15 +221,16 @@ describe('UiActionsService', () => {
|
|||
test(`throws an error with an invalid trigger ID`, async () => {
|
||||
const service = new UiActionsService();
|
||||
|
||||
await expect(service.getTriggerCompatibleActions('I do not exist', {})).rejects.toMatchObject(
|
||||
new Error('Trigger [triggerId = I do not exist] does not exist.')
|
||||
);
|
||||
// Without the cast "as TriggerId" typescript will happily throw an error!
|
||||
await expect(
|
||||
service.getTriggerCompatibleActions('I do not exist' as TriggerId, {})
|
||||
).rejects.toMatchObject(new Error('Trigger [triggerId = I do not exist] does not exist.'));
|
||||
});
|
||||
|
||||
test('returns empty list if trigger not attached to any action', async () => {
|
||||
const service = new UiActionsService();
|
||||
const testTrigger: Trigger = {
|
||||
id: '123',
|
||||
id: '123' as TriggerId,
|
||||
title: '123',
|
||||
};
|
||||
service.registerTrigger(testTrigger);
|
||||
|
@ -243,15 +253,15 @@ describe('UiActionsService', () => {
|
|||
test('triggers registered in original service are available in original an forked services', () => {
|
||||
const service1 = new UiActionsService();
|
||||
service1.registerTrigger({
|
||||
id: 'foo',
|
||||
id: FOO_TRIGGER,
|
||||
});
|
||||
const service2 = service1.fork();
|
||||
|
||||
const trigger1 = service1.getTrigger('foo');
|
||||
const trigger2 = service2.getTrigger('foo');
|
||||
const trigger1 = service1.getTrigger(FOO_TRIGGER);
|
||||
const trigger2 = service2.getTrigger(FOO_TRIGGER);
|
||||
|
||||
expect(trigger1.id).toBe('foo');
|
||||
expect(trigger2.id).toBe('foo');
|
||||
expect(trigger1.id).toBe(FOO_TRIGGER);
|
||||
expect(trigger2.id).toBe(FOO_TRIGGER);
|
||||
});
|
||||
|
||||
test('triggers registered in forked service are not available in original service', () => {
|
||||
|
@ -259,30 +269,30 @@ describe('UiActionsService', () => {
|
|||
const service2 = service1.fork();
|
||||
|
||||
service2.registerTrigger({
|
||||
id: 'foo',
|
||||
id: FOO_TRIGGER,
|
||||
});
|
||||
|
||||
expect(() => service1.getTrigger('foo')).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Trigger [triggerId = foo] does not exist."`
|
||||
expect(() => service1.getTrigger(FOO_TRIGGER)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Trigger [triggerId = FOO_TRIGGER] does not exist."`
|
||||
);
|
||||
|
||||
const trigger2 = service2.getTrigger('foo');
|
||||
expect(trigger2.id).toBe('foo');
|
||||
const trigger2 = service2.getTrigger(FOO_TRIGGER);
|
||||
expect(trigger2.id).toBe(FOO_TRIGGER);
|
||||
});
|
||||
|
||||
test('forked service preserves trigger-to-actions mapping', () => {
|
||||
const service1 = new UiActionsService();
|
||||
|
||||
service1.registerTrigger({
|
||||
id: 'foo',
|
||||
id: FOO_TRIGGER,
|
||||
});
|
||||
service1.registerAction(testAction1);
|
||||
service1.attachAction('foo', testAction1.id);
|
||||
service1.attachAction(FOO_TRIGGER, testAction1.id);
|
||||
|
||||
const service2 = service1.fork();
|
||||
|
||||
const actions1 = service1.getTriggerActions('foo');
|
||||
const actions2 = service2.getTriggerActions('foo');
|
||||
const actions1 = service1.getTriggerActions(FOO_TRIGGER);
|
||||
const actions2 = service2.getTriggerActions(FOO_TRIGGER);
|
||||
|
||||
expect(actions1).toHaveLength(1);
|
||||
expect(actions2).toHaveLength(1);
|
||||
|
@ -294,42 +304,42 @@ describe('UiActionsService', () => {
|
|||
const service1 = new UiActionsService();
|
||||
|
||||
service1.registerTrigger({
|
||||
id: 'foo',
|
||||
id: FOO_TRIGGER,
|
||||
});
|
||||
service1.registerAction(testAction1);
|
||||
service1.registerAction(testAction2);
|
||||
service1.attachAction('foo', testAction1.id);
|
||||
service1.attachAction(FOO_TRIGGER, testAction1.id);
|
||||
|
||||
const service2 = service1.fork();
|
||||
|
||||
expect(service1.getTriggerActions('foo')).toHaveLength(1);
|
||||
expect(service2.getTriggerActions('foo')).toHaveLength(1);
|
||||
expect(service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
|
||||
expect(service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
|
||||
|
||||
service2.attachAction('foo', testAction2.id);
|
||||
service2.attachAction(FOO_TRIGGER, testAction2.id);
|
||||
|
||||
expect(service1.getTriggerActions('foo')).toHaveLength(1);
|
||||
expect(service2.getTriggerActions('foo')).toHaveLength(2);
|
||||
expect(service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
|
||||
expect(service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(2);
|
||||
});
|
||||
|
||||
test('new attachments in original service do not appear in fork', () => {
|
||||
const service1 = new UiActionsService();
|
||||
|
||||
service1.registerTrigger({
|
||||
id: 'foo',
|
||||
id: FOO_TRIGGER,
|
||||
});
|
||||
service1.registerAction(testAction1);
|
||||
service1.registerAction(testAction2);
|
||||
service1.attachAction('foo', testAction1.id);
|
||||
service1.attachAction(FOO_TRIGGER, testAction1.id);
|
||||
|
||||
const service2 = service1.fork();
|
||||
|
||||
expect(service1.getTriggerActions('foo')).toHaveLength(1);
|
||||
expect(service2.getTriggerActions('foo')).toHaveLength(1);
|
||||
expect(service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
|
||||
expect(service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
|
||||
|
||||
service1.attachAction('foo', testAction2.id);
|
||||
service1.attachAction(FOO_TRIGGER, testAction2.id);
|
||||
|
||||
expect(service1.getTriggerActions('foo')).toHaveLength(2);
|
||||
expect(service2.getTriggerActions('foo')).toHaveLength(1);
|
||||
expect(service1.getTriggerActions(FOO_TRIGGER)).toHaveLength(2);
|
||||
expect(service2.getTriggerActions(FOO_TRIGGER)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -342,14 +352,14 @@ describe('UiActionsService', () => {
|
|||
|
||||
service.registerTrigger({
|
||||
description: 'foo',
|
||||
id: 'bar',
|
||||
id: BAR_TRIGGER,
|
||||
title: 'baz',
|
||||
});
|
||||
const triggerContract = service.getTrigger('bar');
|
||||
const triggerContract = service.getTrigger(BAR_TRIGGER);
|
||||
|
||||
expect(triggerContract).toMatchObject({
|
||||
description: 'foo',
|
||||
id: 'bar',
|
||||
id: BAR_TRIGGER,
|
||||
title: 'baz',
|
||||
});
|
||||
});
|
||||
|
@ -373,7 +383,7 @@ describe('UiActionsService', () => {
|
|||
const service = new UiActionsService();
|
||||
|
||||
const trigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: MY_TRIGGER,
|
||||
};
|
||||
const action = {
|
||||
id: HELLO_WORLD_ACTION_ID,
|
||||
|
@ -382,7 +392,7 @@ describe('UiActionsService', () => {
|
|||
|
||||
service.registerTrigger(trigger);
|
||||
service.registerAction(action);
|
||||
service.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID);
|
||||
service.attachAction(MY_TRIGGER, HELLO_WORLD_ACTION_ID);
|
||||
|
||||
const actions = service.getTriggerActions(trigger.id);
|
||||
|
||||
|
@ -394,7 +404,7 @@ describe('UiActionsService', () => {
|
|||
const service = new UiActionsService();
|
||||
|
||||
const trigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: MY_TRIGGER,
|
||||
};
|
||||
const action = {
|
||||
id: HELLO_WORLD_ACTION_ID,
|
||||
|
@ -419,7 +429,9 @@ describe('UiActionsService', () => {
|
|||
} as any;
|
||||
|
||||
service.registerAction(action);
|
||||
expect(() => service.detachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError(
|
||||
expect(() =>
|
||||
service.detachAction('i do not exist' as TriggerId, HELLO_WORLD_ACTION_ID)
|
||||
).toThrowError(
|
||||
'No trigger [triggerId = i do not exist] exists, for detaching action [actionId = HELLO_WORLD_ACTION_ID].'
|
||||
);
|
||||
});
|
||||
|
@ -433,7 +445,9 @@ describe('UiActionsService', () => {
|
|||
} as any;
|
||||
|
||||
service.registerAction(action);
|
||||
expect(() => service.attachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError(
|
||||
expect(() =>
|
||||
service.attachAction('i do not exist' as TriggerId, HELLO_WORLD_ACTION_ID)
|
||||
).toThrowError(
|
||||
'No trigger [triggerId = i do not exist] exists, for attaching action [actionId = HELLO_WORLD_ACTION_ID].'
|
||||
);
|
||||
});
|
||||
|
|
|
@ -17,7 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TriggerRegistry, ActionRegistry, TriggerToActionsRegistry, TriggerId } from '../types';
|
||||
import {
|
||||
TriggerRegistry,
|
||||
ActionRegistry,
|
||||
TriggerToActionsRegistry,
|
||||
TriggerId,
|
||||
TriggerContextMapping,
|
||||
} from '../types';
|
||||
import { Action } from '../actions';
|
||||
import { Trigger, TriggerContext } from '../triggers/trigger';
|
||||
import { TriggerInternal } from '../triggers/trigger_internal';
|
||||
|
@ -60,7 +66,7 @@ export class UiActionsService {
|
|||
};
|
||||
|
||||
public readonly getTrigger = <T extends TriggerId>(triggerId: T): TriggerContract<T> => {
|
||||
const trigger = this.triggers.get(triggerId as string);
|
||||
const trigger = this.triggers.get(triggerId);
|
||||
|
||||
if (!trigger) {
|
||||
throw new Error(`Trigger [triggerId = ${triggerId}] does not exist.`);
|
||||
|
@ -69,7 +75,7 @@ export class UiActionsService {
|
|||
return trigger.contract;
|
||||
};
|
||||
|
||||
public readonly registerAction = (action: Action) => {
|
||||
public readonly registerAction = <Context>(action: Action<Context>) => {
|
||||
if (this.actions.has(action.id)) {
|
||||
throw new Error(`Action [action.id = ${action.id}] already registered.`);
|
||||
}
|
||||
|
@ -77,7 +83,10 @@ export class UiActionsService {
|
|||
this.actions.set(action.id, action);
|
||||
};
|
||||
|
||||
public readonly attachAction = (triggerId: string, actionId: string): void => {
|
||||
// TODO: make this
|
||||
// <T extends TriggerId>(triggerId: T, action: Action<TriggerContextMapping[T]>): \
|
||||
// to get type checks here!
|
||||
public readonly attachAction = <T extends TriggerId>(triggerId: T, actionId: string): void => {
|
||||
const trigger = this.triggers.get(triggerId);
|
||||
|
||||
if (!trigger) {
|
||||
|
@ -93,7 +102,7 @@ export class UiActionsService {
|
|||
}
|
||||
};
|
||||
|
||||
public readonly detachAction = (triggerId: string, actionId: string) => {
|
||||
public readonly detachAction = (triggerId: TriggerId, actionId: string) => {
|
||||
const trigger = this.triggers.get(triggerId);
|
||||
|
||||
if (!trigger) {
|
||||
|
@ -110,23 +119,30 @@ export class UiActionsService {
|
|||
);
|
||||
};
|
||||
|
||||
public readonly getTriggerActions = (triggerId: string) => {
|
||||
public readonly getTriggerActions = <T extends TriggerId>(
|
||||
triggerId: T
|
||||
): Array<Action<TriggerContextMapping[T]>> => {
|
||||
// This line checks if trigger exists, otherwise throws.
|
||||
this.getTrigger!(triggerId);
|
||||
|
||||
const actionIds = this.triggerToActions.get(triggerId);
|
||||
const actions = actionIds!
|
||||
.map(actionId => this.actions.get(actionId))
|
||||
.filter(Boolean) as Action[];
|
||||
|
||||
return actions;
|
||||
const actions = actionIds!.map(actionId => this.actions.get(actionId)).filter(Boolean) as Array<
|
||||
Action<TriggerContextMapping[T]>
|
||||
>;
|
||||
|
||||
return actions as Array<Action<TriggerContext<T>>>;
|
||||
};
|
||||
|
||||
public readonly getTriggerCompatibleActions = async <C>(triggerId: string, context: C) => {
|
||||
public readonly getTriggerCompatibleActions = async <T extends TriggerId>(
|
||||
triggerId: T,
|
||||
context: TriggerContextMapping[T]
|
||||
): Promise<Array<Action<TriggerContextMapping[T]>>> => {
|
||||
const actions = this.getTriggerActions!(triggerId);
|
||||
const isCompatibles = await Promise.all(actions.map(action => action.isCompatible(context)));
|
||||
return actions.reduce<Action[]>(
|
||||
(acc, action, i) => (isCompatibles[i] ? [...acc, action] : acc),
|
||||
return actions.reduce(
|
||||
(acc: Array<Action<TriggerContextMapping[T]>>, action, i) =>
|
||||
isCompatibles[i] ? [...acc, action] : acc,
|
||||
[]
|
||||
);
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@ import { Action, createAction } from '../actions';
|
|||
import { openContextMenu } from '../context_menu';
|
||||
import { uiActionsPluginMock } from '../mocks';
|
||||
import { Trigger } from '../triggers';
|
||||
import { TriggerId } from '../types';
|
||||
|
||||
jest.mock('../context_menu');
|
||||
|
||||
|
@ -55,7 +56,7 @@ beforeEach(reset);
|
|||
test('executes a single action mapped to a trigger', async () => {
|
||||
const { setup, doStart } = uiActions;
|
||||
const trigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: 'MY-TRIGGER' as TriggerId,
|
||||
title: 'My trigger',
|
||||
};
|
||||
const action = createTestAction('test1', () => true);
|
||||
|
@ -66,7 +67,7 @@ test('executes a single action mapped to a trigger', async () => {
|
|||
|
||||
const context = {};
|
||||
const start = doStart();
|
||||
await start.executeTriggerActions('MY-TRIGGER', context);
|
||||
await start.executeTriggerActions('MY-TRIGGER' as TriggerId, context);
|
||||
|
||||
expect(executeFn).toBeCalledTimes(1);
|
||||
expect(executeFn).toBeCalledWith(context);
|
||||
|
@ -75,7 +76,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 } = uiActions;
|
||||
const trigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: 'MY-TRIGGER' as TriggerId,
|
||||
title: 'My trigger',
|
||||
};
|
||||
|
||||
|
@ -84,7 +85,9 @@ test('throws an error if there are no compatible actions to execute', async () =
|
|||
|
||||
const context = {};
|
||||
const start = doStart();
|
||||
await expect(start.executeTriggerActions('MY-TRIGGER', context)).rejects.toMatchObject(
|
||||
await expect(
|
||||
start.executeTriggerActions('MY-TRIGGER' as TriggerId, context)
|
||||
).rejects.toMatchObject(
|
||||
new Error('No compatible actions found to execute for trigger [triggerId = MY-TRIGGER].')
|
||||
);
|
||||
});
|
||||
|
@ -92,7 +95,7 @@ test('throws an error if there are no compatible actions to execute', async () =
|
|||
test('does not execute an incompatible action', async () => {
|
||||
const { setup, doStart } = uiActions;
|
||||
const trigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: 'MY-TRIGGER' as TriggerId,
|
||||
title: 'My trigger',
|
||||
};
|
||||
const action = createTestAction<{ name: string }>('test1', ({ name }) => name === 'executeme');
|
||||
|
@ -105,7 +108,7 @@ test('does not execute an incompatible action', async () => {
|
|||
const context = {
|
||||
name: 'executeme',
|
||||
};
|
||||
await start.executeTriggerActions('MY-TRIGGER', context);
|
||||
await start.executeTriggerActions('MY-TRIGGER' as TriggerId, context);
|
||||
|
||||
expect(executeFn).toBeCalledTimes(1);
|
||||
});
|
||||
|
@ -113,7 +116,7 @@ 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 } = uiActions;
|
||||
const trigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: 'MY-TRIGGER' as TriggerId,
|
||||
title: 'My trigger',
|
||||
};
|
||||
const action1 = createTestAction('test1', () => true);
|
||||
|
@ -129,7 +132,7 @@ test('shows a context menu when more than one action is mapped to a trigger', as
|
|||
|
||||
const start = doStart();
|
||||
const context = {};
|
||||
await start.executeTriggerActions('MY-TRIGGER', context);
|
||||
await start.executeTriggerActions('MY-TRIGGER' as TriggerId, context);
|
||||
|
||||
expect(executeFn).toBeCalledTimes(0);
|
||||
expect(openContextMenu).toHaveBeenCalledTimes(1);
|
||||
|
@ -138,7 +141,7 @@ 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 } = uiActions;
|
||||
const trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: 'MY-TRIGGER' as TriggerId,
|
||||
title: 'My trigger',
|
||||
};
|
||||
const action = createTestAction<{ foo: string }>('test', ({ foo }) => {
|
||||
|
@ -153,5 +156,5 @@ test('passes whole action context to isCompatible()', async () => {
|
|||
const start = doStart();
|
||||
|
||||
const context = { foo: 'bar' };
|
||||
await start.executeTriggerActions('MY-TRIGGER', context);
|
||||
await start.executeTriggerActions('MY-TRIGGER' as TriggerId, context);
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import { Action } from '../actions';
|
||||
import { uiActionsPluginMock } from '../mocks';
|
||||
import { TriggerId } from '../types';
|
||||
|
||||
const action1: Action = {
|
||||
id: 'action1',
|
||||
|
@ -37,23 +38,23 @@ test('returns actions set on trigger', () => {
|
|||
setup.registerAction(action2);
|
||||
setup.registerTrigger({
|
||||
description: 'foo',
|
||||
id: 'trigger',
|
||||
id: 'trigger' as TriggerId,
|
||||
title: 'baz',
|
||||
});
|
||||
|
||||
const start = doStart();
|
||||
const list0 = start.getTriggerActions('trigger');
|
||||
const list0 = start.getTriggerActions('trigger' as TriggerId);
|
||||
|
||||
expect(list0).toHaveLength(0);
|
||||
|
||||
setup.attachAction('trigger', 'action1');
|
||||
const list1 = start.getTriggerActions('trigger');
|
||||
setup.attachAction('trigger' as TriggerId, 'action1');
|
||||
const list1 = start.getTriggerActions('trigger' as TriggerId);
|
||||
|
||||
expect(list1).toHaveLength(1);
|
||||
expect(list1).toEqual([action1]);
|
||||
|
||||
setup.attachAction('trigger', 'action2');
|
||||
const list2 = start.getTriggerActions('trigger');
|
||||
setup.attachAction('trigger' as TriggerId, 'action2');
|
||||
const list2 = start.getTriggerActions('trigger' as TriggerId);
|
||||
|
||||
expect(list2).toHaveLength(2);
|
||||
expect(!!list2.find(({ id }: any) => id === 'action1')).toBe(true);
|
||||
|
|
|
@ -22,6 +22,7 @@ import { uiActionsPluginMock } from '../mocks';
|
|||
import { createRestrictedAction, createHelloWorldAction } from '../tests/test_samples';
|
||||
import { Action } from '../actions';
|
||||
import { Trigger } from '../triggers';
|
||||
import { TriggerId } from '../types';
|
||||
|
||||
let action: Action<{ name: string }>;
|
||||
let uiActions: ReturnType<typeof uiActionsPluginMock.createPlugin>;
|
||||
|
@ -31,10 +32,10 @@ beforeEach(() => {
|
|||
|
||||
uiActions.setup.registerAction(action);
|
||||
uiActions.setup.registerTrigger({
|
||||
id: 'trigger',
|
||||
id: 'trigger' as TriggerId,
|
||||
title: 'trigger',
|
||||
});
|
||||
uiActions.setup.attachAction('trigger', action.id);
|
||||
uiActions.setup.attachAction('trigger' as TriggerId, action.id);
|
||||
});
|
||||
|
||||
test('can register action', async () => {
|
||||
|
@ -51,14 +52,14 @@ test('getTriggerCompatibleActions returns attached actions', async () => {
|
|||
setup.registerAction(helloWorldAction);
|
||||
|
||||
const testTrigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: 'MY-TRIGGER' as TriggerId,
|
||||
title: 'My trigger',
|
||||
};
|
||||
setup.registerTrigger(testTrigger);
|
||||
setup.attachAction('MY-TRIGGER', helloWorldAction.id);
|
||||
setup.attachAction('MY-TRIGGER' as TriggerId, helloWorldAction.id);
|
||||
|
||||
const start = doStart();
|
||||
const actions = await start.getTriggerCompatibleActions('MY-TRIGGER', {});
|
||||
const actions = await start.getTriggerCompatibleActions('MY-TRIGGER' as TriggerId, {});
|
||||
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].id).toBe(helloWorldAction.id);
|
||||
|
@ -73,7 +74,7 @@ test('filters out actions not applicable based on the context', async () => {
|
|||
setup.registerAction(restrictedAction);
|
||||
|
||||
const testTrigger: Trigger = {
|
||||
id: 'MY-TRIGGER',
|
||||
id: 'MY-TRIGGER' as TriggerId,
|
||||
title: 'My trigger',
|
||||
};
|
||||
|
||||
|
@ -94,15 +95,15 @@ test(`throws an error with an invalid trigger ID`, async () => {
|
|||
const { doStart } = uiActions;
|
||||
const start = doStart();
|
||||
|
||||
await expect(start.getTriggerCompatibleActions('I do not exist', {})).rejects.toMatchObject(
|
||||
new Error('Trigger [triggerId = I do not exist] does not exist.')
|
||||
);
|
||||
await expect(
|
||||
start.getTriggerCompatibleActions('I do not exist' as TriggerId, {})
|
||||
).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 } = uiActions;
|
||||
const testTrigger: Trigger = {
|
||||
id: '123',
|
||||
id: '123' as TriggerId,
|
||||
title: '123',
|
||||
};
|
||||
setup.registerTrigger(testTrigger);
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TriggerContext } from './trigger';
|
||||
import { TriggerInternal } from './trigger_internal';
|
||||
import { TriggerId } from '../types';
|
||||
import { TriggerId, TriggerContextMapping } from '../types';
|
||||
|
||||
/**
|
||||
* This is a public representation of a trigger that is provided to other plugins.
|
||||
|
@ -50,7 +49,7 @@ export class TriggerContract<T extends TriggerId> {
|
|||
/**
|
||||
* Use this method to execute action attached to this trigger.
|
||||
*/
|
||||
public readonly exec = async (context: TriggerContext<T>) => {
|
||||
public readonly exec = async (context: TriggerContextMapping[T]) => {
|
||||
await this.internal.execute(context);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { TriggerContext, Trigger } from './trigger';
|
||||
import { Trigger } from './trigger';
|
||||
import { TriggerContract } from './trigger_contract';
|
||||
import { UiActionsService } from '../service';
|
||||
import { Action } from '../actions';
|
||||
import { buildContextMenuForActions, openContextMenu } from '../context_menu';
|
||||
import { TriggerId } from '../types';
|
||||
import { TriggerId, TriggerContextMapping } from '../types';
|
||||
|
||||
/**
|
||||
* Internal representation of a trigger kept for consumption only internally
|
||||
|
@ -33,7 +33,7 @@ export class TriggerInternal<T extends TriggerId> {
|
|||
|
||||
constructor(public readonly service: UiActionsService, public readonly trigger: Trigger<T>) {}
|
||||
|
||||
public async execute(context: TriggerContext<T>) {
|
||||
public async execute(context: TriggerContextMapping[T]) {
|
||||
const triggerId = this.trigger.id;
|
||||
const actions = await this.service.getTriggerCompatibleActions!(triggerId, context);
|
||||
|
||||
|
@ -51,7 +51,10 @@ export class TriggerInternal<T extends TriggerId> {
|
|||
await this.executeMultipleActions(actions, context);
|
||||
}
|
||||
|
||||
private async executeSingleAction(action: Action<TriggerContext<T>>, context: TriggerContext<T>) {
|
||||
private async executeSingleAction(
|
||||
action: Action<TriggerContextMapping[T]>,
|
||||
context: TriggerContextMapping[T]
|
||||
) {
|
||||
const href = action.getHref && action.getHref(context);
|
||||
|
||||
if (href) {
|
||||
|
@ -63,8 +66,8 @@ export class TriggerInternal<T extends TriggerId> {
|
|||
}
|
||||
|
||||
private async executeMultipleActions(
|
||||
actions: Array<Action<TriggerContext<T>>>,
|
||||
context: TriggerContext<T>
|
||||
actions: Array<Action<TriggerContextMapping[T]>>,
|
||||
context: TriggerContextMapping[T]
|
||||
) {
|
||||
const panel = await buildContextMenuForActions({
|
||||
actions,
|
||||
|
|
|
@ -20,12 +20,17 @@
|
|||
import { Action } from './actions/action';
|
||||
import { TriggerInternal } from './triggers/trigger_internal';
|
||||
|
||||
export type TriggerRegistry = Map<string, TriggerInternal<any>>;
|
||||
export type ActionRegistry = Map<string, Action>;
|
||||
export type TriggerToActionsRegistry = Map<string, string[]>;
|
||||
export type TriggerRegistry = Map<TriggerId, TriggerInternal<any>>;
|
||||
export type ActionRegistry = Map<string, Action<any>>;
|
||||
export type TriggerToActionsRegistry = Map<TriggerId, string[]>;
|
||||
|
||||
export type TriggerId = string;
|
||||
const DEFAULT_TRIGGER = '';
|
||||
|
||||
export type TriggerId = keyof TriggerContextMapping;
|
||||
|
||||
export type TriggerContext = BaseContext;
|
||||
export type BaseContext = object | undefined | string | number;
|
||||
|
||||
export interface TriggerContextMapping {
|
||||
[key: string]: object;
|
||||
[DEFAULT_TRIGGER]: TriggerContext;
|
||||
}
|
||||
|
|
|
@ -23,12 +23,12 @@ import {
|
|||
GetEmbeddableFactory,
|
||||
GetEmbeddableFactories,
|
||||
} from 'src/legacy/core_plugins/embeddable_api/public/np_ready/public';
|
||||
import { GetActionsCompatibleWithTrigger } from '../../../../../../../../src/plugins/ui_actions/public';
|
||||
import { UiActionsService } from '../../../../../../../../src/plugins/ui_actions/public';
|
||||
import { DashboardContainerExample } from './dashboard_container_example';
|
||||
import { Start as InspectorStartContract } from '../../../../../../../../src/plugins/inspector/public';
|
||||
|
||||
export interface AppProps {
|
||||
getActions: GetActionsCompatibleWithTrigger;
|
||||
getActions: UiActionsService['getTriggerCompatibleActions'];
|
||||
getEmbeddableFactory: GetEmbeddableFactory;
|
||||
getAllEmbeddableFactories: GetEmbeddableFactories;
|
||||
overlays: CoreStart['overlays'];
|
||||
|
|
|
@ -35,10 +35,10 @@ import {
|
|||
import { CoreStart } from '../../../../../../../../src/core/public';
|
||||
import { dashboardInput } from './dashboard_input';
|
||||
import { Start as InspectorStartContract } from '../../../../../../../../src/plugins/inspector/public';
|
||||
import { GetActionsCompatibleWithTrigger } from '../../../../../../../../src/plugins/ui_actions/public';
|
||||
import { UiActionsService } from '../../../../../../../../src/plugins/ui_actions/public';
|
||||
|
||||
interface Props {
|
||||
getActions: GetActionsCompatibleWithTrigger;
|
||||
getActions: UiActionsService['getTriggerCompatibleActions'];
|
||||
getEmbeddableFactory: GetEmbeddableFactory;
|
||||
getAllEmbeddableFactories: GetEmbeddableFactories;
|
||||
overlays: CoreStart['overlays'];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue