From e411757b111a3e5d842baeba2ebc4324fc4d5461 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 9 Nov 2020 14:46:30 +0100 Subject: [PATCH] [7.x] [Lens] Expose active data in some places (#79851) (#82856) --- ...ressions-public.reactexpressionrenderer.md | 2 +- ...ons-public.reactexpressionrendererprops.md | 1 + ...ic.reactexpressionrendererprops.ondata_.md | 11 ++++ src/plugins/expressions/public/public.api.md | 4 +- .../public/react_expression_renderer.test.tsx | 32 ++++++++++ .../public/react_expression_renderer.tsx | 9 +++ .../editor_frame/config_panel/layer_panel.tsx | 3 + .../editor_frame/editor_frame.tsx | 1 + .../editor_frame/state_management.ts | 11 ++++ .../editor_frame/suggestion_helpers.ts | 6 +- .../editor_frame/suggestion_panel.tsx | 1 + .../workspace_panel/chart_switch.tsx | 1 + .../workspace_panel/workspace_panel.test.tsx | 42 ++++++++++++++ .../workspace_panel/workspace_panel.tsx | 17 ++++++ .../editor_frame_service/merge_tables.test.ts | 58 +++++++++++-------- .../editor_frame_service/merge_tables.ts | 18 +++++- .../lens/public/editor_frame_service/types.ts | 12 ++++ x-pack/plugins/lens/public/types.ts | 13 ++++- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 20 files changed, 211 insertions(+), 33 deletions(-) create mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.ondata_.md create mode 100644 x-pack/plugins/lens/public/editor_frame_service/types.ts diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md index 32a7151578658..8cc32ff698b38 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md @@ -7,5 +7,5 @@ Signature: ```typescript -ReactExpressionRenderer: ({ className, dataAttrs, padding, renderError, expression, onEvent, reload$, debounce, ...expressionLoaderOptions }: ReactExpressionRendererProps) => JSX.Element +ReactExpressionRenderer: ({ className, dataAttrs, padding, renderError, expression, onEvent, onData$, reload$, debounce, ...expressionLoaderOptions }: ReactExpressionRendererProps) => JSX.Element ``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md index e4980ce04b9e2..92ea071b23dfc 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md @@ -18,6 +18,7 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams | [dataAttrs](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.dataattrs.md) | string[] | | | [debounce](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.debounce.md) | number | | | [expression](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.expression.md) | string | ExpressionAstExpression | | +| [onData$](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.ondata_.md) | <TData, TInspectorAdapters>(data: TData, adapters?: TInspectorAdapters) => void | | | [onEvent](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.onevent.md) | (event: ExpressionRendererEvent) => void | | | [padding](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.padding.md) | 'xs' | 's' | 'm' | 'l' | 'xl' | | | [reload$](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.reload_.md) | Observable<unknown> | An observable which can be used to re-run the expression without destroying the component | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.ondata_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.ondata_.md new file mode 100644 index 0000000000000..05ddb0b13a5be --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.ondata_.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) > [onData$](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.ondata_.md) + +## ReactExpressionRendererProps.onData$ property + +Signature: + +```typescript +onData$?: (data: TData, adapters?: TInspectorAdapters) => void; +``` diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index e6bff703aadcd..773d61ebe2e28 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -1057,7 +1057,7 @@ export interface Range { // Warning: (ae-missing-release-tag) "ReactExpressionRenderer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const ReactExpressionRenderer: ({ className, dataAttrs, padding, renderError, expression, onEvent, reload$, debounce, ...expressionLoaderOptions }: ReactExpressionRendererProps) => JSX.Element; +export const ReactExpressionRenderer: ({ className, dataAttrs, padding, renderError, expression, onEvent, onData$, reload$, debounce, ...expressionLoaderOptions }: ReactExpressionRendererProps) => JSX.Element; // Warning: (ae-missing-release-tag) "ReactExpressionRendererProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1072,6 +1072,8 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams { // (undocumented) expression: string | ExpressionAstExpression; // (undocumented) + onData$?: (data: TData, adapters?: TInspectorAdapters) => void; + // (undocumented) onEvent?: (event: ExpressionRendererEvent) => void; // (undocumented) padding?: 'xs' | 's' | 'm' | 'l' | 'xl'; diff --git a/src/plugins/expressions/public/react_expression_renderer.test.tsx b/src/plugins/expressions/public/react_expression_renderer.test.tsx index 052c2a9f6a24a..e52d4d153882f 100644 --- a/src/plugins/expressions/public/react_expression_renderer.test.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.test.tsx @@ -195,6 +195,38 @@ describe('ExpressionRenderer', () => { expect(instance.find('[data-test-subj="custom-error"]')).toHaveLength(0); }); + it('should call onData$ prop on every data$ observable emission in loader', () => { + const dataSubject = new Subject(); + const data$ = dataSubject.asObservable().pipe(share()); + + const newData = {}; + const inspectData = {}; + const onData$ = jest.fn(); + + (ExpressionLoader as jest.Mock).mockImplementation(() => { + return { + render$: new Subject(), + data$, + loading$: new Subject(), + events$: new Subject(), + update: jest.fn(), + inspect: jest.fn(() => inspectData), + }; + }); + + mount(); + + expect(onData$).toHaveBeenCalledTimes(0); + + act(() => { + dataSubject.next(newData); + }); + + expect(onData$).toHaveBeenCalledTimes(1); + expect(onData$.mock.calls[0][0]).toBe(newData); + expect(onData$.mock.calls[0][1]).toBe(inspectData); + }); + it('should fire onEvent prop on every events$ observable emission in loader', () => { const dataSubject = new Subject(); const data$ = dataSubject.asObservable().pipe(share()); diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx index fecebf36ab7e6..894325c8b65f7 100644 --- a/src/plugins/expressions/public/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.tsx @@ -41,6 +41,7 @@ export interface ReactExpressionRendererProps extends IExpressionLoaderParams { ) => React.ReactElement | React.ReactElement[]; padding?: 'xs' | 's' | 'm' | 'l' | 'xl'; onEvent?: (event: ExpressionRendererEvent) => void; + onData$?: (data: TData, adapters?: TInspectorAdapters) => void; /** * An observable which can be used to re-run the expression without destroying the component */ @@ -71,6 +72,7 @@ export const ReactExpressionRenderer = ({ renderError, expression, onEvent, + onData$, reload$, debounce, ...expressionLoaderOptions @@ -135,6 +137,13 @@ export const ReactExpressionRenderer = ({ }) ); } + if (onData$) { + subs.push( + expressionLoaderRef.current.data$.subscribe((newData) => { + onData$(newData, expressionLoaderRef.current?.inspect()); + }) + ); + } subs.push( expressionLoaderRef.current.loading$.subscribe(() => { hasHandledErrorRef.current = false; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 82f753e3520b0..c9d99bcfb6d8d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -93,6 +93,7 @@ export function LayerPanel( state: props.visualizationState, frame: props.framePublicAPI, dateRange: props.framePublicAPI.dateRange, + activeData: props.framePublicAPI.activeData, }; const datasourceId = datasourcePublicAPI.datasourceId; const layerDatasourceState = props.datasourceStates[datasourceId].state; @@ -111,6 +112,7 @@ export function LayerPanel( ...layerDatasourceDropProps, frame: props.framePublicAPI, dateRange: props.framePublicAPI.dateRange, + activeData: props.framePublicAPI.activeData, }; const { groups } = activeVisualization.getConfiguration(layerVisualizationConfigProps); @@ -140,6 +142,7 @@ export function LayerPanel( nativeProps={{ layerId, state: layerDatasourceState, + activeData: props.framePublicAPI.activeData, setState: (updater: unknown) => { const newState = typeof updater === 'function' ? updater(layerDatasourceState) : updater; 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 bb40b9f31d254..935d65bfb6c08 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 @@ -101,6 +101,7 @@ export function EditorFrame(props: EditorFrameProps) { const framePublicAPI: FramePublicAPI = { datasourceLayers, + activeData: state.activeData, dateRange: props.dateRange, query: props.query, filters: props.filters, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts index fc8daaed059dd..e0101493b27aa 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts @@ -6,6 +6,7 @@ import { EditorFrameProps } from './index'; import { Document } from '../../persistence/saved_object_store'; +import { TableInspectorAdapter } from '../types'; export interface PreviewState { visualization: { @@ -21,6 +22,7 @@ export interface EditorFrameState extends PreviewState { description?: string; stagedPreview?: PreviewState; activeDatasourceId: string | null; + activeData?: TableInspectorAdapter; } export type Action = @@ -32,6 +34,10 @@ export type Action = type: 'UPDATE_TITLE'; title: string; } + | { + type: 'UPDATE_ACTIVE_DATA'; + tables: TableInspectorAdapter; + } | { type: 'UPDATE_STATE'; // Just for diagnostics, so we can determine what action @@ -139,6 +145,11 @@ export const reducer = (state: EditorFrameState, action: Action): EditorFrameSta return { ...state, title: action.title }; case 'UPDATE_STATE': return action.updater(state); + case 'UPDATE_ACTIVE_DATA': + return { + ...state, + activeData: action.tables, + }; case 'UPDATE_LAYER': return { ...state, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts index 95057f9db7e93..daaf893f2a703 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts @@ -7,6 +7,7 @@ import _ from 'lodash'; import { Ast } from '@kbn/interpreter/common'; import { IconType } from '@elastic/eui/src/components/icon/icon'; +import { Datatable } from 'src/plugins/expressions'; import { PaletteOutput } from 'src/plugins/charts/public'; import { VisualizeFieldContext } from '../../../../../../src/plugins/ui_actions/public'; import { @@ -50,6 +51,7 @@ export function getSuggestions({ visualizationState, field, visualizeTriggerFieldContext, + activeData, mainPalette, }: { datasourceMap: Record; @@ -66,6 +68,7 @@ export function getSuggestions({ visualizationState: unknown; field?: unknown; visualizeTriggerFieldContext?: VisualizeFieldContext; + activeData?: Record; mainPalette?: PaletteOutput; }): Suggestion[] { const datasources = Object.entries(datasourceMap).filter( @@ -87,7 +90,8 @@ export function getSuggestions({ dataSourceSuggestions = datasource.getDatasourceSuggestionsForField(datasourceState, field); } else { dataSourceSuggestions = datasource.getDatasourceSuggestionsFromCurrentState( - datasourceState + datasourceState, + activeData ); } return dataSourceSuggestions.map((suggestion) => ({ ...suggestion, datasourceId })); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index 201c91ee91676..2e24b64ecca26 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -188,6 +188,7 @@ export function SuggestionPanel({ visualizationMap, activeVisualizationId: currentVisualizationId, visualizationState: currentVisualizationState, + activeData: frame.activeData, }) .filter((suggestion) => !suggestion.hide) .filter( diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx index fe8747de667a3..659626149aef2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx @@ -325,6 +325,7 @@ function getTopSuggestion( activeVisualizationId: props.visualizationId, visualizationState: props.visualizationState, subVisualizationId, + activeData: props.framePublicAPI.activeData, mainPalette, }); const suggestions = unfilteredSuggestions.filter((suggestion) => { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx index 447e94d09cdb2..231c38ea54048 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx @@ -253,6 +253,48 @@ describe('workspace_panel', () => { expect(trigger.exec).toHaveBeenCalledWith({ data: eventData }); }); + it('should push add current data table to state on data$ emitting value', () => { + const framePublicAPI = createMockFramePublicAPI(); + framePublicAPI.datasourceLayers = { + first: mockDatasource.publicAPIMock, + }; + mockDatasource.toExpression.mockReturnValue('datasource'); + mockDatasource.getLayers.mockReturnValue(['first']); + const dispatch = jest.fn(); + + instance = mount( + 'vis' }, + }} + visualizationState={{}} + dispatch={dispatch} + ExpressionRenderer={expressionRendererMock} + core={coreMock.createSetup()} + plugins={{ uiActions: uiActionsMock, data: dataMock }} + /> + ); + + const onData = expressionRendererMock.mock.calls[0][0].onData$!; + + const tableData = { table1: { columns: [], rows: [] } }; + onData(undefined, { tables: tableData }); + + expect(dispatch).toHaveBeenCalledWith({ type: 'UPDATE_ACTIVE_DATA', tables: tableData }); + }); + it('should include data fetching for each layer in the expression', () => { const mockDatasource2 = createMockDatasource('a'); const framePublicAPI = createMockFramePublicAPI(); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index 5a6e9af5d6ff2..e0dd3b3fe01ae 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -50,6 +50,7 @@ import { } from '../../../../../../../src/plugins/data/public'; import { WorkspacePanelWrapper } from './workspace_panel_wrapper'; import { DropIllustration } from '../../../assets/drop_illustration'; +import { LensInspectorAdapters } from '../../types'; import { getOriginalRequestErrorMessage } from '../../error_helper'; import { validateDatasourceAndVisualization } from '../state_helpers'; @@ -296,6 +297,7 @@ export function WorkspacePanel({ expression={expression} framePublicAPI={framePublicAPI} timefilter={plugins.data.query.timefilter.timefilter} + dispatch={dispatch} onEvent={onEvent} setLocalState={setLocalState} localState={{ ...localState, configurationValidationError }} @@ -339,11 +341,13 @@ export const InnerVisualizationWrapper = ({ setLocalState, localState, ExpressionRendererComponent, + dispatch, }: { expression: Ast | null | undefined; framePublicAPI: FramePublicAPI; timefilter: TimefilterContract; onEvent: (event: ExpressionRendererEvent) => void; + dispatch: (action: Action) => void; setLocalState: (dispatch: (prevState: WorkspaceState) => WorkspaceState) => void; localState: WorkspaceState & { configurationValidationError?: Array<{ shortMessage: string; longMessage: string }>; @@ -369,6 +373,18 @@ export const InnerVisualizationWrapper = ({ ] ); + const onData$ = useCallback( + (data: unknown, inspectorAdapters?: LensInspectorAdapters) => { + if (inspectorAdapters && inspectorAdapters.tables) { + dispatch({ + type: 'UPDATE_ACTIVE_DATA', + tables: inspectorAdapters.tables, + }); + } + }, + [dispatch] + ); + if (localState.configurationValidationError) { let showExtraErrors = null; if (localState.configurationValidationError.length > 1) { @@ -455,6 +471,7 @@ export const InnerVisualizationWrapper = ({ searchContext={context} reload$={autoRefreshFetch$} onEvent={onEvent} + onData$={onData$} renderError={(errorMessage?: string | null, error?: ExpressionRenderError | null) => { const visibleErrorMessage = getOriginalRequestErrorMessage(error) || errorMessage; diff --git a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts b/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts index 5afabb9a52367..07c16665d11b4 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts @@ -6,34 +6,35 @@ import moment from 'moment'; import { mergeTables } from './merge_tables'; -import { Datatable } from 'src/plugins/expressions'; +import { Datatable, ExecutionContext } from 'src/plugins/expressions'; +import { LensInspectorAdapters } from './types'; describe('lens_merge_tables', () => { - it('should produce a row with the nested table as defined', () => { - const sampleTable1: Datatable = { - type: 'datatable', - columns: [ - { id: 'bucket', name: 'A', meta: { type: 'string' } }, - { id: 'count', name: 'Count', meta: { type: 'number' } }, - ], - rows: [ - { bucket: 'a', count: 5 }, - { bucket: 'b', count: 10 }, - ], - }; + const sampleTable1: Datatable = { + type: 'datatable', + columns: [ + { id: 'bucket', name: 'A', meta: { type: 'string' } }, + { id: 'count', name: 'Count', meta: { type: 'number' } }, + ], + rows: [ + { bucket: 'a', count: 5 }, + { bucket: 'b', count: 10 }, + ], + }; - const sampleTable2: Datatable = { - type: 'datatable', - columns: [ - { id: 'bucket', name: 'C', meta: { type: 'string' } }, - { id: 'avg', name: 'Average', meta: { type: 'number' } }, - ], - rows: [ - { bucket: 'a', avg: 2.5 }, - { bucket: 'b', avg: 9 }, - ], - }; + const sampleTable2: Datatable = { + type: 'datatable', + columns: [ + { id: 'bucket', name: 'C', meta: { type: 'string' } }, + { id: 'avg', name: 'Average', meta: { type: 'number' } }, + ], + rows: [ + { bucket: 'a', avg: 2.5 }, + { bucket: 'b', avg: 9 }, + ], + }; + it('should produce a row with the nested table as defined', () => { expect( mergeTables.fn( null, @@ -47,6 +48,15 @@ describe('lens_merge_tables', () => { }); }); + it('should store the current tables in the tables inspector', () => { + const adapters: LensInspectorAdapters = { tables: {} }; + mergeTables.fn(null, { layerIds: ['first', 'second'], tables: [sampleTable1, sampleTable2] }, { + inspectorAdapters: adapters, + } as ExecutionContext); + expect(adapters.tables!.first).toBe(sampleTable1); + expect(adapters.tables!.second).toBe(sampleTable2); + }); + it('should pass the date range along', () => { expect( mergeTables.fn( diff --git a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts b/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts index e4f7b07084ea9..03ef7cf9cc637 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import { + ExecutionContext, Datatable, ExpressionFunctionDefinition, ExpressionValueSearchContext, @@ -14,6 +15,7 @@ import { search } from '../../../../../src/plugins/data/public'; const { toAbsoluteDates } = search.aggs; import { LensMultiTable } from '../types'; +import { LensInspectorAdapters } from './types'; interface MergeTables { layerIds: string[]; @@ -24,12 +26,14 @@ export const mergeTables: ExpressionFunctionDefinition< 'lens_merge_tables', ExpressionValueSearchContext | null, MergeTables, - LensMultiTable + LensMultiTable, + ExecutionContext > = { name: 'lens_merge_tables', type: 'lens_multitable', help: i18n.translate('xpack.lens.functions.mergeTables.help', { - defaultMessage: 'A helper to merge any number of kibana tables into a single table', + defaultMessage: + 'A helper to merge any number of kibana tables into a single table and expose it via inspector adapter', }), args: { layerIds: { @@ -44,10 +48,18 @@ export const mergeTables: ExpressionFunctionDefinition< }, }, inputTypes: ['kibana_context', 'null'], - fn(input, { layerIds, tables }) { + fn(input, { layerIds, tables }, context) { + if (!context.inspectorAdapters) { + context.inspectorAdapters = {}; + } + if (!context.inspectorAdapters.tables) { + context.inspectorAdapters.tables = {}; + } const resultTables: Record = {}; tables.forEach((table, index) => { resultTables[layerIds[index]] = table; + // adapter is always defined at that point because we make sure by the beginning of the function + context.inspectorAdapters.tables![layerIds[index]] = table; }); return { type: 'lens_multitable', diff --git a/x-pack/plugins/lens/public/editor_frame_service/types.ts b/x-pack/plugins/lens/public/editor_frame_service/types.ts new file mode 100644 index 0000000000000..2da95ec2fd66f --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/types.ts @@ -0,0 +1,12 @@ +/* + * 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 { Datatable } from 'src/plugins/expressions'; + +export type TableInspectorAdapter = Record; +export interface LensInspectorAdapters { + tables?: TableInspectorAdapter; +} diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 27ab8f258bba8..3c96579fdc943 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -178,7 +178,10 @@ export interface Datasource { indexPatternId: string, fieldName: string ) => Array>; - getDatasourceSuggestionsFromCurrentState: (state: T) => Array>; + getDatasourceSuggestionsFromCurrentState: ( + state: T, + activeData?: Record + ) => Array>; getPublicAPI: (props: PublicAPIProps) => DatasourcePublicAPI; getErrorMessages: (state: T) => Array<{ shortMessage: string; longMessage: string }> | undefined; @@ -231,6 +234,7 @@ export type DatasourceDimensionProps = SharedDimensionProps & { columnId: string; onRemove?: (accessor: string) => void; state: T; + activeData?: Record; }; // The only way a visualization has to restrict the query building @@ -249,6 +253,7 @@ export interface DatasourceLayerPanelProps { layerId: string; state: T; setState: StateSetter; + activeData?: Record; } export interface DraggedOperation { @@ -428,6 +433,12 @@ export interface VisualizationSuggestion { export interface FramePublicAPI { datasourceLayers: Record; + /** + * Data of the chart currently rendered in the preview. + * This data might be not available (e.g. if the chart can't be rendered) or outdated and belonging to another chart. + * If accessing, make sure to check whether expected columns actually exist. + */ + activeData?: Record; dateRange: DateRange; query: Query; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8d45511e04338..07bbc4e14fa88 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10751,7 +10751,6 @@ "xpack.lens.fittingFunctionsTitle.lookahead": "次へ", "xpack.lens.fittingFunctionsTitle.none": "非表示", "xpack.lens.fittingFunctionsTitle.zero": "ゼロ", - "xpack.lens.functions.mergeTables.help": "いくつかの Kibana 表を 1 つの表に結合するのをアシストします", "xpack.lens.functions.renameColumns.help": "データベースの列の名前の変更をアシストします", "xpack.lens.functions.renameColumns.idMap.help": "キーが古い列 ID で値が対応する新しい列 ID となるように JSON エンコーディングされたオブジェクトです。他の列 ID はすべてのそのままです。", "xpack.lens.includeValueButtonAriaLabel": "{value}を含める", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 80e9bc51585e2..e6b94820e3d88 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10764,7 +10764,6 @@ "xpack.lens.fittingFunctionsTitle.lookahead": "下一", "xpack.lens.fittingFunctionsTitle.none": "隐藏", "xpack.lens.fittingFunctionsTitle.zero": "零", - "xpack.lens.functions.mergeTables.help": "将任何数目的 kibana 表合并成单个表的助手", "xpack.lens.functions.renameColumns.help": "用于重命名数据表列的助手", "xpack.lens.functions.renameColumns.idMap.help": "旧列 ID 为键且相应新列 ID 为值的 JSON 编码对象。所有其他列 ID 都将保留。", "xpack.lens.includeValueButtonAriaLabel": "包括 {value}",