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

Drilldown events 3 #59854

Merged
merged 5 commits into from
Mar 11, 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 @@ -48,6 +48,7 @@ import { buildPipeline } from '../legacy/build_pipeline';
import { Vis } from '../vis';
import { getExpressions, getUiActions } from '../services';
import { VisSavedObject } from '../types';
import { VisualizationsStartDeps } from '../plugin';

const getKeys = <T extends {}>(o: T): Array<keyof T> => Object.keys(o) as Array<keyof T>;

Expand All @@ -58,6 +59,7 @@ export interface VisualizeEmbeddableConfiguration {
editable: boolean;
appState?: { save(): void };
uiState?: PersistedState;
uiActions?: VisualizationsStartDeps['uiActions'];
}

export interface VisualizeInput extends EmbeddableInput {
Expand Down Expand Up @@ -107,6 +109,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
editable,
appState,
uiState,
uiActions,
}: VisualizeEmbeddableConfiguration,
initialInput: VisualizeInput,
parent?: Container
Expand All @@ -121,7 +124,8 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
savedObjectId: savedVisualization.id!,
visTypeName: savedVisualization.vis.type.name,
},
parent
parent,
{ uiActions }
);
this.timefilter = timefilter;
this.appState = appState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
getTimeFilter,
} from '../services';
import { showNewVisModal } from '../wizard';
import { VisualizationsStartDeps } from '../plugin';

interface VisualizationAttributes extends SavedObjectAttributes {
visState: string;
Expand All @@ -52,7 +53,11 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
> {
public readonly type = VISUALIZE_EMBEDDABLE_TYPE;

constructor() {
constructor(
private readonly getServices: () => Promise<
[unknown, Pick<VisualizationsStartDeps, 'uiActions'>]
>
) {
super({
savedObjectMetaData: {
name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }),
Expand Down Expand Up @@ -114,6 +119,8 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<

const indexPattern = await getIndexPattern(savedObject);
const indexPatterns = indexPattern ? [indexPattern] : [];
const [, { uiActions }] = await this.getServices();

return new VisualizeEmbeddable(
getTimeFilter(),
{
Expand All @@ -123,6 +130,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
editable: this.isEditable(),
appState: input.appState,
uiState: input.uiState,
uiActions,
},
input,
parent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { PluginInitializerContext } from '../../../../../../core/public';
import { CoreSetup, PluginInitializerContext } from '../../../../../../core/public';
import { VisualizationsSetup, VisualizationsStart } from './';
import { VisualizationsPlugin } from './plugin';
import { coreMock } from '../../../../../../core/public/mocks';
Expand All @@ -26,6 +26,7 @@ import { expressionsPluginMock } from '../../../../../../plugins/expressions/pub
import { dataPluginMock } from '../../../../../../plugins/data/public/mocks';
import { usageCollectionPluginMock } from '../../../../../../plugins/usage_collection/public/mocks';
import { uiActionsPluginMock } from '../../../../../../plugins/ui_actions/public/mocks';
import { VisualizationsStartDeps } from './plugin';

const createSetupContract = (): VisualizationsSetup => ({
createBaseVisualization: jest.fn(),
Expand All @@ -46,7 +47,7 @@ const createStartContract = (): VisualizationsStart => ({
const createInstance = async () => {
const plugin = new VisualizationsPlugin({} as PluginInitializerContext);

const setup = plugin.setup(coreMock.createSetup(), {
const setup = plugin.setup(coreMock.createSetup() as CoreSetup<VisualizationsStartDeps>, {
data: dataPluginMock.createSetupContract(),
expressions: expressionsPluginMock.createSetupContract(),
embeddable: embeddablePluginMock.createStartContract(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export class VisualizationsPlugin
constructor(initializerContext: PluginInitializerContext) {}

public setup(
core: CoreSetup,
core: CoreSetup<VisualizationsStartDeps>,
{ expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps
): VisualizationsSetup {
setUISettings(core.uiSettings);
Expand All @@ -118,7 +118,7 @@ export class VisualizationsPlugin
expressions.registerFunction(visualizationFunction);
expressions.registerRenderer(visualizationRenderer);

const embeddableFactory = new VisualizeEmbeddableFactory();
const embeddableFactory = new VisualizeEmbeddableFactory(core.getStartServices);
embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory);

return {
Expand Down
12 changes: 11 additions & 1 deletion src/plugins/embeddable/public/lib/embeddables/embeddable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,16 @@ import { IContainer } from '../containers';
import { IEmbeddable, EmbeddableInput, EmbeddableOutput } from './i_embeddable';
import { ViewMode } from '../types';
import { EmbeddableActionStorage } from './embeddable_action_storage';
import { UiActionsStart } from '../ui_actions';

function getPanelTitle(input: EmbeddableInput, output: EmbeddableOutput) {
return input.hidePanelTitles ? '' : input.title === undefined ? output.defaultTitle : input.title;
}

export interface EmbeddableParams {
uiActions?: UiActionsStart;
}

export abstract class Embeddable<
TEmbeddableInput extends EmbeddableInput = EmbeddableInput,
TEmbeddableOutput extends EmbeddableOutput = EmbeddableOutput
Expand Down Expand Up @@ -55,7 +60,12 @@ export abstract class Embeddable<
return this.__actionStorage || (this.__actionStorage = new EmbeddableActionStorage(this));
}

constructor(input: TEmbeddableInput, output: TEmbeddableOutput, parent?: IContainer) {
constructor(
input: TEmbeddableInput,
output: TEmbeddableOutput,
parent?: IContainer,
public readonly params: EmbeddableParams = {}
) {
this.id = input.id;
this.output = {
title: getPanelTitle(input, output),
Expand Down
70 changes: 70 additions & 0 deletions src/plugins/ui_actions/public/actions/action_factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { uiToReactComponent } from '../../../kibana_react/public';
import { Presentable } from '../util/presentable';
import { ActionDefinition } from './action_definition';
import {
AnyActionFactoryDefinition,
AFDConfig as Config,
AFDFactoryContext as FactoryContext,
AFDActionContext as ActionContext,
} from './action_factory_definition';
import { Configurable } from '../util';

export class ActionFactory<D extends AnyActionFactoryDefinition>
implements Presentable<FactoryContext<D>>, Configurable<Config<D>> {
constructor(public readonly definition: D) {}

public readonly id = this.definition.id;
public readonly order = this.definition.order || 0;
public readonly MenuItem? = this.definition.MenuItem;
public readonly ReactMenuItem? = this.MenuItem ? uiToReactComponent(this.MenuItem) : undefined;

public readonly CollectConfig = this.definition.CollectConfig;
public readonly ReactCollectConfig = uiToReactComponent(this.CollectConfig);
public readonly createConfig = this.definition.createConfig;
public readonly isConfigValid = this.definition.isConfigValid;

public getIconType(context: FactoryContext<D>): string | undefined {
if (!this.definition.getIconType) return undefined;
return this.definition.getIconType(context);
}

public getDisplayName(context: FactoryContext<D>): string {
if (!this.definition.getDisplayName) return '';
return this.definition.getDisplayName(context);
}

public async isCompatible(context: FactoryContext<D>): Promise<boolean> {
if (!this.definition.isCompatible) return true;
return await this.definition.isCompatible(context);
}

public getHref(context: FactoryContext<D>): string | undefined {
if (!this.definition.getHref) return undefined;
return this.definition.getHref(context);
}

public create(config: Config<D>): ActionDefinition<ActionContext<D>> {
return this.definition.create(config);
}
}

export type AnyActionFactory = ActionFactory<any>;
57 changes: 57 additions & 0 deletions src/plugins/ui_actions/public/actions/action_factory_definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { ActionDefinition } from './action_definition';
import { Presentable, Configurable } from '../util';

/**
* This is a convenience interface for registering new action factories.
*/
export interface ActionFactoryDefinition<
Config extends object = object,
FactoryContext extends object = object,
ActionContext extends object = object
> extends Partial<Presentable<FactoryContext>>, Configurable<Config> {
/**
* Unique ID of the action factory. This ID is used to identify this action
* factory in the registry as well as to construct actions of this ID and
* identify this action factory when presenting it to the user in UI.
*/
id: string;

/**
* This method should return a definition of a new action, normally used to
* register it in `ui_actions` registry.
*/
create(config: Config): ActionDefinition<any>; // TODO: FIX THIS....
}

export type AnyActionFactoryDefinition = ActionFactoryDefinition<any, any, any>;

export type AFDConfig<T> = T extends ActionFactoryDefinition<infer Config, any, any>
? Config
: never;

export type AFDFactoryContext<T> = T extends ActionFactoryDefinition<any, infer FactoryContext, any>
? FactoryContext
: never;

export type AFDActionContext<T> = T extends ActionFactoryDefinition<any, any, infer ActionContext>
? ActionContext
: never;
71 changes: 71 additions & 0 deletions src/plugins/ui_actions/public/actions/action_internal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,75 @@ describe('ActionInternal', () => {
const action = new ActionInternal(defaultActionDef);
expect(action.id).toBe('test-action');
});

describe('serialize()', () => {
test('can serialize very simple action', () => {
const action = new ActionInternal(defaultActionDef);

action.config = {};

const serialized = action.serialize();

expect(serialized).toMatchObject({
id: 'test-action',
name: '',
config: expect.any(Object),
});
});

test('can serialize action with modified state', () => {
const action = new ActionInternal({
...defaultActionDef,
type: 'ACTION_TYPE' as any,
order: 11,
});

action.name = 'qux';
action.config = { foo: 'bar' };

const serialized = action.serialize();

expect(serialized).toMatchObject({
id: 'test-action',
factoryId: 'ACTION_TYPE',
name: 'qux',
config: {
foo: 'bar',
},
});
});
});

describe('deserialize', () => {
const serialized = {
id: 'id',
factoryId: 'type',
name: 'name',
config: {
foo: 'foo',
},
};

test('can deserialize action state', () => {
const action = new ActionInternal({
...defaultActionDef,
});

action.deserialize(serialized);

expect(action.name).toBe('name');
expect(action.config).toMatchObject(serialized.config);
});

test('does not overwrite action id and type', () => {
const action = new ActionInternal({
...defaultActionDef,
});

action.deserialize(serialized);

expect(action.id).toBe('test-action');
expect(action.type).toBe('');
});
});
});
Loading