Skip to content

Commit

Permalink
Drilldowns for TSVB / Vega / Timelion (#74848) (#75037)
Browse files Browse the repository at this point in the history
* Drilldowns for TSVB / Vega

Closes: #60611

* fix PR comment

* fix PR comments

* add support for Timelion

* rename vis.API.events.brush -> vis.API.events.applyFilter
  • Loading branch information
alexwizp authored Aug 14, 2020
1 parent e16d712 commit 400f604
Show file tree
Hide file tree
Showing 16 changed files with 124 additions and 40 deletions.
2 changes: 2 additions & 0 deletions src/plugins/vis_type_timelion/public/components/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import React from 'react';

import { Sheet } from '../helpers/timelion_request_handler';
import { Panel } from './panel';
import { ExprVisAPIEvents } from '../../../visualizations/public';

interface ChartComponentProp {
applyFilter: ExprVisAPIEvents['applyFilter'];
interval: string;
renderComplete(): void;
seriesList: Sheet;
Expand Down
22 changes: 17 additions & 5 deletions src/plugins/vis_type_timelion/public/components/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,20 @@ import {
colors,
Axis,
} from '../helpers/panel_utils';

import { Series, Sheet } from '../helpers/timelion_request_handler';
import { tickFormatters } from '../helpers/tick_formatters';
import { generateTicksProvider } from '../helpers/tick_generator';
import { TimelionVisDependencies } from '../plugin';
import { ExprVisAPIEvents } from '../../../visualizations/public';

interface CrosshairPlot extends jquery.flot.plot {
setCrosshair: (pos: Position) => void;
clearCrosshair: () => void;
}

interface PanelProps {
applyFilter: ExprVisAPIEvents['applyFilter'];
interval: string;
seriesList: Sheet;
renderComplete(): void;
Expand Down Expand Up @@ -72,7 +75,7 @@ const DEBOUNCE_DELAY = 50;
// ensure legend is the same height with or without a caption so legend items do not move around
const emptyCaption = '<br>';

function Panel({ interval, seriesList, renderComplete }: PanelProps) {
function Panel({ interval, seriesList, renderComplete, applyFilter }: PanelProps) {
const kibana = useKibana<TimelionVisDependencies>();
const [chart, setChart] = useState(() => cloneDeep(seriesList.list));
const [canvasElem, setCanvasElem] = useState<HTMLDivElement>();
Expand Down Expand Up @@ -346,12 +349,21 @@ function Panel({ interval, seriesList, renderComplete }: PanelProps) {

const plotSelectedHandler = useCallback(
(event: JQuery.TriggeredEvent, ranges: Ranges) => {
kibana.services.timefilter.setTime({
from: moment(ranges.xaxis.from),
to: moment(ranges.xaxis.to),
applyFilter({
timeFieldName: '*',
filters: [
{
range: {
'*': {
gte: ranges.xaxis.from,
lte: ranges.xaxis.to,
},
},
},
],
});
},
[kibana.services.timefilter]
[applyFilter]
);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function TimelionVisComponent(props: TimelionVisComponentProp) {
return (
<div className="timVis">
<ChartComponent
applyFilter={props.vis.API.events.applyFilter}
seriesList={props.visData.sheet[0]}
renderComplete={props.renderComplete}
interval={props.vis.getState().params.interval}
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { TimelionVisComponent, TimelionVisComponentProp } from './components';
import { TimelionOptions, TimelionOptionsProps } from './timelion_options';
import { TimelionVisDependencies } from './plugin';

import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';

export const TIMELION_VIS_NAME = 'timelion';

export function getTimelionVisDefinition(dependencies: TimelionVisDependencies) {
Expand Down Expand Up @@ -63,6 +65,9 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies)
requestHandler: timelionRequestHandler,
responseHandler: 'none',
inspectorAdapters: {},
getSupportedTriggers: () => {
return [VIS_EVENT_TO_TRIGGER.applyFilter];
},
options: {
showIndexSelection: false,
showQueryBar: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class VisEditor extends Component {
visFields: props.visFields,
extractedIndexPatterns: [''],
};
this.onBrush = createBrushHandler(getDataStart().query.timefilter.timefilter);
this.onBrush = createBrushHandler((data) => props.vis.API.events.applyFilter(data));
this.visDataSubject = new Rx.BehaviorSubject(this.props.visData);
this.visData$ = this.visDataSubject.asObservable().pipe(share());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,31 @@
*/

import { createBrushHandler } from './create_brush_handler';
import moment from 'moment';
import { ExprVisAPIEvents } from '../../../../visualizations/public';

describe('brushHandler', () => {
let mockTimefilter;
let onBrush;
let onBrush: ReturnType<typeof createBrushHandler>;
let applyFilter: ExprVisAPIEvents['applyFilter'];

beforeEach(() => {
mockTimefilter = {
time: {},
setTime: function (time) {
this.time = time;
},
};
onBrush = createBrushHandler(mockTimefilter);
applyFilter = jest.fn();

onBrush = createBrushHandler(applyFilter);
});

it('returns brushHandler() that updates timefilter', () => {
const from = '2017-01-01T00:00:00Z';
const to = '2017-01-01T00:10:00Z';
onBrush(from, to);
expect(mockTimefilter.time.from).toEqual(moment(from).toISOString());
expect(mockTimefilter.time.to).toEqual(moment(to).toISOString());
expect(mockTimefilter.time.mode).toEqual('absolute');
test('returns brushHandler() should updates timefilter through vis.API.events.applyFilter', () => {
const gte = '2017-01-01T00:00:00Z';
const lte = '2017-01-01T00:10:00Z';

onBrush(gte, lte);

expect(applyFilter).toHaveBeenCalledWith({
timeFieldName: '*',
filters: [
{
range: { '*': { gte: '2017-01-01T00:00:00Z', lte: '2017-01-01T00:10:00Z' } },
},
],
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,23 @@
* under the License.
*/

import moment from 'moment';
import { ExprVisAPIEvents } from '../../../../visualizations/public';

const TIME_MODE = 'absolute';

export const createBrushHandler = (timefilter) => (from, to) => {
timefilter.setTime({
from: moment(from).toISOString(),
to: moment(to).toISOString(),
mode: TIME_MODE,
export const createBrushHandler = (applyFilter: ExprVisAPIEvents['applyFilter']) => (
gte: string,
lte: string
) => {
return applyFilter({
timeFieldName: '*',
filters: [
{
range: {
'*': {
gte,
lte,
},
},
},
],
});
};
4 changes: 4 additions & 0 deletions src/plugins/vis_type_timeseries/public/metrics_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { EditorController } from './application';
// @ts-ignore
import { PANEL_TYPES } from '../common/panel_types';
import { VisEditor } from './application/components/vis_editor_lazy';
import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';

export const metricsVisDefinition = {
name: 'metrics',
Expand Down Expand Up @@ -78,6 +79,9 @@ export const metricsVisDefinition = {
showIndexSelection: false,
},
requestHandler: metricsRequestHandler,
getSupportedTriggers: () => {
return [VIS_EVENT_TO_TRIGGER.applyFilter];
},
inspectorAdapters: {},
responseHandler: 'none',
};
4 changes: 4 additions & 0 deletions src/plugins/vis_type_vega/public/vega_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { createVegaRequestHandler } from './vega_request_handler';
import { createVegaVisualization } from './vega_visualization';
import { getDefaultSpec } from './default_spec';
import { createInspectorAdapters } from './vega_inspector';
import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';

export const createVegaTypeDefinition = (dependencies: VegaVisualizationDependencies) => {
const requestHandler = createVegaRequestHandler(dependencies);
Expand Down Expand Up @@ -54,6 +55,9 @@ export const createVegaTypeDefinition = (dependencies: VegaVisualizationDependen
showQueryBar: true,
showFilterBar: true,
},
getSupportedTriggers: () => {
return [VIS_EVENT_TO_TRIGGER.applyFilter];
},
stage: 'experimental',
inspectorAdapters: createInspectorAdapters,
};
Expand Down
21 changes: 19 additions & 2 deletions src/plugins/vis_type_vega/public/vega_view/vega_base_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class VegaBaseView {
this._parser = opts.vegaParser;
this._serviceSettings = opts.serviceSettings;
this._filterManager = opts.filterManager;
this._applyFilter = opts.applyFilter;
this._timefilter = opts.timefilter;
this._findIndex = opts.findIndex;
this._view = null;
Expand Down Expand Up @@ -263,7 +264,8 @@ export class VegaBaseView {
async addFilterHandler(query, index) {
const indexId = await this._findIndex(index);
const filter = esFilters.buildQueryFilter(query, indexId);
this._filterManager.addFilters(filter);

this._applyFilter({ filters: [filter] });
}

/**
Expand Down Expand Up @@ -298,7 +300,22 @@ export class VegaBaseView {
* @param {number|string|Date} end
*/
setTimeFilterHandler(start, end) {
this._timefilter.setTime(VegaBaseView._parseTimeRange(start, end));
const { from, to, mode } = VegaBaseView._parseTimeRange(start, end);

this._applyFilter({
timeFieldName: '*',
filters: [
{
range: {
'*': {
mode,
gte: from,
lte: to,
},
},
},
],
});
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/plugins/vis_type_vega/public/vega_visualization.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export const createVegaVisualization = ({ serviceSettings }) =>
const { timefilter } = this.dataPlugin.query.timefilter;
const vegaViewParams = {
parentEl: this._el,
applyFilter: this._vis.API.events.applyFilter,
vegaParser,
serviceSettings,
filterManager,
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/vis_type_vega/public/vega_visualization.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ describe('VegaVisualizations', () => {

vis = {
type: vegaVisType,
API: {
events: {
applyFilter: jest.fn(),
},
},
};
});

Expand Down
8 changes: 7 additions & 1 deletion src/plugins/visualizations/public/embeddable/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@
* under the License.
*/

import { SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER } from '../../../../plugins/ui_actions/public';
import {
APPLY_FILTER_TRIGGER,
SELECT_RANGE_TRIGGER,
VALUE_CLICK_TRIGGER,
} from '../../../../plugins/ui_actions/public';

export interface VisEventToTrigger {
['applyFilter']: typeof APPLY_FILTER_TRIGGER;
['brush']: typeof SELECT_RANGE_TRIGGER;
['filter']: typeof VALUE_CLICK_TRIGGER;
}

export const VIS_EVENT_TO_TRIGGER: VisEventToTrigger = {
applyFilter: APPLY_FILTER_TRIGGER,
brush: SELECT_RANGE_TRIGGER,
filter: VALUE_CLICK_TRIGGER,
};
Original file line number Diff line number Diff line change
Expand Up @@ -301,12 +301,21 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
}

if (!this.input.disableTriggers) {
const triggerId =
event.name === 'brush' ? VIS_EVENT_TO_TRIGGER.brush : VIS_EVENT_TO_TRIGGER.filter;
const context = {
embeddable: this,
data: { timeFieldName: this.vis.data.indexPattern?.timeFieldName!, ...event.data },
};
const triggerId = get(VIS_EVENT_TO_TRIGGER, event.name, VIS_EVENT_TO_TRIGGER.filter);
let context;

if (triggerId === VIS_EVENT_TO_TRIGGER.applyFilter) {
context = {
embeddable: this,
timeFieldName: this.vis.data.indexPattern?.timeFieldName!,
...event.data,
};
} else {
context = {
embeddable: this,
data: { timeFieldName: this.vis.data.indexPattern?.timeFieldName!, ...event.data },
};
}

getUiActions().getTrigger(triggerId).exec(context);
}
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/visualizations/public/expressions/vis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface ExprVisState {
export interface ExprVisAPIEvents {
filter: (data: any) => void;
brush: (data: any) => void;
applyFilter: (data: any) => void;
}

export interface ExprVisAPI {
Expand Down Expand Up @@ -83,6 +84,10 @@ export class ExprVis extends EventEmitter {
if (!this.eventsSubject) return;
this.eventsSubject.next({ name: 'brush', data });
},
applyFilter: (data: any) => {
if (!this.eventsSubject) return;
this.eventsSubject.next({ name: 'applyFilter', data });
},
},
};
}
Expand Down
1 change: 1 addition & 0 deletions src/plugins/visualizations/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@ export {
VisSavedObject,
VisResponseValue,
} from './types';
export { ExprVisAPIEvents } from './expressions/vis';
export { VisualizationListItem } from './vis_types/vis_type_alias_registry';
export { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants';

0 comments on commit 400f604

Please sign in to comment.