mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -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 {
|
.vgaVis {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 1 100%;
|
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 { 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 };
|
||||||
|
|
|
@ -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 { 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));
|
||||||
}
|
}
|
||||||
|
|
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>;
|
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',
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
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._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,7 +301,9 @@ 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({
|
||||||
|
name: 'applyFilter',
|
||||||
|
data: {
|
||||||
timeFieldName: '*',
|
timeFieldName: '*',
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
|
@ -314,6 +316,7 @@ export class VegaBaseView {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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.
|
* 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 { 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({
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -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' "`;
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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)}`;
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue