Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UiActions] pass trigger into action execution context #74363

Merged
merged 23 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f52c5c1
pass meta everywhere
Dosant Jul 14, 2020
280f52a
Merge branch 'master' of github.com:elastic/kibana into dev/ui-action…
Dosant Jul 15, 2020
0f10919
simplify
Dosant Jul 15, 2020
55de456
wip
Dosant Jul 15, 2020
865cc06
test
Dosant Jul 15, 2020
2407d49
simpler
Dosant Jul 15, 2020
c06d4f6
simpler
Dosant Jul 15, 2020
b34178c
Merge branch 'master' of github.com:elastic/kibana into dev/ui-action…
Dosant Jul 16, 2020
4618512
improve
Dosant Jul 16, 2020
0694dbd
Merge branch 'master' into dev/ui-actions-improve-context
elasticmachine Jul 20, 2020
1ac7ab3
Merge branch 'master' into dev/ui-actions-improve-context
elasticmachine Aug 5, 2020
60f9e6a
Merge branch 'master' of github.com:elastic/kibana into dev/ui-action…
Dosant Aug 5, 2020
c2fb5fb
Merge branch 'dev/ui-actions-improve-context' of github.com:Dosant/ki…
Dosant Aug 5, 2020
2058792
take different direction
Dosant Aug 5, 2020
c460608
fix
Dosant Aug 5, 2020
2c9c359
Merge branch 'master' into dev/ui-actions-improve-context-2
elasticmachine Aug 6, 2020
4393299
improve
Dosant Aug 6, 2020
5567d8e
Merge branch 'dev/ui-actions-improve-context-2' of github.com:Dosant/…
Dosant Aug 6, 2020
bf563fa
improve
Dosant Aug 6, 2020
ba86f46
Merge branch 'master' of github.com:elastic/kibana into dev/ui-action…
Dosant Aug 10, 2020
0a3aed6
fix typescript
Dosant Aug 10, 2020
0885335
Merge branch 'master' into dev/ui-actions-improve-context-2
elasticmachine Aug 13, 2020
664ffcf
Merge branch 'master' into dev/ui-actions-improve-context-2
elasticmachine Aug 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions examples/ui_actions_explorer/public/actions/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import { OverlayStart } from 'kibana/public';
import { EuiFieldText, EuiModalBody, EuiButton } from '@elastic/eui';
import { useState } from 'react';
import { toMountPoint } from '../../../../src/plugins/kibana_react/public';
import { createAction, UiActionsStart } from '../../../../src/plugins/ui_actions/public';
import {
ActionExecutionContext,
createAction,
UiActionsStart,
} from '../../../../src/plugins/ui_actions/public';

export const USER_TRIGGER = 'USER_TRIGGER';
export const COUNTRY_TRIGGER = 'COUNTRY_TRIGGER';
Expand All @@ -37,7 +41,8 @@ export const ACTION_SHOWCASE_PLUGGABILITY = 'ACTION_SHOWCASE_PLUGGABILITY';
export const showcasePluggability = createAction<typeof ACTION_SHOWCASE_PLUGGABILITY>({
type: ACTION_SHOWCASE_PLUGGABILITY,
getDisplayName: () => 'This is pluggable! Any plugin can inject their actions here.',
execute: async () => alert("Isn't that cool?!"),
execute: async (context: ActionExecutionContext) =>
alert(`Isn't that cool?! Triggered by ${context.trigger?.id} trigger`),
});

export interface PhoneContext {
Expand Down
6 changes: 3 additions & 3 deletions examples/ui_actions_explorer/public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ const ActionsExplorer = ({ uiActionsApi, openModal }: Props) => {
});
uiActionsApi.addTriggerAction(HELLO_WORLD_TRIGGER_ID, dynamicAction);
setConfirmationText(
`You've successfully added a new action: ${dynamicAction.getDisplayName(
{}
)}. Refresh the page to reset state. It's up to the user of the system to persist state like this.`
`You've successfully added a new action: ${dynamicAction.getDisplayName({
trigger: uiActionsApi.getTrigger(HELLO_WORLD_TRIGGER_ID),
})}. Refresh the page to reset state. It's up to the user of the system to persist state like this.`
);
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import { createFilterAction } from './apply_filter_action';
import { expectErrorAsync } from '../../tests/helpers';
import { defaultTrigger } from '../../../../ui_actions/public/triggers';

test('has ACTION_APPLY_FILTER type and id', () => {
const action = createFilterAction();
Expand Down Expand Up @@ -51,6 +52,7 @@ describe('isCompatible()', () => {
}),
} as any,
filters: [],
trigger: defaultTrigger,
});
expect(result).toBe(true);
});
Expand All @@ -66,6 +68,7 @@ describe('isCompatible()', () => {
}),
} as any,
filters: [],
trigger: defaultTrigger,
});
expect(result).toBe(false);
});
Expand Down Expand Up @@ -119,6 +122,7 @@ describe('execute()', () => {
await action.execute({
embeddable,
filters: ['FILTER' as any],
trigger: defaultTrigger,
});

expect(root.updateInput).toHaveBeenCalledTimes(1);
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
PANEL_BADGE_TRIGGER,
PANEL_NOTIFICATION_TRIGGER,
EmbeddableContext,
contextMenuTrigger,
} from '../triggers';
import { IEmbeddable, EmbeddableOutput, EmbeddableError } from '../embeddables/i_embeddable';
import { ViewMode } from '../types';
Expand Down Expand Up @@ -311,7 +312,11 @@ export class EmbeddablePanel extends React.Component<Props, State> {
const sortedActions = [...regularActions, ...extraActions].sort(sortByOrderField);

return await buildContextMenuForActions({
actions: sortedActions.map((action) => [action, { embeddable: this.props.embeddable }]),
actions: sortedActions.map((action) => ({
action,
context: { embeddable: this.props.embeddable },
trigger: contextMenuTrigger,
})),
closeMenu: this.closeMyContextMenuPanel,
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { ContactCardEmbeddable } from '../../../../test_samples';
import { esFilters, Filter } from '../../../../../../../../plugins/data/public';
import { EmbeddableStart } from '../../../../../plugin';
import { embeddablePluginMock } from '../../../../../mocks';
import { defaultTrigger } from '../../../../../../../ui_actions/public/triggers';

const { setup, doStart } = embeddablePluginMock.createInstance();
setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory());
Expand Down Expand Up @@ -85,7 +86,9 @@ test('Is not compatible when container is in view mode', async () => {
() => null
);
container.updateInput({ viewMode: ViewMode.VIEW });
expect(await addPanelAction.isCompatible({ embeddable: container })).toBe(false);
expect(
await addPanelAction.isCompatible({ embeddable: container, trigger: defaultTrigger })
).toBe(false);
});

test('Is not compatible when embeddable is not a container', async () => {
Expand All @@ -94,7 +97,7 @@ test('Is not compatible when embeddable is not a container', async () => {

test('Is compatible when embeddable is a parent and in edit mode', async () => {
container.updateInput({ viewMode: ViewMode.EDIT });
expect(await action.isCompatible({ embeddable: container })).toBe(true);
expect(await action.isCompatible({ embeddable: container, trigger: defaultTrigger })).toBe(true);
});

test('Execute throws an error when called with an embeddable that is not a container', async () => {
Expand All @@ -108,6 +111,7 @@ test('Execute throws an error when called with an embeddable that is not a conta
},
{} as any
),
trigger: defaultTrigger,
} as any);
}
await expect(check()).rejects.toThrow(Error);
Expand All @@ -116,6 +120,7 @@ test('Execute does not throw an error when called with a compatible container',
container.updateInput({ viewMode: ViewMode.EDIT });
await action.execute({
embeddable: container,
trigger: defaultTrigger,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/
import { i18n } from '@kbn/i18n';
import { Action } from 'src/plugins/ui_actions/public';
import { Action, ActionExecutionContext } from 'src/plugins/ui_actions/public';
import { NotificationsStart, OverlayStart } from 'src/core/public';
import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin';
import { ViewMode } from '../../../../types';
Expand Down Expand Up @@ -52,12 +52,14 @@ export class AddPanelAction implements Action<ActionContext> {
return 'plusInCircleFilled';
}

public async isCompatible({ embeddable }: ActionContext) {
public async isCompatible(context: ActionExecutionContext<ActionContext>) {
const { embeddable } = context;
return embeddable.getIsContainer() && embeddable.getInput().viewMode === ViewMode.EDIT;
}

public async execute({ embeddable }: ActionContext) {
if (!embeddable.getIsContainer() || !(await this.isCompatible({ embeddable }))) {
public async execute(context: ActionExecutionContext<ActionContext>) {
const { embeddable } = context;
if (!embeddable.getIsContainer() || !(await this.isCompatible(context))) {
throw new Error('Context is incompatible');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,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';
import { EmbeddableContext, panelBadgeTrigger, panelNotificationTrigger } from '../../triggers';

export interface PanelHeaderProps {
title?: string;
Expand All @@ -49,11 +49,11 @@ function renderBadges(badges: Array<Action<EmbeddableContext>>, embeddable: IEmb
<EuiBadge
key={badge.id}
className="embPanel__headerBadge"
iconType={badge.getIconType({ embeddable })}
onClick={() => badge.execute({ embeddable })}
onClickAriaLabel={badge.getDisplayName({ embeddable })}
iconType={badge.getIconType({ embeddable, trigger: panelBadgeTrigger })}
onClick={() => badge.execute({ embeddable, trigger: panelBadgeTrigger })}
onClickAriaLabel={badge.getDisplayName({ embeddable, trigger: panelBadgeTrigger })}
>
{badge.getDisplayName({ embeddable })}
{badge.getDisplayName({ embeddable, trigger: panelBadgeTrigger })}
</EuiBadge>
));
}
Expand All @@ -70,14 +70,17 @@ function renderNotifications(
data-test-subj={`embeddablePanelNotification-${notification.id}`}
key={notification.id}
style={{ marginTop: '4px', marginRight: '4px' }}
onClick={() => notification.execute(context)}
onClick={() => notification.execute({ ...context, trigger: panelNotificationTrigger })}
>
{notification.getDisplayName(context)}
{notification.getDisplayName({ ...context, trigger: panelNotificationTrigger })}
</EuiNotificationBadge>
);

if (notification.getDisplayNameTooltip) {
const tooltip = notification.getDisplayNameTooltip(context);
const tooltip = notification.getDisplayNameTooltip({
...context,
trigger: panelNotificationTrigger,
});

if (tooltip) {
badge = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { ActionByType, IncompatibleActionError, ActionType } from '../../ui_actions';
import { IncompatibleActionError, ActionType, ActionDefinitionByType } from '../../ui_actions';
import { EmbeddableInput, Embeddable, EmbeddableOutput, IEmbeddable } from '../../embeddables';

// Casting to ActionType is a hack - in a real situation use
Expand All @@ -42,7 +42,7 @@ export interface SayHelloActionContext {
message?: string;
}

export class SayHelloAction implements ActionByType<typeof SAY_HELLO_ACTION> {
export class SayHelloAction implements ActionDefinitionByType<typeof SAY_HELLO_ACTION> {
public readonly type = SAY_HELLO_ACTION;
public readonly id = SAY_HELLO_ACTION;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
FilterableEmbeddableInput,
} from '../lib/test_samples';
import { esFilters } from '../../../data/public';
import { applyFilterTrigger } from '../../../ui_actions/public';

test('ApplyFilterAction applies the filter to the root of the container tree', async () => {
const { doStart, setup } = testPlugin();
Expand Down Expand Up @@ -85,7 +86,7 @@ test('ApplyFilterAction applies the filter to the root of the container tree', a
query: { match: { extension: { query: 'foo' } } },
};

await applyFilterAction.execute({ embeddable, filters: [filter] });
await applyFilterAction.execute({ embeddable, filters: [filter], trigger: applyFilterTrigger });
expect(root.getInput().filters.length).toBe(1);
expect(node1.getInput().filters.length).toBe(1);
expect(embeddable.getInput().filters.length).toBe(1);
Expand Down
13 changes: 10 additions & 3 deletions src/plugins/ui_actions/public/actions/action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
* under the License.
*/

import { createAction } from '../../../ui_actions/public';
import { ActionExecutionContext, createAction } from '../../../ui_actions/public';
import { ActionType } from '../types';
import { defaultTrigger } from '../triggers';

const sayHelloAction = createAction({
// Casting to ActionType is a hack - in a real situation use
Expand All @@ -29,11 +30,17 @@ const sayHelloAction = createAction({
});

test('action is not compatible based on context', async () => {
const isCompatible = await sayHelloAction.isCompatible({ amICompatible: false });
const isCompatible = await sayHelloAction.isCompatible({
amICompatible: false,
trigger: defaultTrigger,
} as ActionExecutionContext);
expect(isCompatible).toBe(false);
});

test('action is compatible based on context', async () => {
const isCompatible = await sayHelloAction.isCompatible({ amICompatible: true });
const isCompatible = await sayHelloAction.isCompatible({
amICompatible: true,
trigger: defaultTrigger,
} as ActionExecutionContext);
expect(isCompatible).toBe(true);
});
Loading