From e754fb7691fdfd2d7cf92ebf69310184eb730bff Mon Sep 17 00:00:00 2001
From: Tim Roes
Date: Tue, 6 Oct 2020 14:43:19 +0200
Subject: [PATCH] Improve visualization typings (#79128) (#79636)
* Improve visualization typings
* Fix vis selection dialog
* Fix broken getInfoMessage type
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../public/components/tile_map_options.tsx | 2 +-
src/plugins/tile_map/public/tile_map_type.js | 1 -
.../public/components/sidebar/sidebar.tsx | 4 +-
src/plugins/vis_type_metric/public/to_ast.ts | 2 +-
.../public/table_vis_controller.test.ts | 4 +-
.../vis_type_table/public/table_vis_type.ts | 4 +-
.../vis_type_table/public/vis_controller.ts | 4 +-
.../vis_type_tagcloud/public/to_ast.ts | 2 +-
src/plugins/vis_type_vega/public/vega_type.ts | 5 +-
.../public/components/visualization_chart.tsx | 6 +
.../visualizations/public/expressions/vis.ts | 6 +-
.../public/legacy/build_pipeline.ts | 12 +-
src/plugins/visualizations/public/vis.test.ts | 2 +-
src/plugins/visualizations/public/vis.ts | 14 +-
.../public/vis_types/base_vis_type.ts | 159 ++++---
.../visualizations/public/vis_types/index.ts | 1 +
.../public/vis_types/react_vis_type.ts | 12 +-
.../visualizations/public/vis_types/types.ts | 80 ++++
.../public/vis_types/types_service.ts | 61 +--
.../__snapshots__/new_vis_modal.test.tsx.snap | 404 +++++++++++++++++-
.../public/wizard/new_vis_modal.test.tsx | 41 +-
.../type_selection/new_vis_help.test.tsx | 1 -
.../wizard/type_selection/new_vis_help.tsx | 3 +-
.../wizard/type_selection/type_selection.tsx | 60 +--
.../components/visualize_editor_common.tsx | 2 +-
25 files changed, 696 insertions(+), 196 deletions(-)
create mode 100644 src/plugins/visualizations/public/vis_types/types.ts
diff --git a/src/plugins/tile_map/public/components/tile_map_options.tsx b/src/plugins/tile_map/public/components/tile_map_options.tsx
index f7fb4daff63f01..1a7b11ccf6e208 100644
--- a/src/plugins/tile_map/public/components/tile_map_options.tsx
+++ b/src/plugins/tile_map/public/components/tile_map_options.tsx
@@ -76,7 +76,7 @@ function TileMapOptions(props: TileMapOptionsProps) {
s.name);
const metricAggs = useMemo(
- () => responseAggs.filter((agg) => metricSchemas.includes(get(agg, 'schema'))),
+ () => responseAggs.filter((agg) => agg.schema && metricSchemas.includes(agg.schema)),
[responseAggs, metricSchemas]
);
const hasHistogramAgg = useMemo(() => responseAggs.some((agg) => agg.type.name === 'histogram'), [
diff --git a/src/plugins/vis_type_metric/public/to_ast.ts b/src/plugins/vis_type_metric/public/to_ast.ts
index 7eefd8328ab767..23e4664b82414f 100644
--- a/src/plugins/vis_type_metric/public/to_ast.ts
+++ b/src/plugins/vis_type_metric/public/to_ast.ts
@@ -39,7 +39,7 @@ export const toExpressionAst = (vis: Vis, params: any) => {
const esaggs = buildExpressionFunction('esaggs', {
index: vis.data.indexPattern!.id!,
metricsAtAllLevels: vis.isHierarchical(),
- partialRows: vis.type.requiresPartialRows || vis.params.showPartialRows || false,
+ partialRows: vis.params.showPartialRows || false,
aggConfigs: JSON.stringify(vis.data.aggs!.aggs),
includeFormatHints: false,
});
diff --git a/src/plugins/vis_type_table/public/table_vis_controller.test.ts b/src/plugins/vis_type_table/public/table_vis_controller.test.ts
index 2b4017ae0ee817..035ca044137e90 100644
--- a/src/plugins/vis_type_table/public/table_vis_controller.test.ts
+++ b/src/plugins/vis_type_table/public/table_vis_controller.test.ts
@@ -249,13 +249,13 @@ describe('Table Vis - Controller', () => {
const vis = getRangeVis({ showPartialRows: true });
initController(vis);
- expect(vis.type.hierarchicalData(vis)).toEqual(true);
+ expect((vis.type.hierarchicalData as Function)(vis)).toEqual(true);
});
test('passes partialRows:false to tabify based on the vis params', () => {
const vis = getRangeVis({ showPartialRows: false });
initController(vis);
- expect(vis.type.hierarchicalData(vis)).toEqual(false);
+ expect((vis.type.hierarchicalData as Function)(vis)).toEqual(false);
});
});
diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts
index c1419a48474580..95f4f06ee6111c 100644
--- a/src/plugins/vis_type_table/public/table_vis_type.ts
+++ b/src/plugins/vis_type_table/public/table_vis_type.ts
@@ -20,7 +20,7 @@ import { CoreSetup, PluginInitializerContext } from 'kibana/public';
import { i18n } from '@kbn/i18n';
import { AggGroupNames } from '../../data/public';
import { Schemas } from '../../vis_default_editor/public';
-import { BaseVisTypeOptions, Vis } from '../../visualizations/public';
+import { BaseVisTypeOptions } from '../../visualizations/public';
import { tableVisResponseHandler } from './table_vis_response_handler';
// @ts-ignore
import tableVisTemplate from './table_vis.html';
@@ -99,7 +99,7 @@ export function getTableVisTypeDefinition(
]),
},
responseHandler: tableVisResponseHandler,
- hierarchicalData: (vis: Vis) => {
+ hierarchicalData: (vis) => {
return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels);
},
};
diff --git a/src/plugins/vis_type_table/public/vis_controller.ts b/src/plugins/vis_type_table/public/vis_controller.ts
index 5e82796e663393..1781808660260e 100644
--- a/src/plugins/vis_type_table/public/vis_controller.ts
+++ b/src/plugins/vis_type_table/public/vis_controller.ts
@@ -103,7 +103,9 @@ export function getTableVisualizationControllerClass(
this.$scope = this.$rootScope.$new();
this.$scope.uiState = this.vis.getUiState();
updateScope();
- this.el.find('div').append(this.$compile(this.vis.type!.visConfig.template)(this.$scope));
+ this.el
+ .find('div')
+ .append(this.$compile(this.vis.type.visConfig?.template ?? '')(this.$scope));
this.$scope.$apply();
} else {
updateScope();
diff --git a/src/plugins/vis_type_tagcloud/public/to_ast.ts b/src/plugins/vis_type_tagcloud/public/to_ast.ts
index a284bba3073487..876784cc101405 100644
--- a/src/plugins/vis_type_tagcloud/public/to_ast.ts
+++ b/src/plugins/vis_type_tagcloud/public/to_ast.ts
@@ -38,7 +38,7 @@ export const toExpressionAst = (vis: Vis, params: BuildPipeli
const esaggs = buildExpressionFunction('esaggs', {
index: vis.data.indexPattern!.id!,
metricsAtAllLevels: vis.isHierarchical(),
- partialRows: vis.type.requiresPartialRows || false,
+ partialRows: false,
aggConfigs: JSON.stringify(vis.data.aggs!.aggs),
includeFormatHints: false,
});
diff --git a/src/plugins/vis_type_vega/public/vega_type.ts b/src/plugins/vis_type_vega/public/vega_type.ts
index a9651c1f5eb33c..0496f765e5e99b 100644
--- a/src/plugins/vis_type_vega/public/vega_type.ts
+++ b/src/plugins/vis_type_vega/public/vega_type.ts
@@ -18,6 +18,7 @@
*/
import { i18n } from '@kbn/i18n';
+import { BaseVisTypeOptions } from 'src/plugins/visualizations/public';
import { DefaultEditorSize } from '../../vis_default_editor/public';
import { VegaVisualizationDependencies } from './plugin';
import { VegaVisEditor } from './components';
@@ -31,7 +32,9 @@ import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
import { getInfoMessage } from './components/experimental_map_vis_info';
-export const createVegaTypeDefinition = (dependencies: VegaVisualizationDependencies) => {
+export const createVegaTypeDefinition = (
+ dependencies: VegaVisualizationDependencies
+): BaseVisTypeOptions => {
const requestHandler = createVegaRequestHandler(dependencies);
const visualization = createVegaVisualization(dependencies);
diff --git a/src/plugins/visualizations/public/components/visualization_chart.tsx b/src/plugins/visualizations/public/components/visualization_chart.tsx
index fba0afa89ca539..b8a0412db0a4d9 100644
--- a/src/plugins/visualizations/public/components/visualization_chart.tsx
+++ b/src/plugins/visualizations/public/components/visualization_chart.tsx
@@ -90,6 +90,12 @@ class VisualizationChart extends React.Component {
const { vis } = this.props;
const Visualization = vis.type.visualization;
+ if (!Visualization) {
+ throw new Error(
+ 'Tried to use VisualizationChart component with a vis without visualization property.'
+ );
+ }
+
this.visualization = new Visualization(this.chartDiv.current, vis);
// We know that containerDiv.current will never be null, since we will always
diff --git a/src/plugins/visualizations/public/expressions/vis.ts b/src/plugins/visualizations/public/expressions/vis.ts
index 5a99dceda20bd8..87f77e589c9caa 100644
--- a/src/plugins/visualizations/public/expressions/vis.ts
+++ b/src/plugins/visualizations/public/expressions/vis.ts
@@ -36,7 +36,7 @@ import { VisType } from '../vis_types';
export interface ExprVisState {
title?: string;
- type: VisType | string;
+ type: VisType | string;
params?: VisParams;
}
@@ -52,7 +52,7 @@ export interface ExprVisAPI {
export class ExprVis extends EventEmitter {
public title: string = '';
- public type: VisType;
+ public type: VisType;
public params: VisParams = {};
public sessionState: Record = {};
public API: ExprVisAPI;
@@ -92,7 +92,7 @@ export class ExprVis extends EventEmitter {
};
}
- private getType(type: string | VisType) {
+ private getType(type: string | VisType) {
if (_.isString(type)) {
const newType = getTypes().get(type);
if (!newType) {
diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts
index 79e1c1cca2155a..9f6a4d55532928 100644
--- a/src/plugins/visualizations/public/legacy/build_pipeline.ts
+++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts
@@ -86,7 +86,10 @@ const vislibCharts: string[] = [
'line',
];
-export const getSchemas = (vis: Vis, { timeRange, timefilter }: BuildPipelineParams): Schemas => {
+export const getSchemas = (
+ vis: Vis,
+ { timeRange, timefilter }: BuildPipelineParams
+): Schemas => {
const createSchemaConfig = (accessor: number, agg: IAggConfig): SchemaConfig => {
if (isDateHistogramBucketAggConfig(agg)) {
agg.params.timeRange = timeRange;
@@ -155,7 +158,8 @@ export const getSchemas = (vis: Vis, { timeRange, timefilter }: BuildPipelinePar
}
}
if (schemaName === 'split') {
- schemaName = `split_${vis.params.row ? 'row' : 'column'}`;
+ // TODO: We should check if there's a better way then casting to `any` here
+ schemaName = `split_${(vis.params as any).row ? 'row' : 'column'}`;
skipMetrics = responseAggs.length - metrics.length > 1;
}
if (!schemas[schemaName]) {
@@ -410,7 +414,7 @@ export const buildPipeline = async (vis: Vis, params: BuildPipelineParams) => {
pipeline += `esaggs
${prepareString('index', indexPattern!.id)}
metricsAtAllLevels=${vis.isHierarchical()}
- partialRows=${vis.type.requiresPartialRows || vis.params.showPartialRows || false}
+ partialRows=${vis.params.showPartialRows || false}
${prepareJson('aggConfigs', vis.data.aggs!.aggs)} | `;
}
@@ -433,7 +437,7 @@ export const buildPipeline = async (vis: Vis, params: BuildPipelineParams) => {
pipeline += `visualization type='${vis.type.name}'
${prepareJson('visConfig', visConfig)}
metricsAtAllLevels=${vis.isHierarchical()}
- partialRows=${vis.type.requiresPartialRows || vis.params.showPartialRows || false} `;
+ partialRows=${vis.params.showPartialRows || false} `;
if (indexPattern) {
pipeline += `${prepareString('index', indexPattern.id)} `;
if (vis.data.aggs) {
diff --git a/src/plugins/visualizations/public/vis.test.ts b/src/plugins/visualizations/public/vis.test.ts
index c271888b7c7a4b..e1b188f2e460bd 100644
--- a/src/plugins/visualizations/public/vis.test.ts
+++ b/src/plugins/visualizations/public/vis.test.ts
@@ -121,7 +121,7 @@ describe('Vis Class', function () {
});
it('should return true for hierarchical vis (like pie)', function () {
- vis.type.hierarchicalData = true;
+ (vis.type as any).hierarchicalData = true;
expect(vis.isHierarchical()).toBe(true);
});
});
diff --git a/src/plugins/visualizations/public/vis.ts b/src/plugins/visualizations/public/vis.ts
index c6773e5a1bee3a..5c3233a8de896d 100644
--- a/src/plugins/visualizations/public/vis.ts
+++ b/src/plugins/visualizations/public/vis.ts
@@ -84,7 +84,7 @@ const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?:
type PartialVisState = Assign }>;
export class Vis {
- public readonly type: VisType;
+ public readonly type: VisType;
public readonly id?: string;
public title: string = '';
public description: string = '';
@@ -97,14 +97,14 @@ export class Vis {
public readonly uiState: PersistedState;
constructor(visType: string, visState: SerializedVis = {} as any) {
- this.type = this.getType(visType);
+ this.type = this.getType(visType);
this.params = this.getParams(visState.params);
this.uiState = new PersistedState(visState.uiState);
this.id = visState.id;
}
- private getType(visType: string) {
- const type = getTypes().get(visType);
+ private getType(visType: string) {
+ const type = getTypes().get(visType);
if (!type) {
const errorMessage = i18n.translate('visualizations.visualizationTypeInvalidMessage', {
defaultMessage: 'Invalid visualization type "{visType}"',
@@ -118,7 +118,7 @@ export class Vis {
}
private getParams(params: VisParams) {
- return defaults({}, cloneDeep(params || {}), cloneDeep(this.type.visConfig.defaults || {}));
+ return defaults({}, cloneDeep(params ?? {}), cloneDeep(this.type.visConfig?.defaults ?? {}));
}
async setState(state: PartialVisState) {
@@ -202,10 +202,6 @@ export class Vis {
};
}
- toExpressionAst() {
- return this.type.toExpressionAst(this.params);
- }
-
// deprecated
isHierarchical() {
if (isFunction(this.type.hierarchicalData)) {
diff --git a/src/plugins/visualizations/public/vis_types/base_vis_type.ts b/src/plugins/visualizations/public/vis_types/base_vis_type.ts
index 4763bc9de9d273..f2933de723a393 100644
--- a/src/plugins/visualizations/public/vis_types/base_vis_type.ts
+++ b/src/plugins/visualizations/public/vis_types/base_vis_type.ts
@@ -17,118 +17,113 @@
* under the License.
*/
-import _ from 'lodash';
-import { ReactElement } from 'react';
-import { VisParams, VisToExpressionAst, VisualizationControllerConstructor } from '../types';
-import { TriggerContextMapping } from '../../../ui_actions/public';
-import { Adapters } from '../../../inspector/public';
-import { Vis } from '../vis';
+import { defaultsDeep } from 'lodash';
+import { ISchemas } from 'src/plugins/vis_default_editor/public';
+import { VisParams } from '../types';
+import { VisType, VisTypeOptions } from './types';
-interface CommonBaseVisTypeOptions {
- name: string;
- title: string;
- description?: string;
- getSupportedTriggers?: () => Array;
- icon?: string;
- image?: string;
- stage?: 'experimental' | 'beta' | 'production';
- options?: Record;
- visConfig?: Record;
- editor?: any;
- editorConfig?: Record;
- hidden?: boolean;
- requestHandler?: string | unknown;
- responseHandler?: string | unknown;
- hierarchicalData?: boolean | unknown;
- setup?: unknown;
- useCustomNoDataScreen?: boolean;
- inspectorAdapters?: Adapters | (() => Adapters);
- isDeprecated?: boolean;
- getInfoMessage?: (vis: Vis) => ReactElement<{}> | null;
+interface CommonBaseVisTypeOptions
+ extends Pick<
+ VisType,
+ | 'description'
+ | 'editor'
+ | 'getInfoMessage'
+ | 'getSupportedTriggers'
+ | 'hierarchicalData'
+ | 'icon'
+ | 'image'
+ | 'inspectorAdapters'
+ | 'name'
+ | 'requestHandler'
+ | 'responseHandler'
+ | 'setup'
+ | 'title'
+ >,
+ Pick<
+ Partial>,
+ 'editorConfig' | 'hidden' | 'stage' | 'useCustomNoDataScreen' | 'visConfig'
+ > {
+ options?: Partial['options']>;
}
-interface ExpressionBaseVisTypeOptions extends CommonBaseVisTypeOptions {
- toExpressionAst: VisToExpressionAst;
+interface ExpressionBaseVisTypeOptions extends CommonBaseVisTypeOptions {
+ toExpressionAst: VisType['toExpressionAst'];
visualization?: undefined;
}
-interface VisualizationBaseVisTypeOptions extends CommonBaseVisTypeOptions {
+interface VisualizationBaseVisTypeOptions extends CommonBaseVisTypeOptions {
toExpressionAst?: undefined;
- visualization: VisualizationControllerConstructor | undefined;
+ visualization: VisType['visualization'];
}
export type BaseVisTypeOptions =
| ExpressionBaseVisTypeOptions
- | VisualizationBaseVisTypeOptions;
+ | VisualizationBaseVisTypeOptions;
-export class BaseVisType {
- name: string;
- title: string;
- description: string;
- getSupportedTriggers?: () => Array;
- icon?: string;
- image?: string;
- stage: 'experimental' | 'beta' | 'production';
- isExperimental: boolean;
- options: Record;
- visualization: VisualizationControllerConstructor | undefined;
- visConfig: Record;
- editor: any;
- editorConfig: Record;
- hidden: boolean;
- requiresSearch: boolean;
- requestHandler: string | unknown;
- responseHandler: string | unknown;
- hierarchicalData: boolean | unknown;
- setup?: unknown;
- useCustomNoDataScreen: boolean;
- inspectorAdapters?: Adapters | (() => Adapters);
- toExpressionAst?: VisToExpressionAst;
- getInfoMessage?: (vis: Vis) => ReactElement<{}> | null;
+const defaultOptions: VisTypeOptions = {
+ showTimePicker: true,
+ showQueryBar: true,
+ showFilterBar: true,
+ showIndexSelection: true,
+ hierarchicalData: false, // we should get rid of this i guess ?
+};
+
+export class BaseVisType implements VisType {
+ public readonly name;
+ public readonly title;
+ public readonly description;
+ public readonly getSupportedTriggers;
+ public readonly icon;
+ public readonly image;
+ public readonly stage;
+ public readonly options;
+ public readonly visualization;
+ public readonly visConfig;
+ public readonly editor;
+ public readonly editorConfig;
+ public hidden;
+ public readonly requestHandler;
+ public readonly responseHandler;
+ public readonly hierarchicalData;
+ public readonly setup;
+ public readonly useCustomNoDataScreen;
+ public readonly inspectorAdapters;
+ public readonly toExpressionAst;
+ public readonly getInfoMessage;
constructor(opts: BaseVisTypeOptions) {
if (!opts.icon && !opts.image) {
throw new Error('vis_type must define its icon or image');
}
- const defaultOptions = {
- // controls the visualize editor
- showTimePicker: true,
- showQueryBar: true,
- showFilterBar: true,
- showIndexSelection: true,
- hierarchicalData: false, // we should get rid of this i guess ?
- };
-
this.name = opts.name;
- this.description = opts.description || '';
+ this.description = opts.description ?? '';
this.getSupportedTriggers = opts.getSupportedTriggers;
this.title = opts.title;
this.icon = opts.icon;
this.image = opts.image;
this.visualization = opts.visualization;
- this.visConfig = _.defaultsDeep({}, opts.visConfig, { defaults: {} });
+ this.visConfig = defaultsDeep({}, opts.visConfig, { defaults: {} });
this.editor = opts.editor;
- this.editorConfig = _.defaultsDeep({}, opts.editorConfig, { collections: {} });
- this.options = _.defaultsDeep({}, opts.options, defaultOptions);
- this.stage = opts.stage || 'production';
- this.isExperimental = opts.stage === 'experimental';
- this.hidden = opts.hidden || false;
- this.requestHandler = opts.requestHandler || 'courier';
- this.responseHandler = opts.responseHandler || 'none';
+ this.editorConfig = defaultsDeep({}, opts.editorConfig, { collections: {} });
+ this.options = defaultsDeep({}, opts.options, defaultOptions);
+ this.stage = opts.stage ?? 'production';
+ this.hidden = opts.hidden ?? false;
+ this.requestHandler = opts.requestHandler ?? 'courier';
+ this.responseHandler = opts.responseHandler ?? 'none';
this.setup = opts.setup;
- this.requiresSearch = this.requestHandler !== 'none';
- this.hierarchicalData = opts.hierarchicalData || false;
- this.useCustomNoDataScreen = opts.useCustomNoDataScreen || false;
+ this.hierarchicalData = opts.hierarchicalData ?? false;
+ this.useCustomNoDataScreen = opts.useCustomNoDataScreen ?? false;
this.inspectorAdapters = opts.inspectorAdapters;
this.toExpressionAst = opts.toExpressionAst;
this.getInfoMessage = opts.getInfoMessage;
}
- public get schemas() {
- if (this.editorConfig && this.editorConfig.schemas) {
- return this.editorConfig.schemas;
- }
- return [];
+ public get schemas(): ISchemas {
+ return this.editorConfig?.schemas ?? [];
+ }
+
+ public get requiresSearch(): boolean {
+ return this.requestHandler !== 'none';
}
}
diff --git a/src/plugins/visualizations/public/vis_types/index.ts b/src/plugins/visualizations/public/vis_types/index.ts
index 8f38e335691620..22561decabea48 100644
--- a/src/plugins/visualizations/public/vis_types/index.ts
+++ b/src/plugins/visualizations/public/vis_types/index.ts
@@ -18,5 +18,6 @@
*/
export * from './types_service';
+export { VisType } from './types';
export type { BaseVisTypeOptions } from './base_vis_type';
export type { ReactVisTypeOptions } from './react_vis_type';
diff --git a/src/plugins/visualizations/public/vis_types/react_vis_type.ts b/src/plugins/visualizations/public/vis_types/react_vis_type.ts
index 047d36d8041116..f6bd51df266955 100644
--- a/src/plugins/visualizations/public/vis_types/react_vis_type.ts
+++ b/src/plugins/visualizations/public/vis_types/react_vis_type.ts
@@ -19,15 +19,21 @@
import { BaseVisType, BaseVisTypeOptions } from './base_vis_type';
import { ReactVisController } from './react_vis_controller';
+import { VisType } from './types';
-export type ReactVisTypeOptions = Omit;
+export type ReactVisTypeOptions = Omit<
+ BaseVisTypeOptions,
+ 'visualization' | 'toExpressionAst'
+>;
/**
* This class should only be used for visualizations not using the `toExpressionAst` with a custom renderer.
* If you implement a custom renderer you should just mount a react component inside this.
*/
-export class ReactVisType extends BaseVisType {
- constructor(opts: ReactVisTypeOptions) {
+export class ReactVisType
+ extends BaseVisType
+ implements VisType {
+ constructor(opts: ReactVisTypeOptions) {
super({
...opts,
visualization: ReactVisController,
diff --git a/src/plugins/visualizations/public/vis_types/types.ts b/src/plugins/visualizations/public/vis_types/types.ts
new file mode 100644
index 00000000000000..0cf345bf07be66
--- /dev/null
+++ b/src/plugins/visualizations/public/vis_types/types.ts
@@ -0,0 +1,80 @@
+/*
+ * 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 { IconType } from '@elastic/eui';
+import React from 'react';
+import { Adapters } from 'src/plugins/inspector';
+import { ISchemas } from 'src/plugins/vis_default_editor/public';
+import { TriggerContextMapping } from '../../../ui_actions/public';
+import { Vis, VisToExpressionAst, VisualizationControllerConstructor } from '../types';
+
+export interface VisTypeOptions {
+ showTimePicker: boolean;
+ showQueryBar: boolean;
+ showFilterBar: boolean;
+ showIndexSelection: boolean;
+ hierarchicalData: boolean;
+}
+
+/**
+ * A visualization type representing one specific type of "classical"
+ * visualizations (i.e. not Lens visualizations).
+ */
+export interface VisType {
+ readonly name: string;
+ readonly title: string;
+ readonly description?: string;
+ readonly getSupportedTriggers?: () => Array;
+ readonly isAccessible?: boolean;
+ readonly requestHandler?: string | unknown;
+ readonly responseHandler?: string | unknown;
+ readonly icon?: IconType;
+ readonly image?: string;
+ readonly stage: 'experimental' | 'beta' | 'production';
+ readonly requiresSearch: boolean;
+ readonly useCustomNoDataScreen: boolean;
+ readonly hierarchicalData?: boolean | ((vis: { params: TVisParams }) => boolean);
+ readonly inspectorAdapters?: Adapters | (() => Adapters);
+ /**
+ * When specified this visualization is deprecated. This function
+ * should return a ReactElement that will render a deprecation warning.
+ * It will be shown in the editor when editing/creating visualizations
+ * of this type.
+ */
+ readonly getInfoMessage?: (vis: Vis) => React.ReactNode;
+
+ readonly toExpressionAst?: VisToExpressionAst;
+ readonly visualization?: VisualizationControllerConstructor;
+
+ readonly setup?: (vis: Vis) => Promise>;
+ hidden: boolean;
+
+ readonly schemas: ISchemas;
+
+ readonly options: VisTypeOptions;
+
+ // TODO: The following types still need to be refined properly.
+
+ /**
+ * The editor that should be used to edit visualizations of this type.
+ */
+ readonly editor?: any;
+ readonly editorConfig: Record;
+ readonly visConfig: Record;
+}
diff --git a/src/plugins/visualizations/public/vis_types/types_service.ts b/src/plugins/visualizations/public/vis_types/types_service.ts
index 1afbd6901a1959..5d619064c240e9 100644
--- a/src/plugins/visualizations/public/vis_types/types_service.ts
+++ b/src/plugins/visualizations/public/vis_types/types_service.ts
@@ -17,33 +17,10 @@
* under the License.
*/
-import { IconType } from '@elastic/eui';
import { visTypeAliasRegistry, VisTypeAlias } from './vis_type_alias_registry';
import { BaseVisType, BaseVisTypeOptions } from './base_vis_type';
import { ReactVisType, ReactVisTypeOptions } from './react_vis_type';
-import { TriggerContextMapping } from '../../../ui_actions/public';
-
-export interface VisType {
- name: string;
- title: string;
- description?: string;
- getSupportedTriggers?: () => Array;
- visualization: any;
- isAccessible?: boolean;
- requestHandler: string | unknown;
- responseHandler: string | unknown;
- icon?: IconType;
- image?: string;
- stage: 'experimental' | 'beta' | 'production';
- requiresSearch: boolean;
- hidden: boolean;
-
- // Since we haven't typed everything here yet, we basically "any" the rest
- // of that interface. This should be removed as soon as this type definition
- // has been completed. But that way we at least have typing for a couple of
- // properties on that type.
- [key: string]: any;
-}
+import { VisType } from './types';
/**
* Vis Types Service
@@ -51,21 +28,21 @@ export interface VisType {
* @internal
*/
export class TypesService {
- private types: Record = {};
+ private types: Record> = {};
private unregisteredHiddenTypes: string[] = [];
- public setup() {
- const registerVisualization = (registerFn: () => VisType) => {
- const visDefinition = registerFn();
- if (this.unregisteredHiddenTypes.includes(visDefinition.name)) {
- visDefinition.hidden = true;
- }
+ private registerVisualization(visDefinition: VisType) {
+ if (this.unregisteredHiddenTypes.includes(visDefinition.name)) {
+ visDefinition.hidden = true;
+ }
- if (this.types[visDefinition.name]) {
- throw new Error('type already exists!');
- }
- this.types[visDefinition.name] = visDefinition;
- };
+ if (this.types[visDefinition.name]) {
+ throw new Error('type already exists!');
+ }
+ this.types[visDefinition.name] = visDefinition;
+ }
+
+ public setup() {
return {
/**
* registers a visualization type
@@ -73,15 +50,15 @@ export class TypesService {
*/
createBaseVisualization: (config: BaseVisTypeOptions): void => {
const vis = new BaseVisType(config);
- registerVisualization(() => vis);
+ this.registerVisualization(vis);
},
/**
* registers a visualization which uses react for rendering
* @param config - visualization type definition
*/
- createReactVisualization: (config: ReactVisTypeOptions): void => {
+ createReactVisualization: (config: ReactVisTypeOptions): void => {
const vis = new ReactVisType(config);
- registerVisualization(() => vis);
+ this.registerVisualization(vis);
},
/**
* registers a visualization alias
@@ -93,7 +70,7 @@ export class TypesService {
* allows to hide specific visualization types from create visualization dialog
* @param {string[]} typeNames - list of type ids to hide
*/
- hideTypes: (typeNames: string[]) => {
+ hideTypes: (typeNames: string[]): void => {
typeNames.forEach((name: string) => {
if (this.types[name]) {
this.types[name].hidden = true;
@@ -111,13 +88,13 @@ export class TypesService {
* returns specific visualization or undefined if not found
* @param {string} visualization - id of visualization to return
*/
- get: (visualization: string) => {
+ get: (visualization: string): VisType => {
return this.types[visualization];
},
/**
* returns all registered visualization types
*/
- all: () => {
+ all: (): VisType[] => {
return [...Object.values(this.types)];
},
/**
diff --git a/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap b/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap
index c2a2b27457f8d1..2d55059efb5bbd 100644
--- a/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap
+++ b/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap
@@ -238,12 +238,43 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
aria-live="polite"
class="euiScreenReaderOnly"
>
- 2 types found
+ 3 types found
+
+
+ promotion description
+
+
+
@@ -605,11 +659,11 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
id="visualizations.newVisWizard.resultsFound"
values={
Object {
- "resultCount": 2,
+ "resultCount": 3,
}
}
>
- 2 types found
+ 3 types found
@@ -621,6 +675,75 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
className="euiKeyPadMenu visNewVisDialog__types"
data-test-subj="visNewDialogTypes"
>
+
+
+ Vis alias with promotion
+
+ }
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ >
+
+
+
@@ -867,7 +990,21 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
+
+
+ promotion description
+
+
+
+
+
+
+
@@ -1129,6 +1326,37 @@ exports[`NewVisModal should render as expected 1`] = `
class="euiKeyPadMenu visNewVisDialog__types"
data-test-subj="visNewDialogTypes"
>
+
+
+