From b1cb6950824f95422ae163cd3c27106cc17d369c Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 5 May 2020 17:42:48 +0200 Subject: [PATCH 1/6] fix drilldowns for lens --- .../plugins/canvas/public/application.tsx | 13 ----- .../flyout_create_drilldown.tsx | 8 ---- x-pack/plugins/lens/kibana.json | 3 +- .../datatable_visualization/expression.tsx | 29 ++++++------ .../public/datatable_visualization/index.ts | 7 +-- .../editor_frame/editor_frame.tsx | 3 ++ .../editor_frame/workspace_panel.tsx | 31 +++++++++++- .../embeddable/embeddable.tsx | 39 +++++++++++++-- .../embeddable/embeddable_factory.ts | 4 ++ .../embeddable/expression_wrapper.tsx | 8 +++- .../public/editor_frame_service/service.tsx | 4 ++ x-pack/plugins/lens/public/services.ts | 12 ----- x-pack/plugins/lens/public/types.ts | 39 ++++++++++++++- .../lens/public/xy_visualization/index.ts | 5 +- .../public/xy_visualization/xy_expression.tsx | 47 +++++++++++++------ 15 files changed, 170 insertions(+), 82 deletions(-) delete mode 100644 x-pack/plugins/lens/public/services.ts diff --git a/x-pack/legacy/plugins/canvas/public/application.tsx b/x-pack/legacy/plugins/canvas/public/application.tsx index 8ee65c3386afc..2c5418713b5c5 100644 --- a/x-pack/legacy/plugins/canvas/public/application.tsx +++ b/x-pack/legacy/plugins/canvas/public/application.tsx @@ -27,8 +27,6 @@ import { HelpMenu } from './components/help_menu/help_menu'; import { createStore, destroyStore } from './store'; import { VALUE_CLICK_TRIGGER, ActionByType } from '../../../../../src/plugins/ui_actions/public'; -/* eslint-disable */ -import { ACTION_VALUE_CLICK } from '../../../../../src/plugins/data/public/actions/value_click_action'; /* eslint-enable */ import { init as initStatsReporter } from './lib/ui_metric'; @@ -132,17 +130,6 @@ export const initializeCanvas = async ( }, }); - // TODO: We need this to disable the filtering modal from popping up in lens embeds until - // they honor the disableTriggers parameter - const action = startPlugins.uiActions.getAction(ACTION_VALUE_CLICK); - - if (action) { - restoreAction = action; - - startPlugins.uiActions.detachAction(VALUE_CLICK_TRIGGER, action.id); - startPlugins.uiActions.addTriggerAction(VALUE_CLICK_TRIGGER, emptyAction); - } - if (setupPlugins.usageCollection) { initStatsReporter(setupPlugins.usageCollection.reportUiStats); } diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx index 81f88e563a258..4a0e4c4208011 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx @@ -42,14 +42,6 @@ export class FlyoutCreateDrilldownAction implements ActionByType -1; } diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index ce544b31b88ef..0f3c4bfa8cec1 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -9,9 +9,8 @@ "expressions", "navigation", "kibanaLegacy", - "uiActions", "visualizations" ], - "optionalPlugins": ["embeddable", "usageCollection", "taskManager"], + "optionalPlugins": ["embeddable", "usageCollection", "taskManager", "uiActions"], "configPath": ["xpack", "lens"] } diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 71d29be1744bb..088db8025cb95 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -10,17 +10,16 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import { EuiBasicTable, EuiFlexGroup, EuiButtonIcon, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { IAggType } from 'src/plugins/data/public'; -import { FormatFactory, LensMultiTable } from '../types'; import { - ExpressionFunctionDefinition, - ExpressionRenderDefinition, - IInterpreterRenderHandlers, -} from '../../../../../src/plugins/expressions/public'; + FormatFactory, + ILensInterpreterRenderHandlers, + LensExpressionRenderDefinition, + LensMultiTable, +} from '../types'; +import { ExpressionFunctionDefinition } from '../../../../../src/plugins/expressions/public'; import { VisualizationContainer } from '../visualization_container'; import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; -import { getExecuteTriggerActions } from '../services'; +import { TriggerContext, VALUE_CLICK_TRIGGER } from '../../../../../src/plugins/ui_actions/public'; export interface DatatableColumns { columnIds: string[]; } @@ -37,7 +36,7 @@ export interface DatatableProps { type DatatableRenderProps = DatatableProps & { formatFactory: FormatFactory; - executeTriggerActions: UiActionsStart['executeTriggerActions']; + onClickValue: (context: TriggerContext) => void; getType: (name: string) => IAggType; }; @@ -114,7 +113,7 @@ export const datatableColumns: ExpressionFunctionDefinition< export const getDatatableRenderer = (dependencies: { formatFactory: Promise; getType: Promise<(name: string) => IAggType>; -}): ExpressionRenderDefinition => ({ +}): LensExpressionRenderDefinition => ({ name: 'lens_datatable_renderer', displayName: i18n.translate('xpack.lens.datatable.visualizationName', { defaultMessage: 'Datatable', @@ -125,17 +124,19 @@ export const getDatatableRenderer = (dependencies: { render: async ( domNode: Element, config: DatatableProps, - handlers: IInterpreterRenderHandlers + handlers: ILensInterpreterRenderHandlers ) => { const resolvedFormatFactory = await dependencies.formatFactory; - const executeTriggerActions = getExecuteTriggerActions(); const resolvedGetType = await dependencies.getType; + const onClickValue = (context: TriggerContext) => { + handlers.event({ name: VALUE_CLICK_TRIGGER, data: context }); + }; ReactDOM.render( , @@ -176,7 +177,7 @@ export function DatatableComponent(props: DatatableRenderProps) { }, timeFieldName, }; - props.executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context); + props.onClickValue(context); }; return ( diff --git a/x-pack/plugins/lens/public/datatable_visualization/index.ts b/x-pack/plugins/lens/public/datatable_visualization/index.ts index 44894d31da51d..3ded70b6a8746 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/index.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/index.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup, CoreStart } from 'kibana/public'; +import { CoreSetup } from 'kibana/public'; import { datatableVisualization } from './visualization'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; import { datatable, datatableColumns, getDatatableRenderer } from './expression'; import { EditorFrameSetup, FormatFactory } from '../types'; -import { setExecuteTriggerActions } from '../services'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; @@ -42,7 +41,5 @@ export class DatatableVisualization { ); editorFrame.registerVisualization(datatableVisualization); } - start(core: CoreStart, { uiActions }: DatatableVisualizationPluginStartPlugins) { - setExecuteTriggerActions(uiActions.executeTriggerActions); - } + start() {} } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index 5cd803e7cebbc..3155e20091b00 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -26,6 +26,7 @@ import { getSavedObjectFormat } from './save'; import { WorkspacePanelWrapper } from './workspace_panel_wrapper'; import { generateId } from '../../id_generator'; import { Filter, Query, SavedQuery } from '../../../../../../src/plugins/data/public'; +import { EditorFrameStartPlugins } from '../service'; export interface EditorFrameProps { doc?: Document; @@ -36,6 +37,7 @@ export interface EditorFrameProps { ExpressionRenderer: ReactExpressionRendererType; onError: (e: { message: string }) => void; core: CoreSetup | CoreStart; + plugins: EditorFrameStartPlugins; dateRange: { fromDate: string; toDate: string; @@ -278,6 +280,7 @@ export function EditorFrame(props: EditorFrameProps) { dispatch={dispatch} ExpressionRenderer={props.ExpressionRenderer} core={props.core} + plugins={props.plugins} /> ) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx index 1f741ca37934f..83f572e59c0fe 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect, useMemo, useContext } from 'react'; +import React, { useState, useEffect, useMemo, useContext, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { @@ -17,7 +17,10 @@ import { EuiButtonEmpty, } from '@elastic/eui'; import { CoreStart, CoreSetup } from 'kibana/public'; -import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public'; +import { + ExpressionRendererEvent, + ReactExpressionRendererType, +} from '../../../../../../src/plugins/expressions/public'; import { Action } from './state_management'; import { Datasource, Visualization, FramePublicAPI } from '../../types'; import { DragDrop, DragContext } from '../../drag_drop'; @@ -25,6 +28,11 @@ import { getSuggestions, switchToSuggestion } from './suggestion_helpers'; import { buildExpression } from './expression_helpers'; import { debouncedComponent } from '../../debounced_component'; import { trackUiEvent } from '../../lens_ui_telemetry'; +import { + SELECT_RANGE_TRIGGER, + VALUE_CLICK_TRIGGER, +} from '../../../../../../src/plugins/ui_actions/public'; +import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; export interface WorkspacePanelProps { activeVisualizationId: string | null; @@ -43,6 +51,7 @@ export interface WorkspacePanelProps { dispatch: (action: Action) => void; ExpressionRenderer: ReactExpressionRendererType; core: CoreStart | CoreSetup; + plugins: { uiActions?: UiActionsStart }; } export const WorkspacePanel = debouncedComponent(InnerWorkspacePanel); @@ -58,6 +67,7 @@ export function InnerWorkspacePanel({ framePublicAPI, dispatch, core, + plugins, ExpressionRenderer: ExpressionRendererComponent, }: WorkspacePanelProps) { const IS_DARK_THEME = core.uiSettings.get('theme:darkMode'); @@ -184,6 +194,22 @@ export function InnerWorkspacePanel({ } }, [expression]); + const handleEvent = useCallback( + (event: ExpressionRendererEvent) => { + if (!plugins.uiActions) { + // ui actions not available, not handling event... + return; + } + if (event.name === VALUE_CLICK_TRIGGER) { + plugins.uiActions.executeTriggerActions(VALUE_CLICK_TRIGGER, event.data); + } + if (event.name === SELECT_RANGE_TRIGGER) { + plugins.uiActions.executeTriggerActions(SELECT_RANGE_TRIGGER, event.data); + } + }, + [plugins] + ); + if (expression === null) { return renderEmptyWorkspace(); } @@ -211,6 +237,7 @@ export function InnerWorkspacePanel({ className="lnsExpressionRenderer__component" padding="m" expression={expression!} + onEvent={handleEvent} renderError={(errorMessage?: string | null) => { return ( diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 6cd15e3c93e4e..e1b9f057f1768 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -8,25 +8,34 @@ import _ from 'lodash'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { - Query, - TimeRange, Filter, IIndexPattern, + Query, TimefilterContract, + TimeRange, } from 'src/plugins/data/public'; import { Subscription } from 'rxjs'; -import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public'; +import { + ExpressionRendererEvent, + ReactExpressionRendererType, +} from '../../../../../../src/plugins/expressions/public'; import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public'; import { Embeddable as AbstractEmbeddable, + EmbeddableInput, EmbeddableOutput, IContainer, - EmbeddableInput, } from '../../../../../../src/plugins/embeddable/public'; -import { Document, DOC_TYPE } from '../../persistence'; +import { DOC_TYPE, Document } from '../../persistence'; import { ExpressionWrapper } from './expression_wrapper'; +import { + SELECT_RANGE_TRIGGER, + VALUE_CLICK_TRIGGER, + UiActionsStart, +} from '../../../../../../src/plugins/ui_actions/public'; +import { LensInterpreterEvent } from '../../types'; export interface LensEmbeddableConfiguration { savedVis: Document; @@ -45,10 +54,15 @@ export interface LensEmbeddableOutput extends EmbeddableOutput { indexPatterns?: IIndexPattern[]; } +function isLensInterpreterEvent(event: ExpressionRendererEvent): event is LensInterpreterEvent { + return event.name === VALUE_CLICK_TRIGGER || event.name === SELECT_RANGE_TRIGGER; +} + export class Embeddable extends AbstractEmbeddable { type = DOC_TYPE; private expressionRenderer: ReactExpressionRendererType; + private executeTriggerActions: UiActionsStart['executeTriggerActions'] | undefined; private savedVis: Document; private domNode: HTMLElement | Element | undefined; private subscription: Subscription; @@ -64,6 +78,8 @@ export class Embeddable extends AbstractEmbeddable this.onContainerStateChanged(input)); @@ -97,6 +114,8 @@ export class Embeddable extends AbstractEmbeddable, domNode ); } + handleEvent = (event: ExpressionRendererEvent) => { + if (!this.executeTriggerActions || this.input.disableTriggers) { + return; + } + if (isLensInterpreterEvent(event)) { + this.executeTriggerActions(event.name, { ...event.data, embeddable: this }); + } + }; + destroy() { super.destroy(); if (this.domNode) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index 68dbff263f60d..ec2d3af422140 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -26,6 +26,7 @@ import { import { Embeddable } from './embeddable'; import { SavedObjectIndexStore, DOC_TYPE } from '../../persistence'; import { getEditPath } from '../../../common'; +import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; interface StartServices { timefilter: TimefilterContract; @@ -34,6 +35,7 @@ interface StartServices { savedObjectsClient: SavedObjectsClientContract; expressionRenderer: ReactExpressionRendererType; indexPatternService: IndexPatternsContract; + uiActions?: UiActionsStart; } export class EmbeddableFactory implements EmbeddableFactoryDefinition { @@ -74,6 +76,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { indexPatternService, timefilter, expressionRenderer, + uiActions, } = await this.getStartServices(); const store = new SavedObjectIndexStore(savedObjectsClient); const savedVis = await store.load(savedObjectId); @@ -99,6 +102,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { return new Embeddable( timefilter, expressionRenderer, + uiActions?.executeTriggerActions, { savedVis, editUrl: coreHttp.basePath.prepend(getEditPath(savedObjectId)), diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx index 49c91affe3dc4..41706121830cb 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx @@ -9,7 +9,10 @@ import { I18nProvider } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiText, EuiIcon } from '@elastic/eui'; import { TimeRange, Filter, Query } from 'src/plugins/data/public'; -import { ReactExpressionRendererType } from 'src/plugins/expressions/public'; +import { + ExpressionRendererEvent, + ReactExpressionRendererType, +} from 'src/plugins/expressions/public'; export interface ExpressionWrapperProps { ExpressionRenderer: ReactExpressionRendererType; @@ -20,12 +23,14 @@ export interface ExpressionWrapperProps { filters?: Filter[]; lastReloadRequestTime?: number; }; + handleEvent: (event: ExpressionRendererEvent) => void; } export function ExpressionWrapper({ ExpressionRenderer: ExpressionRendererComponent, expression, context, + handleEvent, }: ExpressionWrapperProps) { return ( @@ -51,6 +56,7 @@ export function ExpressionWrapper({ expression={expression} searchContext={{ ...context }} renderError={error =>
{error}
} + onEvent={handleEvent} /> )} diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index 15fe449d6563b..a815e70c58629 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -26,6 +26,7 @@ import { mergeTables } from './merge_tables'; import { formatColumn } from './format_column'; import { EmbeddableFactory } from './embeddable/embeddable_factory'; import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; +import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; export interface EditorFrameSetupPlugins { data: DataPublicPluginSetup; @@ -37,6 +38,7 @@ export interface EditorFrameStartPlugins { data: DataPublicPluginStart; embeddable?: EmbeddableStart; expressions: ExpressionsStart; + uiActions?: UiActionsStart; } async function collectAsyncDefinitions( @@ -73,6 +75,7 @@ export class EditorFrameService { timefilter: deps.data.query.timefilter.timefilter, expressionRenderer: deps.expressions.ReactExpressionRenderer, indexPatternService: deps.data.indexPatterns, + uiActions: deps.uiActions, }; }; @@ -116,6 +119,7 @@ export class EditorFrameService { (doc && doc.visualizationType) || firstVisualizationId || null } core={core} + plugins={plugins} ExpressionRenderer={plugins.expressions.ReactExpressionRenderer} doc={doc} dateRange={dateRange} diff --git a/x-pack/plugins/lens/public/services.ts b/x-pack/plugins/lens/public/services.ts deleted file mode 100644 index a66743dde2661..0000000000000 --- a/x-pack/plugins/lens/public/services.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createGetterSetter } from '../../../../src/plugins/kibana_utils/public'; -import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; - -export const [getExecuteTriggerActions, setExecuteTriggerActions] = createGetterSetter< - UiActionsStart['executeTriggerActions'] ->('executeTriggerActions'); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 04efc642793b0..160eec3c0e012 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -7,11 +7,21 @@ import { Ast } from '@kbn/interpreter/common'; import { IconType } from '@elastic/eui/src/components/icon/icon'; import { CoreSetup } from 'kibana/public'; -import { KibanaDatatable, SerializedFieldFormat } from '../../../../src/plugins/expressions/public'; +import { + ExpressionRenderDefinition, + IInterpreterRenderHandlers, + KibanaDatatable, + SerializedFieldFormat, +} from '../../../../src/plugins/expressions/public'; import { DragContextState } from './drag_drop'; import { Document } from './persistence'; import { DateRange } from '../common'; import { Query, Filter, SavedQuery, IFieldFormat } from '../../../../src/plugins/data/public'; +import { + SELECT_RANGE_TRIGGER, + TriggerContext, + VALUE_CLICK_TRIGGER, +} from '../../../../src/plugins/ui_actions/public'; export type ErrorCallback = (e: { message: string }) => void; @@ -467,3 +477,30 @@ export interface Visualization { */ toPreviewExpression?: (state: T, frame: FramePublicAPI) => Ast | string | null; } + +export type LensInterpreterEvent = + | { name: typeof VALUE_CLICK_TRIGGER; data: TriggerContext } + | { name: typeof SELECT_RANGE_TRIGGER; data: TriggerContext }; + +/** + * Expression renderer handlers specifically for lens renderers. This is a narrowed down + * version of the general render handlers, specifying supported event types. If this type is + * used, dispatched events will be handled correctly. + */ +export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandlers { + event: (event: LensInterpreterEvent) => void; +} + +/** + * Type narrowing down an expression renderer definition by specifying the events which are handled by the + * lens embeddable. If this type is used to define a renderer, dispatched event will be handled correctly. + * Other events will be ignored. + */ +export interface LensExpressionRenderDefinition + extends ExpressionRenderDefinition { + render: ( + domNode: HTMLElement, + config: Config, + handlers: ILensInterpreterRenderHandlers + ) => void | Promise; +} diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index 9a0819d4f01c4..1c62cff2f414b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -12,7 +12,6 @@ import { xyVisualization } from './xy_visualization'; import { xyChart, getXyChartRenderer } from './xy_expression'; import { legendConfig, xConfig, layerConfig } from './types'; import { EditorFrameSetup, FormatFactory } from '../types'; -import { setExecuteTriggerActions } from '../services'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; export interface XyVisualizationPluginSetupPlugins { @@ -59,7 +58,5 @@ export class XyVisualization { editorFrame.registerVisualization(xyVisualization); } - start(core: CoreStart, { uiActions }: XyVisualizationPluginStartPlugins) { - setExecuteTriggerActions(uiActions.executeTriggerActions); - } + start() {} } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx index 81ae57a5ee638..30488f287dd92 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import ReactDOM from 'react-dom'; import moment from 'moment'; import { @@ -21,8 +21,6 @@ import { } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; import { - IInterpreterRenderHandlers, - ExpressionRenderDefinition, ExpressionFunctionDefinition, ExpressionValueSearchContext, } from 'src/plugins/expressions/public'; @@ -33,13 +31,20 @@ import { ValueClickTriggerContext, RangeSelectTriggerContext, } from '../../../../../src/plugins/embeddable/public'; -import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public'; -import { LensMultiTable, FormatFactory } from '../types'; +import { + LensMultiTable, + FormatFactory, + LensExpressionRenderDefinition, + ILensInterpreterRenderHandlers, +} from '../types'; import { XYArgs, SeriesType, visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart } from './state_helpers'; -import { getExecuteTriggerActions } from '../services'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; +import { + VALUE_CLICK_TRIGGER, + SELECT_RANGE_TRIGGER, + TriggerContext, +} from '../../../../../src/plugins/ui_actions/public'; import { parseInterval } from '../../../../../src/plugins/data/common'; type InferPropType = T extends React.FunctionComponent ? P : T; @@ -63,7 +68,8 @@ type XYChartRenderProps = XYChartProps & { formatFactory: FormatFactory; timeZone: string; histogramBarTarget: number; - executeTriggerActions: UiActionsStart['executeTriggerActions']; + onClickValue: (context: TriggerContext) => void; + onSelectRange: (context: TriggerContext) => void; }; export const xyChart: ExpressionFunctionDefinition< @@ -117,7 +123,7 @@ export const getXyChartRenderer = (dependencies: { chartTheme: PartialTheme; histogramBarTarget: number; timeZone: string; -}): ExpressionRenderDefinition => ({ +}): LensExpressionRenderDefinition => ({ name: 'lens_xy_chart_renderer', displayName: 'XY chart', help: i18n.translate('xpack.lens.xyChart.renderer.help', { @@ -125,9 +131,18 @@ export const getXyChartRenderer = (dependencies: { }), validate: () => undefined, reuseDomNode: true, - render: async (domNode: Element, config: XYChartProps, handlers: IInterpreterRenderHandlers) => { - const executeTriggerActions = getExecuteTriggerActions(); + render: async ( + domNode: Element, + config: XYChartProps, + handlers: ILensInterpreterRenderHandlers + ) => { handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); + const onClickValue = (context: TriggerContext) => { + handlers.event({ name: VALUE_CLICK_TRIGGER, data: context }); + }; + const onSelectRange = (context: TriggerContext) => { + handlers.event({ name: SELECT_RANGE_TRIGGER, data: context }); + }; const formatFactory = await dependencies.formatFactory; ReactDOM.render( @@ -137,7 +152,8 @@ export const getXyChartRenderer = (dependencies: { chartTheme={dependencies.chartTheme} timeZone={dependencies.timeZone} histogramBarTarget={dependencies.histogramBarTarget} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> , domNode, @@ -177,7 +193,8 @@ export function XYChart({ timeZone, chartTheme, histogramBarTarget, - executeTriggerActions, + onClickValue, + onSelectRange, }: XYChartRenderProps) { const { legend, layers } = args; @@ -304,7 +321,7 @@ export function XYChart({ }, timeFieldName, }; - executeTriggerActions(VIS_EVENT_TO_TRIGGER.brush, context); + onSelectRange(context); }} onElementClick={([[geometry, series]]) => { // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue @@ -357,7 +374,7 @@ export function XYChart({ }, timeFieldName, }; - executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context); + onClickValue(context); }} /> From cfc05ac4eb434131825aa6d43c0414bdb6492632 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 7 May 2020 11:54:06 +0200 Subject: [PATCH 2/6] clean up PR --- .../public/actions/select_range_action.ts | 8 +- .../data/public/actions/value_click_action.ts | 10 +- .../public/lib/triggers/triggers.ts | 4 +- .../public/embeddable/visualize_embeddable.ts | 3 +- x-pack/plugins/canvas/public/application.tsx | 17 --- .../expression.test.tsx | 52 ++++--- .../datatable_visualization/expression.tsx | 42 +++--- .../editor_frame/editor_frame.test.tsx | 8 ++ .../editor_frame/workspace_panel.test.tsx | 62 +++++++++ .../editor_frame/workspace_panel.tsx | 25 ++-- .../embeddable/embeddable.test.tsx | 35 +++++ .../embeddable/embeddable.tsx | 34 ++--- .../embeddable/embeddable_factory.ts | 2 +- x-pack/plugins/lens/public/types.ts | 37 +++-- .../xy_visualization/xy_expression.test.tsx | 129 +++++++++++------- .../public/xy_visualization/xy_expression.tsx | 50 +++---- 16 files changed, 310 insertions(+), 208 deletions(-) diff --git a/src/plugins/data/public/actions/select_range_action.ts b/src/plugins/data/public/actions/select_range_action.ts index 4882e8eafc0d3..18853f7e292f6 100644 --- a/src/plugins/data/public/actions/select_range_action.ts +++ b/src/plugins/data/public/actions/select_range_action.ts @@ -53,16 +53,16 @@ export function selectRangeAction( }); }, isCompatible, - execute: async ({ timeFieldName, data }: SelectRangeActionContext) => { - if (!(await isCompatible({ timeFieldName, data }))) { + execute: async ({ data }: SelectRangeActionContext) => { + if (!(await isCompatible({ data }))) { throw new IncompatibleActionError(); } const selectedFilters = await createFiltersFromRangeSelectAction(data); - if (timeFieldName) { + if (data.timeFieldName) { const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter( - timeFieldName, + data.timeFieldName, selectedFilters ); filterManager.addFilters(restOfFilters); diff --git a/src/plugins/data/public/actions/value_click_action.ts b/src/plugins/data/public/actions/value_click_action.ts index 210a58b3f75aa..17c1b1b1e1769 100644 --- a/src/plugins/data/public/actions/value_click_action.ts +++ b/src/plugins/data/public/actions/value_click_action.ts @@ -57,12 +57,12 @@ export function valueClickAction( }); }, isCompatible, - execute: async (context: ValueClickActionContext) => { - if (!(await isCompatible(context))) { + execute: async ({ data }: ValueClickActionContext) => { + if (!(await isCompatible({ data }))) { throw new IncompatibleActionError(); } - const filters: Filter[] = await createFiltersFromValueClickAction(context.data); + const filters: Filter[] = await createFiltersFromValueClickAction(data); let selectedFilters = filters; @@ -98,9 +98,9 @@ export function valueClickAction( selectedFilters = await filterSelectionPromise; } - if (context.timeFieldName) { + if (data.timeFieldName) { const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter( - context.timeFieldName, + data.timeFieldName, selectedFilters ); filterManager.addFilters(restOfFilters); diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts index c097e3e8c13be..2b447c89e2850 100644 --- a/src/plugins/embeddable/public/lib/triggers/triggers.ts +++ b/src/plugins/embeddable/public/lib/triggers/triggers.ts @@ -27,7 +27,6 @@ export interface EmbeddableContext { export interface ValueClickTriggerContext { embeddable?: T; - timeFieldName?: string; data: { data: Array<{ table: Pick; @@ -35,6 +34,7 @@ export interface ValueClickTriggerContext { row: number; value: any; }>; + timeFieldName?: string; negate?: boolean; }; } @@ -45,11 +45,11 @@ export const isValueClickTriggerContext = ( export interface RangeSelectTriggerContext { embeddable?: T; - timeFieldName?: string; data: { table: KibanaDatatable; column: number; range: number[]; + timeFieldName?: string; }; } diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 10704514ab8d5..f137503f6498d 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -269,8 +269,7 @@ export class VisualizeEmbeddable extends Embeddable | undefined; -const emptyAction = { - id: 'empty-action', - type: '', - getDisplayName: () => 'empty action', - getIconType: () => undefined, - isCompatible: async () => true, - execute: async () => undefined, -} as ActionByType; - export const renderApp = ( coreStart: CoreStart, plugins: CanvasStartDeps, @@ -143,12 +132,6 @@ export const teardownCanvas = (coreStart: CoreStart, startPlugins: CanvasStartDe resetInterpreter(); destroyStore(); - startPlugins.uiActions.detachAction(VALUE_CLICK_TRIGGER, emptyAction.id); - if (restoreAction) { - startPlugins.uiActions.addTriggerAction(VALUE_CLICK_TRIGGER, restoreAction); - restoreAction = undefined; - } - coreStart.chrome.setBadge(undefined); coreStart.chrome.setHelpExtension(undefined); diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index 6d5b1153ad1bc..5407389c7fc4c 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -13,7 +13,7 @@ import { DatatableProps } from './expression'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { IFieldFormat } from '../../../../../src/plugins/data/public'; import { IAggType } from 'src/plugins/data/public'; -const executeTriggerActions = jest.fn(); +const onClickValue = jest.fn(); function sampleArgs() { const data: LensMultiTable = { @@ -66,7 +66,7 @@ describe('datatable_expression', () => { data={data} args={args} formatFactory={x => x as IFieldFormat} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} getType={jest.fn()} /> ) @@ -87,7 +87,7 @@ describe('datatable_expression', () => { }} args={args} formatFactory={x => x as IFieldFormat} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} getType={jest.fn(() => ({ type: 'buckets' } as IAggType))} /> ); @@ -97,18 +97,16 @@ describe('datatable_expression', () => { .first() .simulate('click'); - expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', { - data: { - data: [ - { - column: 0, - row: 0, - table: data.tables.l1, - value: 10110, - }, - ], - negate: true, - }, + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 0, + row: 0, + table: data.tables.l1, + value: 10110, + }, + ], + negate: true, timeFieldName: undefined, }); }); @@ -127,7 +125,7 @@ describe('datatable_expression', () => { }} args={args} formatFactory={x => x as IFieldFormat} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} getType={jest.fn(() => ({ type: 'buckets' } as IAggType))} /> ); @@ -137,18 +135,16 @@ describe('datatable_expression', () => { .at(3) .simulate('click'); - expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', { - data: { - data: [ - { - column: 1, - row: 0, - table: data.tables.l1, - value: 1588024800000, - }, - ], - negate: false, - }, + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 1, + row: 0, + table: data.tables.l1, + value: 1588024800000, + }, + ], + negate: false, timeFieldName: 'b', }); }); diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 088db8025cb95..0e22773ee6abd 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -10,13 +10,11 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import { EuiBasicTable, EuiFlexGroup, EuiButtonIcon, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { IAggType } from 'src/plugins/data/public'; +import { FormatFactory, ILensInterpreterRenderHandlers, LensMultiTable } from '../types'; import { - FormatFactory, - ILensInterpreterRenderHandlers, - LensExpressionRenderDefinition, - LensMultiTable, -} from '../types'; -import { ExpressionFunctionDefinition } from '../../../../../src/plugins/expressions/public'; + ExpressionFunctionDefinition, + ExpressionRenderDefinition, +} from '../../../../../src/plugins/expressions/public'; import { VisualizationContainer } from '../visualization_container'; import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public'; import { TriggerContext, VALUE_CLICK_TRIGGER } from '../../../../../src/plugins/ui_actions/public'; @@ -36,7 +34,7 @@ export interface DatatableProps { type DatatableRenderProps = DatatableProps & { formatFactory: FormatFactory; - onClickValue: (context: TriggerContext) => void; + onClickValue: (data: TriggerContext['data']) => void; getType: (name: string) => IAggType; }; @@ -113,7 +111,7 @@ export const datatableColumns: ExpressionFunctionDefinition< export const getDatatableRenderer = (dependencies: { formatFactory: Promise; getType: Promise<(name: string) => IAggType>; -}): LensExpressionRenderDefinition => ({ +}): ExpressionRenderDefinition => ({ name: 'lens_datatable_renderer', displayName: i18n.translate('xpack.lens.datatable.visualizationName', { defaultMessage: 'Datatable', @@ -128,8 +126,8 @@ export const getDatatableRenderer = (dependencies: { ) => { const resolvedFormatFactory = await dependencies.formatFactory; const resolvedGetType = await dependencies.getType; - const onClickValue = (context: TriggerContext) => { - handlers.event({ name: VALUE_CLICK_TRIGGER, data: context }); + const onClickValue = (data: TriggerContext['data']) => { + handlers.event({ name: 'filter', data }); }; ReactDOM.render( @@ -163,21 +161,19 @@ export function DatatableComponent(props: DatatableRenderProps) { const timeFieldName = negate && isDateHistogram ? undefined : col?.meta?.aggConfigParams?.field; const rowIndex = firstTable.rows.findIndex(row => row[field] === value); - const context: ValueClickTriggerContext = { - data: { - negate, - data: [ - { - row: rowIndex, - column: colIndex, - value, - table: firstTable, - }, - ], - }, + const data: ValueClickTriggerContext['data'] = { + negate, + data: [ + { + row: rowIndex, + column: colIndex, + value, + table: firstTable, + }, + ], timeFieldName, }; - props.onClickValue(context); + props.onClickValue(data); }; return ( diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx index 78b038cf702f8..f9c5668ca1e06 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx @@ -21,6 +21,9 @@ import { import { ReactExpressionRendererType } from 'src/plugins/expressions/public'; import { DragDrop } from '../../drag_drop'; import { FrameLayout } from './frame_layout'; +import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks'; +import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; +import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks'; function generateSuggestion(state = {}): DatasourceSuggestion { return { @@ -48,6 +51,11 @@ function getDefaultProps() { query: { query: '', language: 'lucene' }, filters: [], core: coreMock.createSetup(), + plugins: { + uiActions: uiActionsPluginMock.createStartContract(), + data: dataPluginMock.createStartContract(), + expressions: expressionsPluginMock.createStartContract(), + }, }; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx index 33ecee53fa3bc..a20626ebaaad7 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx @@ -22,6 +22,10 @@ import { DragDrop, ChildDragDropProvider } from '../../drag_drop'; import { Ast } from '@kbn/interpreter/common'; import { coreMock } from 'src/core/public/mocks'; import { esFilters, IFieldType, IIndexPattern } from '../../../../../../src/plugins/data/public'; +import { TriggerId, UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; +import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks'; +import { TriggerContract } from '../../../../../../src/plugins/ui_actions/public/triggers'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public/embeddable'; describe('workspace_panel', () => { let mockVisualization: jest.Mocked; @@ -29,10 +33,15 @@ describe('workspace_panel', () => { let mockDatasource: DatasourceMock; let expressionRendererMock: jest.Mock; + let uiActionsMock: jest.Mocked; + let trigger: jest.Mocked>; let instance: ReactWrapper; beforeEach(() => { + trigger = ({ exec: jest.fn() } as unknown) as jest.Mocked>; + uiActionsMock = uiActionsPluginMock.createStartContract(); + uiActionsMock.getTrigger.mockReturnValue(trigger); mockVisualization = createMockVisualization(); mockVisualization2 = createMockVisualization(); @@ -60,6 +69,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -82,6 +92,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -104,6 +115,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -140,6 +152,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -198,6 +211,48 @@ describe('workspace_panel', () => { `); }); + it('should execute a trigger on expression event', () => { + const framePublicAPI = createMockFramePublicAPI(); + framePublicAPI.datasourceLayers = { + first: mockDatasource.publicAPIMock, + }; + mockDatasource.toExpression.mockReturnValue('datasource'); + mockDatasource.getLayers.mockReturnValue(['first']); + + instance = mount( + 'vis' }, + }} + visualizationState={{}} + dispatch={() => {}} + ExpressionRenderer={expressionRendererMock} + core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} + /> + ); + + const onEvent = expressionRendererMock.mock.calls[0][0].onEvent!; + + const eventData = {}; + onEvent({ name: 'brush', data: eventData }); + + expect(uiActionsMock.getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.brush); + expect(trigger.exec).toHaveBeenCalledWith({ data: eventData }); + }); + it('should include data fetching for each layer in the expression', () => { const mockDatasource2 = createMockDatasource('a'); const framePublicAPI = createMockFramePublicAPI(); @@ -237,6 +292,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -316,6 +372,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); }); @@ -370,6 +427,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); }); @@ -424,6 +482,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); @@ -461,6 +520,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); }); @@ -504,6 +564,7 @@ describe('workspace_panel', () => { dispatch={() => {}} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); }); @@ -559,6 +620,7 @@ describe('workspace_panel', () => { dispatch={mockDispatch} ExpressionRenderer={expressionRendererMock} core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock }} /> ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx index 83f572e59c0fe..e83d0e04f42f2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx @@ -22,17 +22,20 @@ import { ReactExpressionRendererType, } from '../../../../../../src/plugins/expressions/public'; import { Action } from './state_management'; -import { Datasource, Visualization, FramePublicAPI } from '../../types'; +import { + Datasource, + Visualization, + FramePublicAPI, + isLensBrushEvent, + isLensFilterEvent, +} from '../../types'; import { DragDrop, DragContext } from '../../drag_drop'; import { getSuggestions, switchToSuggestion } from './suggestion_helpers'; import { buildExpression } from './expression_helpers'; import { debouncedComponent } from '../../debounced_component'; import { trackUiEvent } from '../../lens_ui_telemetry'; -import { - SELECT_RANGE_TRIGGER, - VALUE_CLICK_TRIGGER, -} from '../../../../../../src/plugins/ui_actions/public'; import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public'; export interface WorkspacePanelProps { activeVisualizationId: string | null; @@ -200,11 +203,15 @@ export function InnerWorkspacePanel({ // ui actions not available, not handling event... return; } - if (event.name === VALUE_CLICK_TRIGGER) { - plugins.uiActions.executeTriggerActions(VALUE_CLICK_TRIGGER, event.data); + if (isLensBrushEvent(event)) { + plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: event.data, + }); } - if (event.name === SELECT_RANGE_TRIGGER) { - plugins.uiActions.executeTriggerActions(SELECT_RANGE_TRIGGER, event.data); + if (isLensFilterEvent(event)) { + plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: event.data, + }); } }, [plugins] diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index aeae64514b0fd..1f0b2e6ea0035 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -10,6 +10,7 @@ import { ReactExpressionRendererProps } from 'src/plugins/expressions/public'; import { Query, TimeRange, Filter, TimefilterContract } from 'src/plugins/data/public'; import { Document } from '../../persistence'; import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public/embeddable'; jest.mock('../../../../../../src/plugins/inspector/public/', () => ({ isAvailable: false, @@ -34,10 +35,14 @@ const savedVis: Document = { describe('embeddable', () => { let mountpoint: HTMLDivElement; let expressionRenderer: jest.Mock; + let getTrigger: jest.Mock; + let trigger: { exec: jest.Mock }; beforeEach(() => { mountpoint = document.createElement('div'); expressionRenderer = jest.fn(_props => null); + trigger = { exec: jest.fn() }; + getTrigger = jest.fn(() => trigger); }); afterEach(() => { @@ -48,6 +53,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, + getTrigger, { editUrl: '', editable: true, @@ -69,6 +75,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, + getTrigger, { editUrl: '', editable: true, @@ -95,6 +102,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, + getTrigger, { editUrl: '', editable: true, @@ -111,6 +119,31 @@ describe('embeddable', () => { }); }); + it('should execute trigger on event from expression renderer', () => { + const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, + expressionRenderer, + getTrigger, + { + editUrl: '', + editable: true, + savedVis, + }, + { id: '123' } + ); + embeddable.render(mountpoint); + + const onEvent = expressionRenderer.mock.calls[0][0].onEvent!; + + const eventData = {}; + onEvent({ name: 'brush', data: eventData }); + + expect(getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.brush); + expect(trigger.exec).toHaveBeenCalledWith( + expect.objectContaining({ data: eventData, embeddable: expect.anything() }) + ); + }); + it('should not re-render if only change is in disabled filter', () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; @@ -119,6 +152,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, + getTrigger, { editUrl: '', editable: true, @@ -150,6 +184,7 @@ describe('embeddable', () => { const embeddable = new Embeddable( timefilter, expressionRenderer, + getTrigger, { editUrl: '', editable: true, diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index e1b9f057f1768..6e8ebec3268d9 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -30,12 +30,8 @@ import { } from '../../../../../../src/plugins/embeddable/public'; import { DOC_TYPE, Document } from '../../persistence'; import { ExpressionWrapper } from './expression_wrapper'; -import { - SELECT_RANGE_TRIGGER, - VALUE_CLICK_TRIGGER, - UiActionsStart, -} from '../../../../../../src/plugins/ui_actions/public'; -import { LensInterpreterEvent } from '../../types'; +import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; +import { isLensBrushEvent, isLensFilterEvent } from '../../types'; export interface LensEmbeddableConfiguration { savedVis: Document; @@ -54,15 +50,11 @@ export interface LensEmbeddableOutput extends EmbeddableOutput { indexPatterns?: IIndexPattern[]; } -function isLensInterpreterEvent(event: ExpressionRendererEvent): event is LensInterpreterEvent { - return event.name === VALUE_CLICK_TRIGGER || event.name === SELECT_RANGE_TRIGGER; -} - export class Embeddable extends AbstractEmbeddable { type = DOC_TYPE; private expressionRenderer: ReactExpressionRendererType; - private executeTriggerActions: UiActionsStart['executeTriggerActions'] | undefined; + private getTrigger: UiActionsStart['getTrigger'] | undefined; private savedVis: Document; private domNode: HTMLElement | Element | undefined; private subscription: Subscription; @@ -78,8 +70,7 @@ export class Embeddable extends AbstractEmbeddable this.onContainerStateChanged(input)); @@ -163,11 +154,20 @@ export class Embeddable extends AbstractEmbeddable { - if (!this.executeTriggerActions || this.input.disableTriggers) { + if (!this.getTrigger || this.input.disableTriggers) { return; } - if (isLensInterpreterEvent(event)) { - this.executeTriggerActions(event.name, { ...event.data, embeddable: this }); + if (isLensBrushEvent(event)) { + this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: event.data, + embeddable: this, + }); + } + if (isLensFilterEvent(event)) { + this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: event.data, + embeddable: this, + }); } }; diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index ec2d3af422140..16f983499b177 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -102,7 +102,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { return new Embeddable( timefilter, expressionRenderer, - uiActions?.executeTriggerActions, + uiActions?.getTrigger, { savedVis, editUrl: coreHttp.basePath.prepend(getEditPath(savedObjectId)), diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 160eec3c0e012..42dcce0e438d7 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -8,7 +8,7 @@ import { Ast } from '@kbn/interpreter/common'; import { IconType } from '@elastic/eui/src/components/icon/icon'; import { CoreSetup } from 'kibana/public'; import { - ExpressionRenderDefinition, + ExpressionRendererEvent, IInterpreterRenderHandlers, KibanaDatatable, SerializedFieldFormat, @@ -478,9 +478,22 @@ export interface Visualization { toPreviewExpression?: (state: T, frame: FramePublicAPI) => Ast | string | null; } -export type LensInterpreterEvent = - | { name: typeof VALUE_CLICK_TRIGGER; data: TriggerContext } - | { name: typeof SELECT_RANGE_TRIGGER; data: TriggerContext }; +export interface LensFilterEvent { + name: 'filter'; + data: TriggerContext['data']; +} +export interface LensBrushEvent { + name: 'brush'; + data: TriggerContext['data']; +} + +export function isLensFilterEvent(event: ExpressionRendererEvent): event is LensFilterEvent { + return event.name === 'filter'; +} + +export function isLensBrushEvent(event: ExpressionRendererEvent): event is LensBrushEvent { + return event.name === 'brush'; +} /** * Expression renderer handlers specifically for lens renderers. This is a narrowed down @@ -488,19 +501,5 @@ export type LensInterpreterEvent = * used, dispatched events will be handled correctly. */ export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandlers { - event: (event: LensInterpreterEvent) => void; -} - -/** - * Type narrowing down an expression renderer definition by specifying the events which are handled by the - * lens embeddable. If this type is used to define a renderer, dispatched event will be handled correctly. - * Other events will be ignored. - */ -export interface LensExpressionRenderDefinition - extends ExpressionRenderDefinition { - render: ( - domNode: HTMLElement, - config: Config, - handlers: ILensInterpreterRenderHandlers - ) => void | Promise; + event: (event: LensFilterEvent | LensBrushEvent) => void; } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx index 0f9aa1c10e127..72e51b175543c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx @@ -25,7 +25,8 @@ import { XYArgs, LegendConfig, legendConfig, layerConfig, LayerArgs } from './ty import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -const executeTriggerActions = jest.fn(); +const onClickValue = jest.fn(); +const onSelectRange = jest.fn(); const dateHistogramData: LensMultiTable = { type: 'lens_multitable', @@ -296,7 +297,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -344,7 +346,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` @@ -379,7 +382,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -415,7 +419,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -452,7 +457,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -496,7 +502,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -530,7 +537,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(Settings).prop('xDomain')).toBeUndefined(); @@ -546,7 +554,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -563,7 +572,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -580,7 +590,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -602,7 +613,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -611,12 +623,10 @@ describe('xy_expression', () => { .first() .prop('onBrushEnd')!({ x: [1585757732783, 1585758880838] }); - expect(executeTriggerActions).toHaveBeenCalledWith('SELECT_RANGE_TRIGGER', { - data: { - column: 0, - table: dateHistogramData.tables.timeLayer, - range: [1585757732783, 1585758880838], - }, + expect(onSelectRange).toHaveBeenCalledWith({ + column: 0, + table: dateHistogramData.tables.timeLayer, + range: [1585757732783, 1585758880838], timeFieldName: 'order_date', }); }); @@ -656,7 +666,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -665,23 +676,21 @@ describe('xy_expression', () => { .first() .prop('onElementClick')!([[geometry, series as XYChartSeriesIdentifier]]); - expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', { - data: { - data: [ - { - column: 1, - row: 1, - table: data.tables.first, - value: 5, - }, - { - column: 1, - row: 0, - table: data.tables.first, - value: 2, - }, - ], - }, + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 1, + row: 1, + table: data.tables.first, + value: 5, + }, + { + column: 1, + row: 0, + table: data.tables.first, + value: 2, + }, + ], }); }); @@ -695,7 +704,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -713,7 +723,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -734,7 +745,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component).toMatchSnapshot(); @@ -753,7 +765,8 @@ describe('xy_expression', () => { timeZone="CEST" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(LineSeries).prop('timeZone')).toEqual('CEST'); @@ -771,7 +784,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(BarSeries).prop('enableHistogramMode')).toEqual(true); @@ -796,7 +810,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(BarSeries).prop('enableHistogramMode')).toEqual(true); @@ -815,7 +830,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(BarSeries).prop('enableHistogramMode')).toEqual(false); @@ -876,7 +892,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); }; @@ -1071,7 +1088,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(LineSeries).prop('xScaleType')).toEqual(ScaleType.Ordinal); @@ -1088,7 +1106,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(component.find(LineSeries).prop('yScaleType')).toEqual(ScaleType.Sqrt); @@ -1105,7 +1124,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -1123,7 +1143,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -1141,7 +1162,8 @@ describe('xy_expression', () => { chartTheme={{}} histogramBarTarget={50} timeZone="UTC" - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); expect(getFormatSpy).toHaveBeenCalledWith({ @@ -1161,7 +1183,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -1248,7 +1271,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); @@ -1302,7 +1326,8 @@ describe('xy_expression', () => { timeZone="UTC" chartTheme={{}} histogramBarTarget={50} - executeTriggerActions={executeTriggerActions} + onClickValue={onClickValue} + onSelectRange={onSelectRange} /> ); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx index e35ad9ff214e0..f886bdfee3832 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; import moment from 'moment'; import { @@ -22,6 +22,7 @@ import { import { I18nProvider } from '@kbn/i18n/react'; import { ExpressionFunctionDefinition, + ExpressionRenderDefinition, ExpressionValueSearchContext, } from 'src/plugins/expressions/public'; import { EuiIcon, EuiText, IconType, EuiSpacer } from '@elastic/eui'; @@ -31,12 +32,7 @@ import { ValueClickTriggerContext, RangeSelectTriggerContext, } from '../../../../../src/plugins/embeddable/public'; -import { - LensMultiTable, - FormatFactory, - LensExpressionRenderDefinition, - ILensInterpreterRenderHandlers, -} from '../types'; +import { LensMultiTable, FormatFactory, ILensInterpreterRenderHandlers } from '../types'; import { XYArgs, SeriesType, visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart } from './state_helpers'; @@ -68,8 +64,8 @@ type XYChartRenderProps = XYChartProps & { formatFactory: FormatFactory; timeZone: string; histogramBarTarget: number; - onClickValue: (context: TriggerContext) => void; - onSelectRange: (context: TriggerContext) => void; + onClickValue: (data: TriggerContext['data']) => void; + onSelectRange: (data: TriggerContext['data']) => void; }; export const xyChart: ExpressionFunctionDefinition< @@ -123,7 +119,7 @@ export const getXyChartRenderer = (dependencies: { chartTheme: PartialTheme; histogramBarTarget: number; timeZone: string; -}): LensExpressionRenderDefinition => ({ +}): ExpressionRenderDefinition => ({ name: 'lens_xy_chart_renderer', displayName: 'XY chart', help: i18n.translate('xpack.lens.xyChart.renderer.help', { @@ -137,11 +133,11 @@ export const getXyChartRenderer = (dependencies: { handlers: ILensInterpreterRenderHandlers ) => { handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - const onClickValue = (context: TriggerContext) => { - handlers.event({ name: VALUE_CLICK_TRIGGER, data: context }); + const onClickValue = (data: TriggerContext['data']) => { + handlers.event({ name: 'filter', data }); }; - const onSelectRange = (context: TriggerContext) => { - handlers.event({ name: SELECT_RANGE_TRIGGER, data: context }); + const onSelectRange = (data: TriggerContext['data']) => { + handlers.event({ name: 'brush', data }); }; const formatFactory = await dependencies.formatFactory; ReactDOM.render( @@ -325,12 +321,10 @@ export function XYChart({ ); const timeFieldName = table.columns[xAxisColumnIndex]?.meta?.aggConfigParams?.field; - const context: RangeSelectTriggerContext = { - data: { - range: [min, max], - table, - column: xAxisColumnIndex, - }, + const context: RangeSelectTriggerContext['data'] = { + range: [min, max], + table, + column: xAxisColumnIndex, timeFieldName, }; onSelectRange(context); @@ -375,15 +369,13 @@ export function XYChart({ ?.aggConfigParams?.field; const timeFieldName = xDomain && xAxisFieldName; - const context: ValueClickTriggerContext = { - data: { - data: points.map(point => ({ - row: point.row, - column: point.column, - value: point.value, - table, - })), - }, + const context: ValueClickTriggerContext['data'] = { + data: points.map(point => ({ + row: point.row, + column: point.column, + value: point.value, + table, + })), timeFieldName, }; onClickValue(context); From a4414e27123b93adb19bd5b7a6e1de1fbad36b7f Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 7 May 2020 14:49:13 +0200 Subject: [PATCH 3/6] fix bugs --- .../drilldown.tsx | 4 +- .../datatable_visualization/expression.tsx | 15 +++-- .../editor_frame/state_management.test.ts | 8 +++ .../embeddable/embeddable.tsx | 1 + .../lens/public/pie_visualization/index.ts | 7 +- .../pie_visualization/register_expression.tsx | 9 +-- .../render_function.test.tsx | 56 +++++++++++++++- .../pie_visualization/render_function.tsx | 10 ++- .../pie_visualization/render_helpers.test.ts | 66 +++++++++---------- .../pie_visualization/render_helpers.ts | 18 +++-- x-pack/plugins/lens/public/plugin.ts | 6 +- .../lens/public/xy_visualization/index.ts | 7 +- .../public/xy_visualization/xy_expression.tsx | 27 ++++---- 13 files changed, 139 insertions(+), 95 deletions(-) diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx index 848e77384f7f0..0c0ed08096511 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx @@ -131,9 +131,9 @@ export class DashboardToDashboardDrilldown } })(); - if (context.timeFieldName) { + if (context.data.timeFieldName) { const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter( - context.timeFieldName, + context.data.timeFieldName, filtersFromEvent ); filtersFromEvent = restOfFilters; diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 0e22773ee6abd..3be5c72d2af37 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -10,14 +10,17 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import { EuiBasicTable, EuiFlexGroup, EuiButtonIcon, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { IAggType } from 'src/plugins/data/public'; -import { FormatFactory, ILensInterpreterRenderHandlers, LensMultiTable } from '../types'; +import { + FormatFactory, + ILensInterpreterRenderHandlers, + LensFilterEvent, + LensMultiTable, +} from '../types'; import { ExpressionFunctionDefinition, ExpressionRenderDefinition, } from '../../../../../src/plugins/expressions/public'; import { VisualizationContainer } from '../visualization_container'; -import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public'; -import { TriggerContext, VALUE_CLICK_TRIGGER } from '../../../../../src/plugins/ui_actions/public'; export interface DatatableColumns { columnIds: string[]; } @@ -34,7 +37,7 @@ export interface DatatableProps { type DatatableRenderProps = DatatableProps & { formatFactory: FormatFactory; - onClickValue: (data: TriggerContext['data']) => void; + onClickValue: (data: LensFilterEvent['data']) => void; getType: (name: string) => IAggType; }; @@ -126,7 +129,7 @@ export const getDatatableRenderer = (dependencies: { ) => { const resolvedFormatFactory = await dependencies.formatFactory; const resolvedGetType = await dependencies.getType; - const onClickValue = (data: TriggerContext['data']) => { + const onClickValue = (data: LensFilterEvent['data']) => { handlers.event({ name: 'filter', data }); }; ReactDOM.render( @@ -161,7 +164,7 @@ export function DatatableComponent(props: DatatableRenderProps) { const timeFieldName = negate && isDateHistogram ? undefined : col?.meta?.aggConfigParams?.field; const rowIndex = firstTable.rows.findIndex(row => row[field] === value); - const data: ValueClickTriggerContext['data'] = { + const data: LensFilterEvent['data'] = { negate, data: [ { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts index 1f62929783b63..71aabaae3c65c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts @@ -9,6 +9,9 @@ import { EditorFrameProps } from './index'; import { Datasource, Visualization } from '../../types'; import { createExpressionRendererMock } from '../mocks'; import { coreMock } from 'src/core/public/mocks'; +import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks'; +import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; +import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks'; describe('editor_frame state management', () => { describe('initialization', () => { @@ -24,6 +27,11 @@ describe('editor_frame state management', () => { ExpressionRenderer: createExpressionRendererMock(), onChange: jest.fn(), core: coreMock.createSetup(), + plugins: { + uiActions: uiActionsPluginMock.createStartContract(), + data: dataPluginMock.createStartContract(), + expressions: expressionsPluginMock.createStartContract(), + }, dateRange: { fromDate: 'now-7d', toDate: 'now' }, query: { query: '', language: 'lucene' }, filters: [], diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 6e8ebec3268d9..f2a6457cca999 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -106,6 +106,7 @@ export class Embeddable extends AbstractEmbeddable { - const executeTriggerActions = getExecuteTriggerActions(); + const onClickValue = (data: LensFilterEvent['data']) => { + handlers.event({ name: 'filter', data }); + }; const formatFactory = await dependencies.formatFactory; ReactDOM.render( , domNode, () => { diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx index bdc8004540bae..2d7cb6e2f5e34 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { Settings } from '@elastic/charts'; +import { SeriesIdentifier, Settings } from '@elastic/charts'; import { shallow } from 'enzyme'; import { LensMultiTable } from '../types'; import { PieComponent } from './render_function'; @@ -58,7 +58,7 @@ describe('PieVisualization component', () => { formatFactory: getFormatSpy, isDarkMode: false, chartTheme: {}, - executeTriggerActions: jest.fn(), + onClickValue: jest.fn(), }; } @@ -109,5 +109,57 @@ describe('PieVisualization component', () => { ); expect(component.find(Settings).prop('legendMaxDepth')).toBeUndefined(); }); + + test('it calls filter callback with the given context', () => { + const defaultArgs = getDefaultArgs(); + const component = shallow(); + component + .find(Settings) + .first() + .prop('onElementClick')!([[[{ groupByRollup: 6, value: 6 }], {} as SeriesIdentifier]]); + + expect(defaultArgs.onClickValue.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "data": Array [ + Object { + "column": 0, + "row": 0, + "table": Object { + "columns": Array [ + Object { + "id": "a", + "name": "a", + }, + Object { + "id": "b", + "name": "b", + }, + Object { + "id": "c", + "name": "c", + }, + ], + "rows": Array [ + Object { + "a": 6, + "b": 2, + "c": "I", + "d": "Row 1", + }, + Object { + "a": 1, + "b": 5, + "c": "J", + "d": "Row 2", + }, + ], + "type": "kibana_datatable", + }, + "value": 6, + }, + ], + } + `); + }); }); }); diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index 0c27a3e4b44e3..82ff1e62d9a3d 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -24,12 +24,10 @@ import { RecursivePartial, LayerValue, } from '@elastic/charts'; -import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public'; -import { FormatFactory } from '../types'; +import { FormatFactory, LensFilterEvent } from '../types'; import { VisualizationContainer } from '../visualization_container'; import { CHART_NAMES, DEFAULT_PERCENT_DECIMALS } from './constants'; import { ColumnGroups, PieExpressionProps } from './types'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { getSliceValueWithFallback, getFilterContext } from './render_helpers'; import './visualization.scss'; @@ -42,13 +40,13 @@ export function PieComponent( formatFactory: FormatFactory; chartTheme: Exclude; isDarkMode: boolean; - executeTriggerActions: UiActionsStart['executeTriggerActions']; + onClickValue: (data: LensFilterEvent['data']) => void; } ) { const [firstTable] = Object.values(props.data.tables); const formatters: Record> = {}; - const { chartTheme, isDarkMode, executeTriggerActions } = props; + const { chartTheme, isDarkMode, onClickValue } = props; const { shape, groups, @@ -243,7 +241,7 @@ export function PieComponent( firstTable ); - executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context); + onClickValue(context); }} /> { ], }; expect(getFilterContext([{ groupByRollup: 'Test', value: 100 }], ['a'], table)).toEqual({ - data: { - data: [ - { - row: 1, - column: 0, - value: 'Test', - table, - }, - ], - }, + data: [ + { + row: 1, + column: 0, + value: 'Test', + table, + }, + ], }); }); @@ -124,16 +122,14 @@ describe('render helpers', () => { ], }; expect(getFilterContext([{ groupByRollup: 'Test', value: 100 }], ['a', 'b'], table)).toEqual({ - data: { - data: [ - { - row: 1, - column: 0, - value: 'Test', - table, - }, - ], - }, + data: [ + { + row: 1, + column: 0, + value: 'Test', + table, + }, + ], }); }); @@ -161,22 +157,20 @@ describe('render helpers', () => { table ) ).toEqual({ - data: { - data: [ - { - row: 1, - column: 0, - value: 'Test', - table, - }, - { - row: 1, - column: 1, - value: 'Two', - table, - }, - ], - }, + data: [ + { + row: 1, + column: 0, + value: 'Test', + table, + }, + { + row: 1, + column: 1, + value: 'Two', + table, + }, + ], }); }); }); diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts index bc3c29ba0fff1..3f7494661c049 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts +++ b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts @@ -6,8 +6,8 @@ import { Datum, LayerValue } from '@elastic/charts'; import { KibanaDatatable, KibanaDatatableColumn } from 'src/plugins/expressions/public'; -import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public'; import { ColumnGroups } from './types'; +import { LensFilterEvent } from '../types'; export function getSliceValueWithFallback( d: Datum, @@ -28,7 +28,7 @@ export function getFilterContext( clickedLayers: LayerValue[], layerColumnIds: string[], table: KibanaDatatable -): ValueClickTriggerContext { +): LensFilterEvent['data'] { const matchingIndex = table.rows.findIndex(row => clickedLayers.every((layer, index) => { const columnId = layerColumnIds[index]; @@ -37,13 +37,11 @@ export function getFilterContext( ); return { - data: { - data: clickedLayers.map((clickedLayer, index) => ({ - column: table.columns.findIndex(col => col.id === layerColumnIds[index]), - row: matchingIndex, - value: clickedLayer.groupByRollup, - table, - })), - }, + data: clickedLayers.map((clickedLayer, index) => ({ + column: table.columns.findIndex(col => col.id === layerColumnIds[index]), + row: matchingIndex, + value: clickedLayer.groupByRollup, + table, + })), }; } diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index a8ec525604977..5c0de56ec6eff 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -97,9 +97,9 @@ export class LensPlugin { start(core: CoreStart, startDependencies: LensPluginStartDependencies) { this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance; - this.xyVisualization.start(core, startDependencies); - this.datatableVisualization.start(core, startDependencies); - this.pieVisualization.start(core, startDependencies); + this.xyVisualization.start(); + this.datatableVisualization.start(); + this.pieVisualization.start(); } stop() { diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index 1c62cff2f414b..c0eec48b91a29 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -5,14 +5,13 @@ */ import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; -import { CoreSetup, IUiSettingsClient, CoreStart } from 'kibana/public'; +import { CoreSetup, IUiSettingsClient } from 'kibana/public'; import moment from 'moment-timezone'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; import { xyVisualization } from './xy_visualization'; import { xyChart, getXyChartRenderer } from './xy_expression'; import { legendConfig, xConfig, layerConfig } from './types'; import { EditorFrameSetup, FormatFactory } from '../types'; -import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; export interface XyVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; @@ -20,10 +19,6 @@ export interface XyVisualizationPluginSetupPlugins { editorFrame: EditorFrameSetup; } -interface XyVisualizationPluginStartPlugins { - uiActions: UiActionsStart; -} - function getTimeZone(uiSettings: IUiSettingsClient) { const configuredTimeZone = uiSettings.get('dateFormat:tz'); if (configuredTimeZone === 'Browser') { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx index f886bdfee3832..15173a74d1b91 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -29,18 +29,15 @@ import { EuiIcon, EuiText, IconType, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { - ValueClickTriggerContext, - RangeSelectTriggerContext, -} from '../../../../../src/plugins/embeddable/public'; -import { LensMultiTable, FormatFactory, ILensInterpreterRenderHandlers } from '../types'; + LensMultiTable, + FormatFactory, + ILensInterpreterRenderHandlers, + LensFilterEvent, + LensBrushEvent, +} from '../types'; import { XYArgs, SeriesType, visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart } from './state_helpers'; -import { - VALUE_CLICK_TRIGGER, - SELECT_RANGE_TRIGGER, - TriggerContext, -} from '../../../../../src/plugins/ui_actions/public'; import { parseInterval } from '../../../../../src/plugins/data/common'; type InferPropType = T extends React.FunctionComponent ? P : T; @@ -64,8 +61,8 @@ type XYChartRenderProps = XYChartProps & { formatFactory: FormatFactory; timeZone: string; histogramBarTarget: number; - onClickValue: (data: TriggerContext['data']) => void; - onSelectRange: (data: TriggerContext['data']) => void; + onClickValue: (data: LensFilterEvent['data']) => void; + onSelectRange: (data: LensBrushEvent['data']) => void; }; export const xyChart: ExpressionFunctionDefinition< @@ -133,10 +130,10 @@ export const getXyChartRenderer = (dependencies: { handlers: ILensInterpreterRenderHandlers ) => { handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - const onClickValue = (data: TriggerContext['data']) => { + const onClickValue = (data: LensFilterEvent['data']) => { handlers.event({ name: 'filter', data }); }; - const onSelectRange = (data: TriggerContext['data']) => { + const onSelectRange = (data: LensBrushEvent['data']) => { handlers.event({ name: 'brush', data }); }; const formatFactory = await dependencies.formatFactory; @@ -321,7 +318,7 @@ export function XYChart({ ); const timeFieldName = table.columns[xAxisColumnIndex]?.meta?.aggConfigParams?.field; - const context: RangeSelectTriggerContext['data'] = { + const context: LensBrushEvent['data'] = { range: [min, max], table, column: xAxisColumnIndex, @@ -369,7 +366,7 @@ export function XYChart({ ?.aggConfigParams?.field; const timeFieldName = xDomain && xAxisFieldName; - const context: ValueClickTriggerContext['data'] = { + const context: LensFilterEvent['data'] = { data: points.map(point => ({ row: point.row, column: point.column, From 4bece3544f0a55e45104f2f0a367a2384b5a406b Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 7 May 2020 18:11:39 +0200 Subject: [PATCH 4/6] fix drilldown usage of trigger contexts --- .../drilldown.test.tsx | 10 ++++++---- .../editor_frame/workspace_panel.tsx | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx index 18ee95cb57b3b..b3e10a05fc414 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx @@ -138,10 +138,12 @@ describe('.execute() & getHref', () => { }; const context = ({ - data: useRangeEvent - ? ({ range: {} } as RangeSelectTriggerContext['data']) - : ({ data: [] } as ValueClickTriggerContext['data']), - timeFieldName: 'order_date', + data: { + ...(useRangeEvent + ? ({ range: {} } as RangeSelectTriggerContext['data']) + : ({ data: [] } as ValueClickTriggerContext['data'])), + timeFieldName: 'order_date', + }, embeddable: { getInput: () => ({ filters: [], diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx index a7145482ca687..b000fc7fa0176 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect, useMemo, useContext, useCallback } from 'react'; +import React, { useState, useEffect, useMemo, useContext } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { From d50f53b24aa6d5b0f54715d442f5cdee7cd61266 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 12 May 2020 15:45:46 +0200 Subject: [PATCH 5/6] remove unused lifecycle methods --- x-pack/plugins/lens/public/datatable_visualization/index.ts | 1 - x-pack/plugins/lens/public/pie_visualization/index.ts | 4 ---- x-pack/plugins/lens/public/plugin.ts | 3 --- x-pack/plugins/lens/public/xy_visualization/index.ts | 1 - 4 files changed, 9 deletions(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/index.ts b/x-pack/plugins/lens/public/datatable_visualization/index.ts index 3ded70b6a8746..5cc3c40591c3f 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/index.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/index.ts @@ -41,5 +41,4 @@ export class DatatableVisualization { ); editorFrame.registerVisualization(datatableVisualization); } - start() {} } diff --git a/x-pack/plugins/lens/public/pie_visualization/index.ts b/x-pack/plugins/lens/public/pie_visualization/index.ts index 92f1285de768f..dd828c6c35300 100644 --- a/x-pack/plugins/lens/public/pie_visualization/index.ts +++ b/x-pack/plugins/lens/public/pie_visualization/index.ts @@ -43,8 +43,4 @@ export class PieVisualization { editorFrame.registerVisualization(pieVisualization); } - - start() {} - - stop() {} } diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 5c0de56ec6eff..1ff677f042395 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -97,9 +97,6 @@ export class LensPlugin { start(core: CoreStart, startDependencies: LensPluginStartDependencies) { this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance; - this.xyVisualization.start(); - this.datatableVisualization.start(); - this.pieVisualization.start(); } stop() { diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index c0eec48b91a29..23cf9e7ff818f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -53,5 +53,4 @@ export class XyVisualization { editorFrame.registerVisualization(xyVisualization); } - start() {} } From c965fa469382b4cb12fa584506ca0589444400f6 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 14 May 2020 17:38:21 +0200 Subject: [PATCH 6/6] fix type error --- .../public/editor_frame_service/embeddable/embeddable.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index d6056e89acd3b..4e5b32ad7f7a3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -128,6 +128,7 @@ describe('embeddable', () => { expressionRenderer, getTrigger, { + editPath: '', editUrl: '', editable: true, savedVis,