Skip to content

Commit

Permalink
[uiActions] Support emitting nested triggers and actions (elastic#70602)
Browse files Browse the repository at this point in the history
* Introduce automatically executed actions
* Introduce batching of emitted triggers to be execute on the macro task
  • Loading branch information
Dosant committed Jul 16, 2020
1 parent 907e43b commit b766101
Show file tree
Hide file tree
Showing 27 changed files with 368 additions and 215 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) &gt; [embeddable](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md)

## ApplyGlobalFilterActionContext.embeddable property

<b>Signature:</b>

```typescript
embeddable?: IEmbeddable;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) &gt; [filters](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md)

## ApplyGlobalFilterActionContext.filters property

<b>Signature:</b>

```typescript
filters: Filter[];
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md)

## ApplyGlobalFilterActionContext interface

<b>Signature:</b>

```typescript
export interface ApplyGlobalFilterActionContext
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [embeddable](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md) | <code>IEmbeddable</code> | |
| [filters](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md) | <code>Filter[]</code> | |
| [timeFieldName](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md) | <code>string</code> | |

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) &gt; [timeFieldName](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md)

## ApplyGlobalFilterActionContext.timeFieldName property

<b>Signature:</b>

```typescript
timeFieldName?: string;
```
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
| Interface | Description |
| --- | --- |
| [AggParamOption](./kibana-plugin-plugins-data-public.aggparamoption.md) | |
| [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) | |
| [DataPublicPluginSetup](./kibana-plugin-plugins-data-public.datapublicpluginsetup.md) | |
| [DataPublicPluginStart](./kibana-plugin-plugins-data-public.datapublicpluginstart.md) | |
| [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
<b>Signature:</b>

```typescript
setup(core: CoreSetup, { expressions, uiActions, usageCollection }: DataSetupDependencies): DataPublicPluginSetup;
setup(core: CoreSetup<DataStartDependencies, DataPublicPluginStart>, { expressions, uiActions, usageCollection }: DataSetupDependencies): DataPublicPluginSetup;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| core | <code>CoreSetup</code> | |
| core | <code>CoreSetup&lt;DataStartDependencies, DataPublicPluginStart&gt;</code> | |
| { expressions, uiActions, usageCollection } | <code>DataSetupDependencies</code> | |

<b>Returns:</b>
Expand Down
16 changes: 5 additions & 11 deletions examples/ui_actions_explorer/public/actions/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const ACTION_VIEW_IN_MAPS = 'ACTION_VIEW_IN_MAPS';
export const ACTION_TRAVEL_GUIDE = 'ACTION_TRAVEL_GUIDE';
export const ACTION_CALL_PHONE_NUMBER = 'ACTION_CALL_PHONE_NUMBER';
export const ACTION_EDIT_USER = 'ACTION_EDIT_USER';
export const ACTION_PHONE_USER = 'ACTION_PHONE_USER';
export const ACTION_TRIGGER_PHONE_USER = 'ACTION_TRIGGER_PHONE_USER';
export const ACTION_SHOWCASE_PLUGGABILITY = 'ACTION_SHOWCASE_PLUGGABILITY';

export const showcasePluggability = createAction<typeof ACTION_SHOWCASE_PLUGGABILITY>({
Expand Down Expand Up @@ -120,19 +120,13 @@ export interface UserContext {
update: (user: User) => void;
}

export const createPhoneUserAction = (getUiActionsApi: () => Promise<UiActionsStart>) =>
createAction<typeof ACTION_PHONE_USER>({
type: ACTION_PHONE_USER,
export const createTriggerPhoneTriggerAction = (getUiActionsApi: () => Promise<UiActionsStart>) =>
createAction<typeof ACTION_TRIGGER_PHONE_USER>({
type: ACTION_TRIGGER_PHONE_USER,
getDisplayName: () => 'Call phone number',
shouldAutoExecute: async () => true,
isCompatible: async ({ user }) => user.phone !== undefined,
execute: async ({ user }) => {
// One option - execute the more specific action directly.
// makePhoneCallAction.execute({ phone: user.phone });

// Another option - emit the trigger and automatically get *all* the actions attached
// 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.
if (user.phone !== undefined) {
(await getUiActionsApi()).executeTriggerActions(PHONE_TRIGGER, { phone: user.phone });
}
Expand Down
8 changes: 4 additions & 4 deletions examples/ui_actions_explorer/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
PHONE_TRIGGER,
USER_TRIGGER,
COUNTRY_TRIGGER,
createPhoneUserAction,
lookUpWeatherAction,
viewInMapsAction,
createEditUserAction,
Expand All @@ -37,7 +36,8 @@ import {
ACTION_CALL_PHONE_NUMBER,
ACTION_TRAVEL_GUIDE,
ACTION_VIEW_IN_MAPS,
ACTION_PHONE_USER,
ACTION_TRIGGER_PHONE_USER,
createTriggerPhoneTriggerAction,
} from './actions/actions';
import { DeveloperExamplesSetup } from '../../developer_examples/public';
import image from './ui_actions.png';
Expand All @@ -64,7 +64,7 @@ declare module '../../../src/plugins/ui_actions/public' {
[ACTION_CALL_PHONE_NUMBER]: PhoneContext;
[ACTION_TRAVEL_GUIDE]: CountryContext;
[ACTION_VIEW_IN_MAPS]: CountryContext;
[ACTION_PHONE_USER]: UserContext;
[ACTION_TRIGGER_PHONE_USER]: UserContext;
}
}

Expand All @@ -84,7 +84,7 @@ export class UiActionsExplorerPlugin implements Plugin<void, void, {}, StartDeps

deps.uiActions.addTriggerAction(
USER_TRIGGER,
createPhoneUserAction(async () => (await startServices)[1].uiActions)
createTriggerPhoneTriggerAction(async () => (await startServices)[1].uiActions)
);
deps.uiActions.addTriggerAction(
USER_TRIGGER,
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/data/public/actions/apply_filter_action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ import { toMountPoint } from '../../../kibana_react/public';
import { ActionByType, createAction, IncompatibleActionError } from '../../../ui_actions/public';
import { getOverlays, getIndexPatterns } from '../services';
import { applyFiltersPopover } from '../ui/apply_filters';
import type { IEmbeddable } from '../../../embeddable/public';
import { Filter, FilterManager, TimefilterContract, esFilters } from '..';

export const ACTION_GLOBAL_APPLY_FILTER = 'ACTION_GLOBAL_APPLY_FILTER';

export interface ApplyGlobalFilterActionContext {
filters: Filter[];
timeFieldName?: string;
embeddable?: IEmbeddable;
}

async function isCompatible(context: ApplyGlobalFilterActionContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import moment from 'moment';
import { esFilters, IFieldType, RangeFilterParams } from '../../../public';
import { getIndexPatterns } from '../../../public/services';
import { deserializeAggConfig } from '../../search/expressions/utils';
import { RangeSelectContext } from '../../../../embeddable/public';
import type { RangeSelectContext } from '../../../../embeddable/public';

export async function createFiltersFromRangeSelectAction(event: RangeSelectContext['data']) {
const column: Record<string, any> = event.table.columns[event.column];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { KibanaDatatable } from '../../../../../plugins/expressions/public';
import { deserializeAggConfig } from '../../search/expressions';
import { esFilters, Filter } from '../../../public';
import { getIndexPatterns } from '../../../public/services';
import { ValueClickContext } from '../../../../embeddable/public';
import type { ValueClickContext } from '../../../../embeddable/public';

/**
* For terms aggregations on `__other__` buckets, this assembles a list of applicable filter
Expand Down
10 changes: 7 additions & 3 deletions src/plugins/data/public/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
* under the License.
*/

export { ACTION_GLOBAL_APPLY_FILTER, createFilterAction } from './apply_filter_action';
export {
ACTION_GLOBAL_APPLY_FILTER,
createFilterAction,
ApplyGlobalFilterActionContext,
} from './apply_filter_action';
export { createFiltersFromValueClickAction } from './filters/create_filters_from_value_click';
export { createFiltersFromRangeSelectAction } from './filters/create_filters_from_range_select';
export { selectRangeAction } from './select_range_action';
export { valueClickAction } from './value_click_action';
export * from './select_range_action';
export * from './value_click_action';
61 changes: 20 additions & 41 deletions src/plugins/data/public/actions/select_range_action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,60 +17,39 @@
* under the License.
*/

import { i18n } from '@kbn/i18n';
import {
createAction,
IncompatibleActionError,
ActionByType,
APPLY_FILTER_TRIGGER,
createAction,
UiActionsStart,
} from '../../../../plugins/ui_actions/public';
import { createFiltersFromRangeSelectAction } from './filters/create_filters_from_range_select';
import { RangeSelectContext } from '../../../embeddable/public';
import { FilterManager, TimefilterContract, esFilters } from '..';

export const ACTION_SELECT_RANGE = 'ACTION_SELECT_RANGE';
import type { RangeSelectContext } from '../../../embeddable/public';

export type SelectRangeActionContext = RangeSelectContext;

async function isCompatible(context: SelectRangeActionContext) {
try {
return Boolean(await createFiltersFromRangeSelectAction(context.data));
} catch {
return false;
}
}
export const ACTION_SELECT_RANGE = 'ACTION_SELECT_RANGE';

export function selectRangeAction(
filterManager: FilterManager,
timeFilter: TimefilterContract
export function createSelectRangeAction(
getStartServices: () => { uiActions: UiActionsStart }
): ActionByType<typeof ACTION_SELECT_RANGE> {
return createAction<typeof ACTION_SELECT_RANGE>({
type: ACTION_SELECT_RANGE,
id: ACTION_SELECT_RANGE,
getIconType: () => 'filter',
getDisplayName: () => {
return i18n.translate('data.filter.applyFilterActionTitle', {
defaultMessage: 'Apply filter to current view',
});
},
isCompatible,
execute: async ({ data }: SelectRangeActionContext) => {
if (!(await isCompatible({ data }))) {
throw new IncompatibleActionError();
}

const selectedFilters = await createFiltersFromRangeSelectAction(data);

if (data.timeFieldName) {
const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
data.timeFieldName,
selectedFilters
);
filterManager.addFilters(restOfFilters);
if (timeRangeFilter) {
esFilters.changeTimeFilter(timeFilter, timeRangeFilter);
shouldAutoExecute: async () => true,
execute: async (context: SelectRangeActionContext) => {
try {
const filters = await createFiltersFromRangeSelectAction(context.data);
if (filters.length > 0) {
await getStartServices().uiActions.getTrigger(APPLY_FILTER_TRIGGER).exec({
filters,
embeddable: context.embeddable,
timeFieldName: context.data.timeFieldName,
});
}
} else {
filterManager.addFilters(selectedFilters);
} catch (e) {
// eslint-disable-next-line no-console
console.warn(`Error [ACTION_SELECT_RANGE]: can\'t extract filters from action context`);
}
},
});
Expand Down
Loading

0 comments on commit b766101

Please sign in to comment.