Skip to content

Commit

Permalink
Improve types so emitting the wrong context shape complains, as does …
Browse files Browse the repository at this point in the history
…using a trigger id that has not been added to the trigger context mapping.
  • Loading branch information
stacey-gammon committed Feb 26, 2020
1 parent 8511fe3 commit d5b2c75
Show file tree
Hide file tree
Showing 35 changed files with 278 additions and 219 deletions.
9 changes: 7 additions & 2 deletions examples/ui_action_examples/public/hello_world_action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
28 changes: 16 additions & 12 deletions examples/ui_action_examples/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
}
30 changes: 19 additions & 11 deletions examples/ui_actions_explorer/public/actions/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }>({
Expand All @@ -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');
},
});
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
},
});
2 changes: 1 addition & 1 deletion examples/ui_actions_explorer/public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
11 changes: 11 additions & 0 deletions examples/ui_actions_explorer/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import {
makePhoneCallAction,
showcasePluggability,
SHOWCASE_PLUGGABILITY_ACTION,
UserContext,
CountryContext,
PhoneContext,
} from './actions/actions';

interface StartDeps {
Expand All @@ -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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ const createRowData = (
<Fragment>
<EuiButtonEmpty
onClick={() => {
uiActionsApi.executeTriggerActions(COUNTRY_TRIGGER, {
country: user.countryOfResidence,
});
uiActionsApi.executeTriggerActions(COUNTRY_TRIGGER, user.countryOfResidence);
}}
>
{user.countryOfResidence}
Expand All @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
) {
Expand Down
7 changes: 4 additions & 3 deletions src/plugins/embeddable/public/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -27,6 +27,7 @@ import {
valueClickTrigger,
EmbeddableVisTriggerContext,
IEmbeddable,
EmbeddableContext,
APPLY_FILTER_TRIGGER,
VALUE_CLICK_TRIGGER,
SELECT_RANGE_TRIGGER,
Expand All @@ -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;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
24 changes: 13 additions & 11 deletions src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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'];
Expand All @@ -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> {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,20 @@ 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;
isViewMode: boolean;
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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit d5b2c75

Please sign in to comment.