mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
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:
parent
db92edff1f
commit
28a726fe7e
22 changed files with 354 additions and 106 deletions
|
@ -1,3 +1,11 @@
|
|||
.vgaVis__wrapper {
|
||||
@include euiScrollBar;
|
||||
|
||||
display: flex;
|
||||
flex: 1 1 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.vgaVis {
|
||||
display: flex;
|
||||
flex: 1 1 100%;
|
|
@ -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 };
|
|
@ -30,6 +30,8 @@ import { VisParams } from '../vega_fn';
|
|||
import { VegaHelpMenu } from './vega_help_menu';
|
||||
import { VegaActionsMenu } from './vega_actions_menu';
|
||||
|
||||
import './vega_editor.scss';
|
||||
|
||||
const aceOptions = {
|
||||
maxLines: Infinity,
|
||||
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 };
|
||||
|
|
|
@ -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} />
|
||||
);
|
|
@ -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';
|
|
@ -35,10 +35,10 @@ import {
|
|||
import { createVegaFn } from './vega_fn';
|
||||
import { createVegaTypeDefinition } from './vega_type';
|
||||
import { IServiceSettings } from '../../maps_legacy/public';
|
||||
import './index.scss';
|
||||
import { ConfigSchema } from '../config';
|
||||
|
||||
import { getVegaInspectorView } from './vega_inspector';
|
||||
import { getVegaVisRenderer } from './vega_vis_renderer';
|
||||
|
||||
/** @internal */
|
||||
export interface VegaVisualizationDependencies {
|
||||
|
@ -93,6 +93,7 @@ export class VegaPlugin implements Plugin<Promise<void>, void> {
|
|||
inspector.registerView(getVegaInspectorView({ uiSettings: core.uiSettings }));
|
||||
|
||||
expressions.registerFunction(() => createVegaFn(visualizationDependencies));
|
||||
expressions.registerRenderer(getVegaVisRenderer(visualizationDependencies));
|
||||
|
||||
visualizations.createBaseVisualization(createVegaTypeDefinition(visualizationDependencies));
|
||||
}
|
||||
|
|
32
src/plugins/vis_type_vega/public/to_ast.ts
Normal file
32
src/plugins/vis_type_vega/public/to_ast.ts
Normal 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();
|
||||
};
|
|
@ -40,21 +40,23 @@ interface Arguments {
|
|||
|
||||
export type VisParams = Required<Arguments>;
|
||||
|
||||
interface RenderValue {
|
||||
export interface RenderValue {
|
||||
visData: VegaParser;
|
||||
visType: 'vega';
|
||||
visConfig: VisParams;
|
||||
}
|
||||
|
||||
export const createVegaFn = (
|
||||
dependencies: VegaVisualizationDependencies
|
||||
): ExpressionFunctionDefinition<
|
||||
export type VegaExpressionFunctionDefinition = ExpressionFunctionDefinition<
|
||||
'vega',
|
||||
Input,
|
||||
Arguments,
|
||||
Output,
|
||||
ExecutionContext<VegaInspectorAdapters>
|
||||
> => ({
|
||||
>;
|
||||
|
||||
export const createVegaFn = (
|
||||
dependencies: VegaVisualizationDependencies
|
||||
): VegaExpressionFunctionDefinition => ({
|
||||
name: 'vega',
|
||||
type: 'render',
|
||||
inputTypes: ['kibana_context', 'null'],
|
||||
|
@ -80,7 +82,7 @@ export const createVegaFn = (
|
|||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'visualization',
|
||||
as: 'vega_vis',
|
||||
value: {
|
||||
visData: response,
|
||||
visType: 'vega',
|
||||
|
|
|
@ -41,7 +41,7 @@ const specLabel = i18n.translate('visTypeVega.inspector.specLabel', {
|
|||
defaultMessage: 'Spec',
|
||||
});
|
||||
|
||||
export const VegaDataInspector = ({ adapters }: VegaDataInspectorProps) => {
|
||||
const VegaDataInspector = ({ adapters }: VegaDataInspectorProps) => {
|
||||
const tabs = [
|
||||
{
|
||||
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 };
|
||||
|
|
|
@ -16,14 +16,17 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IUiSettingsClient } from 'kibana/public';
|
||||
import { VegaAdapter } from './vega_adapter';
|
||||
import { VegaDataInspector, VegaDataInspectorProps } from './vega_data_inspector';
|
||||
import { KibanaContextProvider } from '../../../kibana_react/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 {
|
||||
requests: RequestAdapter;
|
||||
|
@ -46,7 +49,9 @@ export const getVegaInspectorView = (dependencies: VegaInspectorViewDependencies
|
|||
},
|
||||
component: (props) => (
|
||||
<KibanaContextProvider services={dependencies}>
|
||||
<VegaDataInspector {...(props as VegaDataInspectorProps)}> </VegaDataInspector>
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<VegaDataInspector {...(props as VegaDataInspectorProps)} />
|
||||
</Suspense>
|
||||
</KibanaContextProvider>
|
||||
),
|
||||
} as InspectorViewDescription);
|
||||
|
|
|
@ -21,22 +21,20 @@ 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';
|
||||
|
||||
import { createVegaRequestHandler } from './vega_request_handler';
|
||||
// @ts-expect-error
|
||||
import { createVegaVisualization } from './vega_visualization';
|
||||
import { getDefaultSpec } from './default_spec';
|
||||
import { createInspectorAdapters } from './vega_inspector';
|
||||
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 { VegaVisEditorComponent } from './components/vega_vis_editor_lazy';
|
||||
|
||||
export const createVegaTypeDefinition = (
|
||||
dependencies: VegaVisualizationDependencies
|
||||
): BaseVisTypeOptions => {
|
||||
): BaseVisTypeOptions<VisParams> => {
|
||||
const requestHandler = createVegaRequestHandler(dependencies);
|
||||
const visualization = createVegaVisualization(dependencies);
|
||||
|
||||
return {
|
||||
name: 'vega',
|
||||
|
@ -49,13 +47,12 @@ export const createVegaTypeDefinition = (
|
|||
icon: 'visVega',
|
||||
visConfig: { defaults: { spec: getDefaultSpec() } },
|
||||
editorConfig: {
|
||||
optionsTemplate: VegaVisEditor,
|
||||
optionsTemplate: VegaVisEditorComponent,
|
||||
enableAutoApply: true,
|
||||
defaultSize: DefaultEditorSize.MEDIUM,
|
||||
},
|
||||
visualization,
|
||||
requestHandler,
|
||||
responseHandler: 'none',
|
||||
toExpressionAst,
|
||||
options: {
|
||||
showIndexSelection: false,
|
||||
showQueryBar: true,
|
||||
|
|
40
src/plugins/vis_type_vega/public/vega_view/vega_base_view.d.ts
vendored
Normal file
40
src/plugins/vis_type_vega/public/vega_view/vega_base_view.d.ts
vendored
Normal 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>;
|
||||
}
|
|
@ -63,7 +63,7 @@ export class VegaBaseView {
|
|||
this._parser = opts.vegaParser;
|
||||
this._serviceSettings = opts.serviceSettings;
|
||||
this._filterManager = opts.filterManager;
|
||||
this._applyFilter = opts.applyFilter;
|
||||
this._fireEvent = opts.fireEvent;
|
||||
this._timefilter = opts.timefilter;
|
||||
this._findIndex = opts.findIndex;
|
||||
this._view = null;
|
||||
|
@ -264,7 +264,7 @@ export class VegaBaseView {
|
|||
const indexId = await this._findIndex(index);
|
||||
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) {
|
||||
const { from, to, mode } = VegaBaseView._parseTimeRange(start, end);
|
||||
|
||||
this._applyFilter({
|
||||
timeFieldName: '*',
|
||||
filters: [
|
||||
{
|
||||
range: {
|
||||
'*': {
|
||||
mode,
|
||||
gte: from,
|
||||
lte: to,
|
||||
this._fireEvent({
|
||||
name: 'applyFilter',
|
||||
data: {
|
||||
timeFieldName: '*',
|
||||
filters: [
|
||||
{
|
||||
range: {
|
||||
'*': {
|
||||
mode,
|
||||
gte: from,
|
||||
lte: to,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
22
src/plugins/vis_type_vega/public/vega_view/vega_map_view.d.ts
vendored
Normal file
22
src/plugins/vis_type_vega/public/vega_view/vega_map_view.d.ts
vendored
Normal 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 {}
|
|
@ -17,4 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { VegaVisEditor } from './vega_vis_editor';
|
||||
import { VegaBaseView } from './vega_base_view';
|
||||
|
||||
export class VegaView extends VegaBaseView {}
|
51
src/plugins/vis_type_vega/public/vega_vis_renderer.tsx
Normal file
51
src/plugins/vis_type_vega/public/vega_vis_renderer.tsx
Normal 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
|
||||
);
|
||||
},
|
||||
});
|
|
@ -30,8 +30,6 @@ import vegaMapGraph from './test_utils/vega_map_test.json';
|
|||
import { VegaParser } from './data_model/vega_parser';
|
||||
import { SearchAPI } from './data_model/search_api';
|
||||
|
||||
import { createVegaTypeDefinition } from './vega_type';
|
||||
|
||||
import { setInjectedVars, setData, setSavedObjects, setNotifications } from './services';
|
||||
import { coreMock } from '../../../core/public/mocks';
|
||||
import { dataPluginMock } from '../../data/public/mocks';
|
||||
|
@ -49,9 +47,7 @@ jest.mock('./lib/vega', () => ({
|
|||
describe('VegaVisualizations', () => {
|
||||
let domNode;
|
||||
let VegaVisualization;
|
||||
let vis;
|
||||
let vegaVisualizationDependencies;
|
||||
let vegaVisType;
|
||||
|
||||
let mockWidth;
|
||||
let mockedWidthValue;
|
||||
|
@ -91,22 +87,12 @@ describe('VegaVisualizations', () => {
|
|||
getServiceSettings: mockGetServiceSettings,
|
||||
};
|
||||
|
||||
vegaVisType = createVegaTypeDefinition(vegaVisualizationDependencies);
|
||||
VegaVisualization = createVegaVisualization(vegaVisualizationDependencies);
|
||||
});
|
||||
|
||||
describe('VegaVisualization - basics', () => {
|
||||
beforeEach(async () => {
|
||||
setupDOM();
|
||||
|
||||
vis = {
|
||||
type: vegaVisType,
|
||||
API: {
|
||||
events: {
|
||||
applyFilter: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -117,7 +103,7 @@ describe('VegaVisualizations', () => {
|
|||
test('should show vegalite graph and update on resize (may fail in dev env)', async () => {
|
||||
let vegaVis;
|
||||
try {
|
||||
vegaVis = new VegaVisualization(domNode, vis);
|
||||
vegaVis = new VegaVisualization(domNode, jest.fn());
|
||||
|
||||
const vegaParser = new VegaParser(
|
||||
JSON.stringify(vegaliteGraph),
|
||||
|
@ -137,7 +123,7 @@ describe('VegaVisualizations', () => {
|
|||
mockedWidthValue = 256;
|
||||
mockedHeightValue = 256;
|
||||
|
||||
await vegaVis._vegaView.resize();
|
||||
await vegaVis.vegaView.resize();
|
||||
|
||||
expect(domNode.innerHTML).toMatchSnapshot();
|
||||
} finally {
|
||||
|
@ -148,7 +134,7 @@ describe('VegaVisualizations', () => {
|
|||
test('should show vega graph (may fail in dev env)', async () => {
|
||||
let vegaVis;
|
||||
try {
|
||||
vegaVis = new VegaVisualization(domNode, vis);
|
||||
vegaVis = new VegaVisualization(domNode, jest.fn());
|
||||
const vegaParser = new VegaParser(
|
||||
JSON.stringify(vegaGraph),
|
||||
new SearchAPI({
|
||||
|
@ -172,7 +158,7 @@ describe('VegaVisualizations', () => {
|
|||
test('should show vega blank rectangle on top of a map (vegamap)', async () => {
|
||||
let vegaVis;
|
||||
try {
|
||||
vegaVis = new VegaVisualization(domNode, vis);
|
||||
vegaVis = new VegaVisualization(domNode, jest.fn());
|
||||
const vegaParser = new VegaParser(
|
||||
JSON.stringify(vegaMapGraph),
|
||||
new SearchAPI({
|
||||
|
|
|
@ -17,28 +17,34 @@
|
|||
* under the License.
|
||||
*/
|
||||
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 type { VegaView } from './vega_view/vega_view';
|
||||
|
||||
export const createVegaVisualization = ({ getServiceSettings }) =>
|
||||
export const createVegaVisualization = ({ getServiceSettings }: VegaVisualizationDependencies) =>
|
||||
class VegaVisualization {
|
||||
constructor(el, vis) {
|
||||
this._el = el;
|
||||
this._vis = vis;
|
||||
private readonly dataPlugin = getData();
|
||||
private readonly savedObjectsClient = getSavedObjects();
|
||||
private vegaView: InstanceType<typeof VegaView> | null = null;
|
||||
|
||||
this.savedObjectsClient = getSavedObjects();
|
||||
this.dataPlugin = getData();
|
||||
}
|
||||
constructor(
|
||||
private el: HTMLDivElement,
|
||||
private fireEvent: IInterpreterRenderHandlers['event']
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Find index pattern by its title, of if not given, gets default
|
||||
* @param {string} [index]
|
||||
* @returns {Promise<string>} index id
|
||||
*/
|
||||
async findIndex(index) {
|
||||
async findIndex(index: string) {
|
||||
const { indexPatterns } = this.dataPlugin;
|
||||
let idxObj;
|
||||
|
||||
if (index) {
|
||||
// @ts-expect-error
|
||||
idxObj = indexPatterns.findByTitle(this.savedObjectsClient, index);
|
||||
if (!idxObj) {
|
||||
throw new Error(
|
||||
|
@ -61,16 +67,10 @@ export const createVegaVisualization = ({ getServiceSettings }) =>
|
|||
return idxObj.id;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {VegaParser} visData
|
||||
* @param {*} status
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async render(visData) {
|
||||
async render(visData: VegaParser) {
|
||||
const { toasts } = getNotifications();
|
||||
|
||||
if (!visData && !this._vegaView) {
|
||||
if (!visData && !this.vegaView) {
|
||||
toasts.addWarning(
|
||||
i18n.translate('visTypeVega.visualization.unableToRenderWithoutDataWarningMessage', {
|
||||
defaultMessage: 'Unable to render without data',
|
||||
|
@ -82,8 +82,8 @@ export const createVegaVisualization = ({ getServiceSettings }) =>
|
|||
try {
|
||||
await this._render(visData);
|
||||
} catch (error) {
|
||||
if (this._vegaView) {
|
||||
this._vegaView.onError(error);
|
||||
if (this.vegaView) {
|
||||
this.vegaView.onError(error);
|
||||
} else {
|
||||
toasts.addError(error, {
|
||||
title: i18n.translate('visTypeVega.visualization.renderErrorTitle', {
|
||||
|
@ -94,20 +94,20 @@ export const createVegaVisualization = ({ getServiceSettings }) =>
|
|||
}
|
||||
}
|
||||
|
||||
async _render(vegaParser) {
|
||||
async _render(vegaParser: VegaParser) {
|
||||
if (vegaParser) {
|
||||
// New data received, rebuild the graph
|
||||
if (this._vegaView) {
|
||||
await this._vegaView.destroy();
|
||||
this._vegaView = null;
|
||||
if (this.vegaView) {
|
||||
await this.vegaView.destroy();
|
||||
this.vegaView = null;
|
||||
}
|
||||
|
||||
const serviceSettings = await getServiceSettings();
|
||||
const { filterManager } = this.dataPlugin.query;
|
||||
const { timefilter } = this.dataPlugin.query.timefilter;
|
||||
const vegaViewParams = {
|
||||
parentEl: this._el,
|
||||
applyFilter: this._vis.API.events.applyFilter,
|
||||
parentEl: this.el,
|
||||
fireEvent: this.fireEvent,
|
||||
vegaParser,
|
||||
serviceSettings,
|
||||
filterManager,
|
||||
|
@ -116,18 +116,17 @@ export const createVegaVisualization = ({ getServiceSettings }) =>
|
|||
};
|
||||
|
||||
if (vegaParser.useMap) {
|
||||
const services = { toastService: getNotifications().toasts };
|
||||
const { VegaMapView } = await import('./vega_view/vega_map_view');
|
||||
this._vegaView = new VegaMapView(vegaViewParams, services);
|
||||
this.vegaView = new VegaMapView(vegaViewParams);
|
||||
} else {
|
||||
const { VegaView } = await import('./vega_view/vega_view');
|
||||
this._vegaView = new VegaView(vegaViewParams);
|
||||
const { VegaView: VegaViewClass } = await import('./vega_view/vega_view');
|
||||
this.vegaView = new VegaViewClass(vegaViewParams);
|
||||
}
|
||||
await this._vegaView.init();
|
||||
await this.vegaView?.init();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
return this._vegaView && this._vegaView.destroy();
|
||||
this.vegaView?.destroy();
|
||||
}
|
||||
};
|
|
@ -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 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' "`;
|
||||
|
|
|
@ -94,14 +94,6 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
|
|||
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', () => {
|
||||
const params = {
|
||||
some: 'nested',
|
||||
|
|
|
@ -254,9 +254,6 @@ const adjustVislibDimensionFormmaters = (vis: Vis, dimensions: { y: any[] }): vo
|
|||
};
|
||||
|
||||
export const buildPipelineVisFunction: BuildPipelineVisFunction = {
|
||||
vega: (params) => {
|
||||
return `vega ${prepareString('spec', params.spec)}`;
|
||||
},
|
||||
input_control_vis: (params) => {
|
||||
return `input_control_vis ${prepareJson('visConfig', params)}`;
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue