Vega visualization renderer (#81606)

* Create vega to_ast function

* Create a custom vega renderer

* Fix sass error

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Daniil Suleiman 2020-10-29 14:51:09 +03:00 committed by GitHub
parent db92edff1f
commit 28a726fe7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 354 additions and 106 deletions

View file

@ -1,3 +1,11 @@
.vgaVis__wrapper {
@include euiScrollBar;
display: flex;
flex: 1 1 0;
overflow: auto;
}
.vgaVis { .vgaVis {
display: flex; display: flex;
flex: 1 1 100%; flex: 1 1 100%;

View file

@ -0,0 +1,85 @@
/*
* 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 React, { useEffect, useMemo, useRef } from 'react';
import { EuiResizeObserver } from '@elastic/eui';
import { throttle } from 'lodash';
import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { createVegaVisualization } from '../vega_visualization';
import { VegaVisualizationDependencies } from '../plugin';
import { VegaParser } from '../data_model/vega_parser';
import './vega_vis.scss';
interface VegaVisComponentProps {
deps: VegaVisualizationDependencies;
fireEvent: IInterpreterRenderHandlers['event'];
renderComplete: () => void;
visData: VegaParser;
}
type VegaVisController = InstanceType<ReturnType<typeof createVegaVisualization>>;
const VegaVisComponent = ({ visData, fireEvent, renderComplete, deps }: VegaVisComponentProps) => {
const chartDiv = useRef<HTMLDivElement>(null);
const visController = useRef<VegaVisController | null>(null);
useEffect(() => {
if (chartDiv.current) {
const VegaVis = createVegaVisualization(deps);
visController.current = new VegaVis(chartDiv.current, fireEvent);
}
return () => {
visController.current?.destroy();
visController.current = null;
};
}, [deps, fireEvent]);
useEffect(() => {
if (visController.current) {
visController.current.render(visData).then(renderComplete);
}
}, [visData, renderComplete]);
const updateChartSize = useMemo(
() =>
throttle(() => {
if (visController.current) {
visController.current.render(visData).then(renderComplete);
}
}, 300),
[renderComplete, visData]
);
return (
<EuiResizeObserver onResize={updateChartSize}>
{(resizeRef) => (
<div className="vgaVis__wrapper" ref={resizeRef}>
<div ref={chartDiv} />
</div>
)}
</EuiResizeObserver>
);
};
// default export required for React.Lazy
// eslint-disable-next-line import/no-default-export
export { VegaVisComponent as default };

View file

@ -30,6 +30,8 @@ import { VisParams } from '../vega_fn';
import { VegaHelpMenu } from './vega_help_menu'; import { VegaHelpMenu } from './vega_help_menu';
import { VegaActionsMenu } from './vega_actions_menu'; import { VegaActionsMenu } from './vega_actions_menu';
import './vega_editor.scss';
const aceOptions = { const aceOptions = {
maxLines: Infinity, maxLines: Infinity,
highlightActiveLine: false, highlightActiveLine: false,
@ -102,4 +104,6 @@ function VegaVisEditor({ stateParams, setValue }: VisOptionsProps<VisParams>) {
); );
} }
export { VegaVisEditor }; // default export required for React.Lazy
// eslint-disable-next-line import/no-default-export
export { VegaVisEditor as default };

View file

@ -0,0 +1,29 @@
/*
* 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 React, { lazy } from 'react';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
import { VisParams } from '../vega_fn';
const VegaVisEditor = lazy(() => import('./vega_vis_editor'));
export const VegaVisEditorComponent = (props: VisOptionsProps<VisParams>) => (
<VegaVisEditor {...props} />
);

View file

@ -1,9 +0,0 @@
// Prefix all styles with "vga" to avoid conflicts.
// Examples
// vgaChart
// vgaChart__legend
// vgaChart__legend--small
// vgaChart__legend-isLoading
@import './vega_vis';
@import './vega_editor';

View file

@ -35,10 +35,10 @@ import {
import { createVegaFn } from './vega_fn'; import { createVegaFn } from './vega_fn';
import { createVegaTypeDefinition } from './vega_type'; import { createVegaTypeDefinition } from './vega_type';
import { IServiceSettings } from '../../maps_legacy/public'; import { IServiceSettings } from '../../maps_legacy/public';
import './index.scss';
import { ConfigSchema } from '../config'; import { ConfigSchema } from '../config';
import { getVegaInspectorView } from './vega_inspector'; import { getVegaInspectorView } from './vega_inspector';
import { getVegaVisRenderer } from './vega_vis_renderer';
/** @internal */ /** @internal */
export interface VegaVisualizationDependencies { export interface VegaVisualizationDependencies {
@ -93,6 +93,7 @@ export class VegaPlugin implements Plugin<Promise<void>, void> {
inspector.registerView(getVegaInspectorView({ uiSettings: core.uiSettings })); inspector.registerView(getVegaInspectorView({ uiSettings: core.uiSettings }));
expressions.registerFunction(() => createVegaFn(visualizationDependencies)); expressions.registerFunction(() => createVegaFn(visualizationDependencies));
expressions.registerRenderer(getVegaVisRenderer(visualizationDependencies));
visualizations.createBaseVisualization(createVegaTypeDefinition(visualizationDependencies)); visualizations.createBaseVisualization(createVegaTypeDefinition(visualizationDependencies));
} }

View file

@ -0,0 +1,32 @@
/*
* 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 { buildExpression, buildExpressionFunction } from '../../expressions/public';
import { Vis } from '../../visualizations/public';
import { VegaExpressionFunctionDefinition, VisParams } from './vega_fn';
export const toExpressionAst = (vis: Vis<VisParams>) => {
const vega = buildExpressionFunction<VegaExpressionFunctionDefinition>('vega', {
spec: vis.params.spec,
});
const ast = buildExpression([vega]);
return ast.toAst();
};

View file

@ -40,21 +40,23 @@ interface Arguments {
export type VisParams = Required<Arguments>; export type VisParams = Required<Arguments>;
interface RenderValue { export interface RenderValue {
visData: VegaParser; visData: VegaParser;
visType: 'vega'; visType: 'vega';
visConfig: VisParams; visConfig: VisParams;
} }
export const createVegaFn = ( export type VegaExpressionFunctionDefinition = ExpressionFunctionDefinition<
dependencies: VegaVisualizationDependencies
): ExpressionFunctionDefinition<
'vega', 'vega',
Input, Input,
Arguments, Arguments,
Output, Output,
ExecutionContext<VegaInspectorAdapters> ExecutionContext<VegaInspectorAdapters>
> => ({ >;
export const createVegaFn = (
dependencies: VegaVisualizationDependencies
): VegaExpressionFunctionDefinition => ({
name: 'vega', name: 'vega',
type: 'render', type: 'render',
inputTypes: ['kibana_context', 'null'], inputTypes: ['kibana_context', 'null'],
@ -80,7 +82,7 @@ export const createVegaFn = (
return { return {
type: 'render', type: 'render',
as: 'visualization', as: 'vega_vis',
value: { value: {
visData: response, visData: response,
visType: 'vega', visType: 'vega',

View file

@ -41,7 +41,7 @@ const specLabel = i18n.translate('visTypeVega.inspector.specLabel', {
defaultMessage: 'Spec', defaultMessage: 'Spec',
}); });
export const VegaDataInspector = ({ adapters }: VegaDataInspectorProps) => { const VegaDataInspector = ({ adapters }: VegaDataInspectorProps) => {
const tabs = [ const tabs = [
{ {
id: 'data-viewer--id', id: 'data-viewer--id',
@ -75,3 +75,7 @@ export const VegaDataInspector = ({ adapters }: VegaDataInspectorProps) => {
/> />
); );
}; };
// default export required for React.Lazy
// eslint-disable-next-line import/no-default-export
export { VegaDataInspector as default };

View file

@ -16,14 +16,17 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import React from 'react'; import React, { lazy, Suspense } from 'react';
import { EuiLoadingSpinner } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { IUiSettingsClient } from 'kibana/public'; import { IUiSettingsClient } from 'kibana/public';
import { VegaAdapter } from './vega_adapter';
import { VegaDataInspector, VegaDataInspectorProps } from './vega_data_inspector';
import { KibanaContextProvider } from '../../../kibana_react/public'; import { KibanaContextProvider } from '../../../kibana_react/public';
import { Adapters, RequestAdapter, InspectorViewDescription } from '../../../inspector/public'; import { Adapters, RequestAdapter, InspectorViewDescription } from '../../../inspector/public';
import { VegaAdapter } from './vega_adapter';
import type { VegaDataInspectorProps } from './vega_data_inspector';
const VegaDataInspector = lazy(() => import('./vega_data_inspector'));
export interface VegaInspectorAdapters extends Adapters { export interface VegaInspectorAdapters extends Adapters {
requests: RequestAdapter; requests: RequestAdapter;
@ -46,7 +49,9 @@ export const getVegaInspectorView = (dependencies: VegaInspectorViewDependencies
}, },
component: (props) => ( component: (props) => (
<KibanaContextProvider services={dependencies}> <KibanaContextProvider services={dependencies}>
<VegaDataInspector {...(props as VegaDataInspectorProps)}> </VegaDataInspector> <Suspense fallback={<EuiLoadingSpinner />}>
<VegaDataInspector {...(props as VegaDataInspectorProps)} />
</Suspense>
</KibanaContextProvider> </KibanaContextProvider>
), ),
} as InspectorViewDescription); } as InspectorViewDescription);

View file

@ -21,22 +21,20 @@ import { i18n } from '@kbn/i18n';
import { BaseVisTypeOptions } from 'src/plugins/visualizations/public'; import { BaseVisTypeOptions } from 'src/plugins/visualizations/public';
import { DefaultEditorSize } from '../../vis_default_editor/public'; import { DefaultEditorSize } from '../../vis_default_editor/public';
import { VegaVisualizationDependencies } from './plugin'; import { VegaVisualizationDependencies } from './plugin';
import { VegaVisEditor } from './components';
import { createVegaRequestHandler } from './vega_request_handler'; import { createVegaRequestHandler } from './vega_request_handler';
// @ts-expect-error
import { createVegaVisualization } from './vega_visualization';
import { getDefaultSpec } from './default_spec'; import { getDefaultSpec } from './default_spec';
import { createInspectorAdapters } from './vega_inspector'; import { createInspectorAdapters } from './vega_inspector';
import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
import { toExpressionAst } from './to_ast';
import { VisParams } from './vega_fn';
import { getInfoMessage } from './components/experimental_map_vis_info'; import { getInfoMessage } from './components/experimental_map_vis_info';
import { VegaVisEditorComponent } from './components/vega_vis_editor_lazy';
export const createVegaTypeDefinition = ( export const createVegaTypeDefinition = (
dependencies: VegaVisualizationDependencies dependencies: VegaVisualizationDependencies
): BaseVisTypeOptions => { ): BaseVisTypeOptions<VisParams> => {
const requestHandler = createVegaRequestHandler(dependencies); const requestHandler = createVegaRequestHandler(dependencies);
const visualization = createVegaVisualization(dependencies);
return { return {
name: 'vega', name: 'vega',
@ -49,13 +47,12 @@ export const createVegaTypeDefinition = (
icon: 'visVega', icon: 'visVega',
visConfig: { defaults: { spec: getDefaultSpec() } }, visConfig: { defaults: { spec: getDefaultSpec() } },
editorConfig: { editorConfig: {
optionsTemplate: VegaVisEditor, optionsTemplate: VegaVisEditorComponent,
enableAutoApply: true, enableAutoApply: true,
defaultSize: DefaultEditorSize.MEDIUM, defaultSize: DefaultEditorSize.MEDIUM,
}, },
visualization,
requestHandler, requestHandler,
responseHandler: 'none', toExpressionAst,
options: { options: {
showIndexSelection: false, showIndexSelection: false,
showQueryBar: true, showQueryBar: true,

View file

@ -0,0 +1,40 @@
/*
* 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 { DataPublicPluginStart } from 'src/plugins/data/public';
import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { IServiceSettings } from 'src/plugins/maps_legacy/public';
import { VegaParser } from '../data_model/vega_parser';
interface VegaViewParams {
parentEl: HTMLDivElement;
fireEvent: IInterpreterRenderHandlers['event'];
vegaParser: VegaParser;
serviceSettings: IServiceSettings;
filterManager: DataPublicPluginStart['query']['filterManager'];
timefilter: DataPublicPluginStart['query']['timefilter']['timefilter'];
// findIndex: (index: string) => Promise<...>;
}
export class VegaBaseView {
constructor(params: VegaViewParams);
init(): Promise<void>;
onError(error: any): void;
destroy(): Promise<void>;
}

View file

@ -63,7 +63,7 @@ export class VegaBaseView {
this._parser = opts.vegaParser; this._parser = opts.vegaParser;
this._serviceSettings = opts.serviceSettings; this._serviceSettings = opts.serviceSettings;
this._filterManager = opts.filterManager; this._filterManager = opts.filterManager;
this._applyFilter = opts.applyFilter; this._fireEvent = opts.fireEvent;
this._timefilter = opts.timefilter; this._timefilter = opts.timefilter;
this._findIndex = opts.findIndex; this._findIndex = opts.findIndex;
this._view = null; this._view = null;
@ -264,7 +264,7 @@ export class VegaBaseView {
const indexId = await this._findIndex(index); const indexId = await this._findIndex(index);
const filter = esFilters.buildQueryFilter(query, indexId); const filter = esFilters.buildQueryFilter(query, indexId);
this._applyFilter({ filters: [filter] }); this._fireEvent({ name: 'applyFilter', data: { filters: [filter] } });
} }
/** /**
@ -301,19 +301,22 @@ export class VegaBaseView {
setTimeFilterHandler(start, end) { setTimeFilterHandler(start, end) {
const { from, to, mode } = VegaBaseView._parseTimeRange(start, end); const { from, to, mode } = VegaBaseView._parseTimeRange(start, end);
this._applyFilter({ this._fireEvent({
timeFieldName: '*', name: 'applyFilter',
filters: [ data: {
{ timeFieldName: '*',
range: { filters: [
'*': { {
mode, range: {
gte: from, '*': {
lte: to, mode,
gte: from,
lte: to,
},
}, },
}, },
}, ],
], },
}); });
} }

View file

@ -0,0 +1,22 @@
/*
* 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 { VegaBaseView } from './vega_base_view';
export class VegaMapView extends VegaBaseView {}

View file

@ -17,4 +17,6 @@
* under the License. * under the License.
*/ */
export { VegaVisEditor } from './vega_vis_editor'; import { VegaBaseView } from './vega_base_view';
export class VegaView extends VegaBaseView {}

View file

@ -0,0 +1,51 @@
/*
* 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 React, { lazy } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { ExpressionRenderDefinition } from 'src/plugins/expressions';
import { VisualizationContainer } from '../../visualizations/public';
import { VegaVisualizationDependencies } from './plugin';
import { RenderValue } from './vega_fn';
const VegaVisComponent = lazy(() => import('./components/vega_vis_component'));
export const getVegaVisRenderer: (
deps: VegaVisualizationDependencies
) => ExpressionRenderDefinition<RenderValue> = (deps) => ({
name: 'vega_vis',
reuseDomNode: true,
render: (domNode, { visData }, handlers) => {
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
render(
<VisualizationContainer handlers={handlers}>
<VegaVisComponent
deps={deps}
fireEvent={handlers.event}
renderComplete={handlers.done}
visData={visData}
/>
</VisualizationContainer>,
domNode
);
},
});

View file

@ -30,8 +30,6 @@ import vegaMapGraph from './test_utils/vega_map_test.json';
import { VegaParser } from './data_model/vega_parser'; import { VegaParser } from './data_model/vega_parser';
import { SearchAPI } from './data_model/search_api'; import { SearchAPI } from './data_model/search_api';
import { createVegaTypeDefinition } from './vega_type';
import { setInjectedVars, setData, setSavedObjects, setNotifications } from './services'; import { setInjectedVars, setData, setSavedObjects, setNotifications } from './services';
import { coreMock } from '../../../core/public/mocks'; import { coreMock } from '../../../core/public/mocks';
import { dataPluginMock } from '../../data/public/mocks'; import { dataPluginMock } from '../../data/public/mocks';
@ -49,9 +47,7 @@ jest.mock('./lib/vega', () => ({
describe('VegaVisualizations', () => { describe('VegaVisualizations', () => {
let domNode; let domNode;
let VegaVisualization; let VegaVisualization;
let vis;
let vegaVisualizationDependencies; let vegaVisualizationDependencies;
let vegaVisType;
let mockWidth; let mockWidth;
let mockedWidthValue; let mockedWidthValue;
@ -91,22 +87,12 @@ describe('VegaVisualizations', () => {
getServiceSettings: mockGetServiceSettings, getServiceSettings: mockGetServiceSettings,
}; };
vegaVisType = createVegaTypeDefinition(vegaVisualizationDependencies);
VegaVisualization = createVegaVisualization(vegaVisualizationDependencies); VegaVisualization = createVegaVisualization(vegaVisualizationDependencies);
}); });
describe('VegaVisualization - basics', () => { describe('VegaVisualization - basics', () => {
beforeEach(async () => { beforeEach(async () => {
setupDOM(); setupDOM();
vis = {
type: vegaVisType,
API: {
events: {
applyFilter: jest.fn(),
},
},
};
}); });
afterEach(() => { afterEach(() => {
@ -117,7 +103,7 @@ describe('VegaVisualizations', () => {
test('should show vegalite graph and update on resize (may fail in dev env)', async () => { test('should show vegalite graph and update on resize (may fail in dev env)', async () => {
let vegaVis; let vegaVis;
try { try {
vegaVis = new VegaVisualization(domNode, vis); vegaVis = new VegaVisualization(domNode, jest.fn());
const vegaParser = new VegaParser( const vegaParser = new VegaParser(
JSON.stringify(vegaliteGraph), JSON.stringify(vegaliteGraph),
@ -137,7 +123,7 @@ describe('VegaVisualizations', () => {
mockedWidthValue = 256; mockedWidthValue = 256;
mockedHeightValue = 256; mockedHeightValue = 256;
await vegaVis._vegaView.resize(); await vegaVis.vegaView.resize();
expect(domNode.innerHTML).toMatchSnapshot(); expect(domNode.innerHTML).toMatchSnapshot();
} finally { } finally {
@ -148,7 +134,7 @@ describe('VegaVisualizations', () => {
test('should show vega graph (may fail in dev env)', async () => { test('should show vega graph (may fail in dev env)', async () => {
let vegaVis; let vegaVis;
try { try {
vegaVis = new VegaVisualization(domNode, vis); vegaVis = new VegaVisualization(domNode, jest.fn());
const vegaParser = new VegaParser( const vegaParser = new VegaParser(
JSON.stringify(vegaGraph), JSON.stringify(vegaGraph),
new SearchAPI({ new SearchAPI({
@ -172,7 +158,7 @@ describe('VegaVisualizations', () => {
test('should show vega blank rectangle on top of a map (vegamap)', async () => { test('should show vega blank rectangle on top of a map (vegamap)', async () => {
let vegaVis; let vegaVis;
try { try {
vegaVis = new VegaVisualization(domNode, vis); vegaVis = new VegaVisualization(domNode, jest.fn());
const vegaParser = new VegaParser( const vegaParser = new VegaParser(
JSON.stringify(vegaMapGraph), JSON.stringify(vegaMapGraph),
new SearchAPI({ new SearchAPI({

View file

@ -17,28 +17,34 @@
* under the License. * under the License.
*/ */
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { VegaParser } from './data_model/vega_parser';
import { VegaVisualizationDependencies } from './plugin';
import { getNotifications, getData, getSavedObjects } from './services'; import { getNotifications, getData, getSavedObjects } from './services';
import type { VegaView } from './vega_view/vega_view';
export const createVegaVisualization = ({ getServiceSettings }) => export const createVegaVisualization = ({ getServiceSettings }: VegaVisualizationDependencies) =>
class VegaVisualization { class VegaVisualization {
constructor(el, vis) { private readonly dataPlugin = getData();
this._el = el; private readonly savedObjectsClient = getSavedObjects();
this._vis = vis; private vegaView: InstanceType<typeof VegaView> | null = null;
this.savedObjectsClient = getSavedObjects(); constructor(
this.dataPlugin = getData(); private el: HTMLDivElement,
} private fireEvent: IInterpreterRenderHandlers['event']
) {}
/** /**
* Find index pattern by its title, of if not given, gets default * Find index pattern by its title, of if not given, gets default
* @param {string} [index] * @param {string} [index]
* @returns {Promise<string>} index id * @returns {Promise<string>} index id
*/ */
async findIndex(index) { async findIndex(index: string) {
const { indexPatterns } = this.dataPlugin; const { indexPatterns } = this.dataPlugin;
let idxObj; let idxObj;
if (index) { if (index) {
// @ts-expect-error
idxObj = indexPatterns.findByTitle(this.savedObjectsClient, index); idxObj = indexPatterns.findByTitle(this.savedObjectsClient, index);
if (!idxObj) { if (!idxObj) {
throw new Error( throw new Error(
@ -61,16 +67,10 @@ export const createVegaVisualization = ({ getServiceSettings }) =>
return idxObj.id; return idxObj.id;
} }
/** async render(visData: VegaParser) {
*
* @param {VegaParser} visData
* @param {*} status
* @returns {Promise<void>}
*/
async render(visData) {
const { toasts } = getNotifications(); const { toasts } = getNotifications();
if (!visData && !this._vegaView) { if (!visData && !this.vegaView) {
toasts.addWarning( toasts.addWarning(
i18n.translate('visTypeVega.visualization.unableToRenderWithoutDataWarningMessage', { i18n.translate('visTypeVega.visualization.unableToRenderWithoutDataWarningMessage', {
defaultMessage: 'Unable to render without data', defaultMessage: 'Unable to render without data',
@ -82,8 +82,8 @@ export const createVegaVisualization = ({ getServiceSettings }) =>
try { try {
await this._render(visData); await this._render(visData);
} catch (error) { } catch (error) {
if (this._vegaView) { if (this.vegaView) {
this._vegaView.onError(error); this.vegaView.onError(error);
} else { } else {
toasts.addError(error, { toasts.addError(error, {
title: i18n.translate('visTypeVega.visualization.renderErrorTitle', { title: i18n.translate('visTypeVega.visualization.renderErrorTitle', {
@ -94,20 +94,20 @@ export const createVegaVisualization = ({ getServiceSettings }) =>
} }
} }
async _render(vegaParser) { async _render(vegaParser: VegaParser) {
if (vegaParser) { if (vegaParser) {
// New data received, rebuild the graph // New data received, rebuild the graph
if (this._vegaView) { if (this.vegaView) {
await this._vegaView.destroy(); await this.vegaView.destroy();
this._vegaView = null; this.vegaView = null;
} }
const serviceSettings = await getServiceSettings(); const serviceSettings = await getServiceSettings();
const { filterManager } = this.dataPlugin.query; const { filterManager } = this.dataPlugin.query;
const { timefilter } = this.dataPlugin.query.timefilter; const { timefilter } = this.dataPlugin.query.timefilter;
const vegaViewParams = { const vegaViewParams = {
parentEl: this._el, parentEl: this.el,
applyFilter: this._vis.API.events.applyFilter, fireEvent: this.fireEvent,
vegaParser, vegaParser,
serviceSettings, serviceSettings,
filterManager, filterManager,
@ -116,18 +116,17 @@ export const createVegaVisualization = ({ getServiceSettings }) =>
}; };
if (vegaParser.useMap) { if (vegaParser.useMap) {
const services = { toastService: getNotifications().toasts };
const { VegaMapView } = await import('./vega_view/vega_map_view'); const { VegaMapView } = await import('./vega_view/vega_map_view');
this._vegaView = new VegaMapView(vegaViewParams, services); this.vegaView = new VegaMapView(vegaViewParams);
} else { } else {
const { VegaView } = await import('./vega_view/vega_view'); const { VegaView: VegaViewClass } = await import('./vega_view/vega_view');
this._vegaView = new VegaView(vegaViewParams); this.vegaView = new VegaViewClass(vegaViewParams);
} }
await this._vegaView.init(); await this.vegaView?.init();
} }
} }
destroy() { destroy() {
return this._vegaView && this._vegaView.destroy(); this.vegaView?.destroy();
} }
}; };

View file

@ -13,5 +13,3 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunct
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles region_map function without buckets 1`] = `"regionmap visConfig='{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}}' "`; exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles region_map function without buckets 1`] = `"regionmap visConfig='{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}}' "`;
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tile_map function 1`] = `"tilemap visConfig='{\\"metric\\":{},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},\\"geohash\\":1,\\"geocentroid\\":3}}' "`; exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tile_map function 1`] = `"tilemap visConfig='{\\"metric\\":{},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},\\"geohash\\":1,\\"geocentroid\\":3}}' "`;
exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles vega function 1`] = `"vega spec='this is a test' "`;

View file

@ -94,14 +94,6 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
uiState = {}; uiState = {};
}); });
it('handles vega function', () => {
const vis = {
params: { spec: 'this is a test' },
};
const actual = buildPipelineVisFunction.vega(vis.params, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
it('handles input_control_vis function', () => { it('handles input_control_vis function', () => {
const params = { const params = {
some: 'nested', some: 'nested',

View file

@ -254,9 +254,6 @@ const adjustVislibDimensionFormmaters = (vis: Vis, dimensions: { y: any[] }): vo
}; };
export const buildPipelineVisFunction: BuildPipelineVisFunction = { export const buildPipelineVisFunction: BuildPipelineVisFunction = {
vega: (params) => {
return `vega ${prepareString('spec', params.spec)}`;
},
input_control_vis: (params) => { input_control_vis: (params) => {
return `input_control_vis ${prepareJson('visConfig', params)}`; return `input_control_vis ${prepareJson('visConfig', params)}`;
}, },