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

Drilldowns multitrigger #60357

Merged
merged 2 commits into from
Mar 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
case 'histogram':
case 'horizontal_bar':
case 'line':
case 'metrics':
case 'pie':
case 'table':
case 'tagcloud':
Expand All @@ -415,7 +416,6 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
case 'input_control_vis':
case 'markdown':
case 'metric':
case 'metrics':
case 'region_map':
case 'tile_map':
case 'timelion':
Expand Down
31 changes: 18 additions & 13 deletions src/plugins/ui_actions/public/actions/dynamic_action_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Subscription } from 'rxjs';
import { ActionStorage, SerializedEvent } from './dynamic_action_storage';
import { UiActionsService } from '../service';
import { SerializedAction } from './types';
import { TriggerContextMapping } from '../types';
import { ActionDefinition } from './action';
import { defaultState, transitions, selectors, State } from './dynamic_action_manager_state';
import { StateContainer, createStateContainer } from '../../../kibana_utils';
Expand All @@ -41,7 +42,7 @@ export interface DynamicActionManagerParams {
storage: ActionStorage;
uiActions: Pick<
UiActionsService,
'addTriggerAction' | 'removeTriggerAction' | 'getActionFactory'
'registerAction' | '__attachAction' | 'unregisterAction' | 'detachAction' | 'getActionFactory'
>;
isCompatible: <C = unknown>(context: C) => Promise<boolean>;
}
Expand Down Expand Up @@ -75,7 +76,7 @@ export class DynamicActionManager {
}

protected reviveAction(event: SerializedEvent) {
const { eventId, triggerId, action } = event;
const { eventId, triggers, action } = event;
const { uiActions, isCompatible } = this.params;
const { name } = action;

Expand All @@ -88,13 +89,16 @@ export class DynamicActionManager {
getDisplayName: () => name,
};

uiActions.addTriggerAction(triggerId as any, actionDefinition);
uiActions.registerAction(actionDefinition);
for (const trigger of triggers) uiActions.__attachAction(trigger as any, actionId);
}

protected killAction({ eventId, triggerId }: SerializedEvent) {
protected killAction({ eventId, triggers }: SerializedEvent) {
const { uiActions } = this.params;
const actionId = this.generateActionId(eventId);
uiActions.removeTriggerAction(triggerId as any, actionId);

for (const trigger of triggers) uiActions.detachAction(trigger as any, actionId);
uiActions.unregisterAction(actionId);
}

private syncId = 0;
Expand Down Expand Up @@ -179,15 +183,16 @@ export class DynamicActionManager {
* 2. Optimistically adds it to UI state, and rolls back on failure.
* 3. Adds action to `ui_actions` registry.
*
* @todo `triggerId` should not be optional.
*
* @param action Dynamic action for which to create an event.
* @param triggerId Trigger to which to attach the action.
* @param triggers List of triggers to which action should react.
*/
public async createEvent(action: SerializedAction<unknown>, triggerId = 'VALUE_CLICK_TRIGGER') {
public async createEvent(
action: SerializedAction<unknown>,
triggers: Array<keyof TriggerContextMapping>
) {
const event: SerializedEvent = {
eventId: uuidv4(),
triggerId,
triggers,
action,
};

Expand All @@ -212,16 +217,16 @@ export class DynamicActionManager {
*
* @param eventId ID of the event to replace.
* @param action New action for which to create the event.
* @param triggerId New trigger with which to associate the event.
* @param triggers List of triggers to which action should react.
*/
public async updateEvent(
eventId: string,
action: SerializedAction<unknown>,
triggerId = 'VALUE_CLICK_TRIGGER'
triggers: Array<keyof TriggerContextMapping>
) {
const event: SerializedEvent = {
eventId,
triggerId,
triggers,
action,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { SerializedAction } from './types';
*/
export interface SerializedEvent {
eventId: string;
triggerId: string;
triggers: string[];
action: SerializedAction<unknown>;
}

Expand Down
2 changes: 2 additions & 0 deletions src/plugins/ui_actions/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const createSetupContract = (): Setup => {

const createStartContract = (): Start => {
const startContract: Start = {
__attachAction: jest.fn(),
unregisterAction: jest.fn(),
addTriggerAction: jest.fn(),
attachAction: jest.fn(),
clear: jest.fn(),
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/ui_actions/public/service/ui_actions_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class UiActionsService {
return action;
};

protected readonly unregisterAction = (actionId: string): void => {
public readonly unregisterAction = (actionId: string): void => {
if (!this.actions.has(actionId)) {
throw new Error(`Action [action.id = ${actionId}] is not registered.`);
}
Expand Down Expand Up @@ -133,7 +133,7 @@ export class UiActionsService {

// public readonly removeTriggerAction =

protected readonly __attachAction = <TriggerId extends keyof TriggerContextMapping>(
public readonly __attachAction = <TriggerId extends keyof TriggerContextMapping>(
triggerId: TriggerId,
actionId: string
): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ test('Create only mode', async () => {

await wait(() => expect(notifications.toasts.addSuccess).toBeCalled());
expect(onClose).toBeCalled();
expect(await mockDynamicActionManager.count()).toBe(1);
expect(await mockDynamicActionManager.state.get().events.length).toBe(1);
});

test.todo("Error when can't fetch drilldown list");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import {
DynamicActionManager,
UiActionsSerializedEvent,
UiActionsSerializedAction,
VALUE_CLICK_TRIGGER,
SELECT_RANGE_TRIGGER,
TriggerContextMapping,
} from '../../../../../../src/plugins/ui_actions/public';
import { useContainerState } from '../../../../../../src/plugins/kibana_utils/common';
import { DrilldownListItem } from '../list_manage_drilldowns';
Expand Down Expand Up @@ -66,6 +69,11 @@ export function createFlyoutManageDrilldowns({
return (props: ConnectedFlyoutManageDrilldownsProps) => {
const isCreateOnly = props.viewMode === 'create';

const selectedTriggers: Array<keyof TriggerContextMapping> = React.useMemo(
() => [VALUE_CLICK_TRIGGER, SELECT_RANGE_TRIGGER],
[]
);

const factoryContext: DrilldownFactoryContext<unknown> = React.useMemo(
() => ({
place: '',
Expand Down Expand Up @@ -149,18 +157,24 @@ export function createFlyoutManageDrilldowns({
onBack={isCreateOnly ? undefined : () => setRoute(Routes.Manage)}
onSubmit={({ actionConfig, actionFactory, name }) => {
if (route === Routes.Create) {
createDrilldown({
name,
config: actionConfig,
factoryId: actionFactory.id,
});
createDrilldown(
{
name,
config: actionConfig,
factoryId: actionFactory.id,
},
selectedTriggers
);
} else {
// edit
editDrilldown(currentEditId!, {
name,
config: actionConfig,
factoryId: actionFactory.id,
});
editDrilldown(
currentEditId!,
{
name,
config: actionConfig,
factoryId: actionFactory.id,
},
selectedTriggers
);
}

if (isCreateOnly) {
Expand Down Expand Up @@ -270,9 +284,12 @@ function useDrilldownsStateManager(
}
}

async function createDrilldown(action: UiActionsSerializedAction<any>, triggerId?: string) {
async function createDrilldown(
action: UiActionsSerializedAction<any>,
selectedTriggers: Array<keyof TriggerContextMapping>
) {
await run(async () => {
await actionManager.createEvent(action, triggerId);
await actionManager.createEvent(action, selectedTriggers);
notifications.toasts.addSuccess({
title: toastDrilldownCreated.title,
text: toastDrilldownCreated.text(action.name),
Expand All @@ -283,10 +300,10 @@ function useDrilldownsStateManager(
async function editDrilldown(
drilldownId: string,
action: UiActionsSerializedAction<any>,
triggerId?: string
selectedTriggers: Array<keyof TriggerContextMapping>
) {
await run(async () => {
await actionManager.updateEvent(drilldownId, action, triggerId);
await actionManager.updateEvent(drilldownId, action, selectedTriggers);
notifications.toasts.addSuccess({
title: toastDrilldownEdited.title,
text: toastDrilldownEdited.text(action.name),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
DynamicActionManager,
DynamicActionManagerState,
UiActionsSerializedAction,
TriggerContextMapping,
} from '../../../../../../src/plugins/ui_actions/public';
import { createStateContainer } from '../../../../../../src/plugins/kibana_utils/common';

Expand All @@ -29,11 +30,11 @@ class MockDynamicActionManager implements PublicMethodsOf<DynamicActionManager>

async createEvent(
action: UiActionsSerializedAction<any>,
triggerId: string = 'VALUE_CLICK_TRIGGER'
triggers: Array<keyof TriggerContextMapping>
) {
const event = {
action,
triggerId,
triggers,
eventId: uuid(),
};
const state = this.state.get();
Expand All @@ -60,15 +61,15 @@ class MockDynamicActionManager implements PublicMethodsOf<DynamicActionManager>
async updateEvent(
eventId: string,
action: UiActionsSerializedAction<unknown>,
triggerId: string = 'VALUE_CLICK_TRIGGER'
triggers: Array<keyof TriggerContextMapping>
) {
const state = this.state.get();
const events = state.events;
const idx = events.findIndex(e => e.eventId === eventId);
const event = {
eventId,
action,
triggerId,
triggers,
};

this.state.set({
Expand Down