[Lens] Inspect flyout should be available in editor mode. (#109656)

* [Lens] Inspect flyout should be available in editor mode.

* fix typo

* add test

* add functional tests for inspector

* toMatchInlineSnapshot -> toMatchSnapshot

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Alexey Antonov 2021-08-26 17:18:09 +03:00 committed by GitHub
parent 85e030249f
commit 3cc7da8435
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 351 additions and 68 deletions

View file

@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) &gt; [createDefaultInspectorAdapters](./kibana-plugin-plugins-expressions-public.createdefaultinspectoradapters.md)
## createDefaultInspectorAdapters variable
<b>Signature:</b>
```typescript
createDefaultInspectorAdapters: () => DefaultInspectorAdapters
```

View file

@ -84,6 +84,7 @@
| Variable | Description | | Variable | Description |
| --- | --- | | --- | --- |
| [createDefaultInspectorAdapters](./kibana-plugin-plugins-expressions-public.createdefaultinspectoradapters.md) | |
| [ReactExpressionRenderer](./kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md) | | | [ReactExpressionRenderer](./kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md) | |
## Type Aliases ## Type Aliases

View file

@ -25,7 +25,7 @@ import { Executor } from '../executor';
import { createExecutionContainer, ExecutionContainer } from './container'; import { createExecutionContainer, ExecutionContainer } from './container';
import { createError } from '../util'; import { createError } from '../util';
import { abortSignalToPromise, now } from '../../../kibana_utils/common'; import { abortSignalToPromise, now } from '../../../kibana_utils/common';
import { RequestAdapter, Adapters } from '../../../inspector/common'; import { Adapters } from '../../../inspector/common';
import { isExpressionValueError, ExpressionValueError } from '../expression_types/specs/error'; import { isExpressionValueError, ExpressionValueError } from '../expression_types/specs/error';
import { import {
ExpressionAstArgument, ExpressionAstArgument,
@ -42,8 +42,7 @@ import { ExpressionFunction } from '../expression_functions';
import { getByAlias } from '../util/get_by_alias'; import { getByAlias } from '../util/get_by_alias';
import { ExecutionContract } from './execution_contract'; import { ExecutionContract } from './execution_contract';
import { ExpressionExecutionParams } from '../service'; import { ExpressionExecutionParams } from '../service';
import { TablesAdapter } from '../util/tables_adapter'; import { createDefaultInspectorAdapters } from '../util/create_default_inspector_adapters';
import { ExpressionsInspectorAdapter } from '../util/expressions_inspector_adapter';
/** /**
* The result returned after an expression function execution. * The result returned after an expression function execution.
@ -90,12 +89,6 @@ export interface ExecutionParams {
params: ExpressionExecutionParams; params: ExpressionExecutionParams;
} }
const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({
requests: new RequestAdapter(),
tables: new TablesAdapter(),
expression: new ExpressionsInspectorAdapter(),
});
export class Execution< export class Execution<
Input = unknown, Input = unknown,
Output = unknown, Output = unknown,

View file

@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { RequestAdapter } from '../../../inspector/common';
import { TablesAdapter } from './tables_adapter';
import { ExpressionsInspectorAdapter } from './expressions_inspector_adapter';
import type { DefaultInspectorAdapters } from '../execution';
export const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({
requests: new RequestAdapter(),
tables: new TablesAdapter(),
expression: new ExpressionsInspectorAdapter(),
});

View file

@ -11,3 +11,4 @@ export * from './get_by_alias';
export * from './tables_adapter'; export * from './tables_adapter';
export * from './expressions_inspector_adapter'; export * from './expressions_inspector_adapter';
export * from './test_utils'; export * from './test_utils';
export * from './create_default_inspector_adapters';

View file

@ -108,4 +108,5 @@ export {
ExpressionsServiceStart, ExpressionsServiceStart,
TablesAdapter, TablesAdapter,
ExpressionsInspectorAdapter, ExpressionsInspectorAdapter,
createDefaultInspectorAdapters,
} from '../common'; } from '../common';

View file

@ -55,6 +55,12 @@ initialArgs: {
[K in keyof FunctionArgs<FnDef>]: FunctionArgs<FnDef>[K] | ExpressionAstExpressionBuilder | ExpressionAstExpressionBuilder[] | ExpressionAstExpression | ExpressionAstExpression[]; [K in keyof FunctionArgs<FnDef>]: FunctionArgs<FnDef>[K] | ExpressionAstExpressionBuilder | ExpressionAstExpressionBuilder[] | ExpressionAstExpression | ExpressionAstExpression[];
}): ExpressionAstFunctionBuilder<FnDef>; }): ExpressionAstFunctionBuilder<FnDef>;
// Warning: (ae-forgotten-export) The symbol "DefaultInspectorAdapters" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "createDefaultInspectorAdapters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const createDefaultInspectorAdapters: () => DefaultInspectorAdapters;
// Warning: (ae-missing-release-tag) "Datatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // Warning: (ae-missing-release-tag) "Datatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// //
// @public // @public
@ -95,7 +101,6 @@ export type DatatableRow = Record<string, any>;
// Warning: (ae-forgotten-export) The symbol "Adapters" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "Adapters" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "ExpressionExecutionParams" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ExpressionExecutionParams" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "DefaultInspectorAdapters" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "Execution" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // Warning: (ae-missing-release-tag) "Execution" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// //
// @public (undocumented) // @public (undocumented)

View file

@ -45,12 +45,12 @@ export class InspectorService extends FtrService {
/** /**
* Opens inspector panel * Opens inspector panel
*/ */
public async open(): Promise<void> { public async open(openButton: string = 'openInspectorButton'): Promise<void> {
this.log.debug('Inspector.open'); this.log.debug('Inspector.open');
const isOpen = await this.testSubjects.exists('inspectorPanel'); const isOpen = await this.testSubjects.exists('inspectorPanel');
if (!isOpen) { if (!isOpen) {
await this.retry.try(async () => { await this.retry.try(async () => {
await this.testSubjects.click('openInspectorButton'); await this.testSubjects.click(openButton);
await this.testSubjects.exists('inspectorPanel'); await this.testSubjects.exists('inspectorPanel');
}); });
} }

View file

@ -0,0 +1,70 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Lens App renders the editor frame 1`] = `
Array [
Array [
Object {
"lensInspector": Object {
"adapters": Object {
"expression": ExpressionsInspectorAdapter {
"_ast": Object {},
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
Symbol(kCapture): false,
},
"requests": RequestAdapter {
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
"requests": Map {},
Symbol(kCapture): false,
},
"tables": TablesAdapter {
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
"_tables": Object {},
Symbol(kCapture): false,
},
},
"inspect": [Function],
},
"showNoDataPopover": [Function],
},
Object {},
],
Array [
Object {
"lensInspector": Object {
"adapters": Object {
"expression": ExpressionsInspectorAdapter {
"_ast": Object {},
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
Symbol(kCapture): false,
},
"requests": RequestAdapter {
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
"requests": Map {},
Symbol(kCapture): false,
},
"tables": TablesAdapter {
"_events": Object {},
"_eventsCount": 0,
"_maxListeners": undefined,
"_tables": Object {},
Symbol(kCapture): false,
},
},
"inspect": [Function],
},
"showNoDataPopover": [Function],
},
Object {},
],
]
`;

View file

@ -140,16 +140,7 @@ describe('Lens App', () => {
it('renders the editor frame', async () => { it('renders the editor frame', async () => {
const { frame } = await mountWith({}); const { frame } = await mountWith({});
expect(frame.EditorFrameContainer.mock.calls).toMatchInlineSnapshot(` expect(frame.EditorFrameContainer.mock.calls).toMatchSnapshot();
Array [
Array [
Object {
"showNoDataPopover": [Function],
},
Object {},
],
]
`);
}); });
it('updates global filters with store state', async () => { it('updates global filters with store state', async () => {
@ -772,6 +763,37 @@ describe('Lens App', () => {
}); });
}); });
describe('inspector', () => {
function getButton(inst: ReactWrapper): TopNavMenuData {
return (inst
.find('[data-test-subj="lnsApp_topNav"]')
.prop('config') as TopNavMenuData[]).find(
(button) => button.testId === 'lnsApp_inspectButton'
)!;
}
async function runInspect(inst: ReactWrapper) {
await getButton(inst).run(inst.getDOMNode());
await inst.update();
}
it('inspector button should be available', async () => {
const { instance } = await mountWith({ preloadedState: { isSaveable: true } });
const button = getButton(instance);
expect(button.disableButton).toEqual(false);
});
it('should open inspect panel', async () => {
const services = makeDefaultServices(sessionIdSubject);
const { instance } = await mountWith({ services, preloadedState: { isSaveable: true } });
await runInspect(instance);
expect(services.inspector.open).toHaveBeenCalledTimes(1);
});
});
describe('query bar state management', () => { describe('query bar state management', () => {
it('uses the default time and query language settings', async () => { it('uses the default time and query language settings', async () => {
const { lensStore, services } = await mountWith({}); const { lensStore, services } = await mountWith({});

View file

@ -23,6 +23,7 @@ import { LensTopNavMenu } from './lens_top_nav';
import { LensByReferenceInput } from '../embeddable'; import { LensByReferenceInput } from '../embeddable';
import { EditorFrameInstance } from '../types'; import { EditorFrameInstance } from '../types';
import { Document } from '../persistence/saved_object_store'; import { Document } from '../persistence/saved_object_store';
import { import {
setState, setState,
useLensSelector, useLensSelector,
@ -36,6 +37,7 @@ import {
getLastKnownDocWithoutPinnedFilters, getLastKnownDocWithoutPinnedFilters,
runSaveLensVisualization, runSaveLensVisualization,
} from './save_modal_container'; } from './save_modal_container';
import { getLensInspectorService, LensInspector } from '../lens_inspector_service';
export type SaveProps = Omit<OnSaveProps, 'onTitleDuplicate' | 'newDescription'> & { export type SaveProps = Omit<OnSaveProps, 'onTitleDuplicate' | 'newDescription'> & {
returnToOrigin: boolean; returnToOrigin: boolean;
@ -63,11 +65,11 @@ export function App({
data, data,
chrome, chrome,
uiSettings, uiSettings,
inspector,
application, application,
notifications, notifications,
savedObjectsTagging, savedObjectsTagging,
getOriginatingAppName, getOriginatingAppName,
// Temporarily required until the 'by value' paradigm is default. // Temporarily required until the 'by value' paradigm is default.
dashboardFeatureFlag, dashboardFeatureFlag,
} = lensAppServices; } = lensAppServices;
@ -95,6 +97,8 @@ export function App({
const [isSaveModalVisible, setIsSaveModalVisible] = useState(false); const [isSaveModalVisible, setIsSaveModalVisible] = useState(false);
const [lastKnownDoc, setLastKnownDoc] = useState<Document | undefined>(undefined); const [lastKnownDoc, setLastKnownDoc] = useState<Document | undefined>(undefined);
const lensInspector = getLensInspectorService(inspector);
useEffect(() => { useEffect(() => {
if (currentDoc) { if (currentDoc) {
setLastKnownDoc(currentDoc); setLastKnownDoc(currentDoc);
@ -267,11 +271,13 @@ export function App({
indicateNoData={indicateNoData} indicateNoData={indicateNoData}
datasourceMap={datasourceMap} datasourceMap={datasourceMap}
title={persistedDoc?.title} title={persistedDoc?.title}
lensInspector={lensInspector}
/> />
{(!isLoading || persistedDoc) && ( {(!isLoading || persistedDoc) && (
<MemoizedEditorFrameWrapper <MemoizedEditorFrameWrapper
editorFrame={editorFrame} editorFrame={editorFrame}
showNoDataPopover={showNoDataPopover} showNoDataPopover={showNoDataPopover}
lensInspector={lensInspector}
/> />
)} )}
</div> </div>
@ -308,10 +314,14 @@ export function App({
const MemoizedEditorFrameWrapper = React.memo(function EditorFrameWrapper({ const MemoizedEditorFrameWrapper = React.memo(function EditorFrameWrapper({
editorFrame, editorFrame,
showNoDataPopover, showNoDataPopover,
lensInspector,
}: { }: {
editorFrame: EditorFrameInstance; editorFrame: EditorFrameInstance;
lensInspector: LensInspector;
showNoDataPopover: () => void; showNoDataPopover: () => void;
}) { }) {
const { EditorFrameContainer } = editorFrame; const { EditorFrameContainer } = editorFrame;
return <EditorFrameContainer showNoDataPopover={showNoDataPopover} />; return (
<EditorFrameContainer showNoDataPopover={showNoDataPopover} lensInspector={lensInspector} />
);
}); });

View file

@ -71,6 +71,18 @@ function getLensTopNavConfig(options: {
defaultMessage: 'Save', defaultMessage: 'Save',
}); });
topNavMenu.push({
label: i18n.translate('xpack.lens.app.inspect', {
defaultMessage: 'Inspect',
}),
run: actions.inspect,
testId: 'lnsApp_inspectButton',
description: i18n.translate('xpack.lens.app.inspectAriaLabel', {
defaultMessage: 'inspect',
}),
disableButton: false,
});
topNavMenu.push({ topNavMenu.push({
label: i18n.translate('xpack.lens.app.downloadCSV', { label: i18n.translate('xpack.lens.app.downloadCSV', {
defaultMessage: 'Download as CSV', defaultMessage: 'Download as CSV',
@ -131,6 +143,7 @@ export const LensTopNavMenu = ({
setHeaderActionMenu, setHeaderActionMenu,
initialInput, initialInput,
indicateNoData, indicateNoData,
lensInspector,
setIsSaveModalVisible, setIsSaveModalVisible,
getIsByValueMode, getIsByValueMode,
runSave, runSave,
@ -242,6 +255,7 @@ export const LensTopNavMenu = ({
}, },
}, },
actions: { actions: {
inspect: lensInspector.inspect,
exportToCSV: () => { exportToCSV: () => {
if (!activeData) { if (!activeData) {
return; return;
@ -321,6 +335,7 @@ export const LensTopNavMenu = ({
setIsSaveModalVisible, setIsSaveModalVisible,
uiSettings, uiSettings,
unsavedTitle, unsavedTitle,
lensInspector.inspect,
] ]
); );

View file

@ -48,6 +48,7 @@ export async function getLensServices(
): Promise<LensAppServices> { ): Promise<LensAppServices> {
const { const {
data, data,
inspector,
navigation, navigation,
embeddable, embeddable,
savedObjectsTagging, savedObjectsTagging,
@ -62,6 +63,7 @@ export async function getLensServices(
return { return {
data, data,
storage, storage,
inspector,
navigation, navigation,
fieldFormats, fieldFormats,
stateTransfer, stateTransfer,
@ -82,7 +84,6 @@ export async function getLensServices(
? stateTransfer?.getAppNameFromId(embeddableEditorIncomingState.originatingApp) ? stateTransfer?.getAppNameFromId(embeddableEditorIncomingState.originatingApp)
: undefined; : undefined;
}, },
// Temporarily required until the 'by value' paradigm is default. // Temporarily required until the 'by value' paradigm is default.
dashboardFeatureFlag: startDependencies.dashboard.dashboardFeatureFlagConfig, dashboardFeatureFlag: startDependencies.dashboard.dashboardFeatureFlagConfig,
}; };

View file

@ -20,6 +20,7 @@ import type {
import type { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import type { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
import type { UsageCollectionStart } from '../../../../../src/plugins/usage_collection/public'; import type { UsageCollectionStart } from '../../../../../src/plugins/usage_collection/public';
import type { DashboardStart } from '../../../../../src/plugins/dashboard/public'; import type { DashboardStart } from '../../../../../src/plugins/dashboard/public';
import type { Start as InspectorStart } from '../../../../../src/plugins/inspector/public';
import type { LensEmbeddableInput } from '../embeddable/embeddable'; import type { LensEmbeddableInput } from '../embeddable/embeddable';
import type { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public'; import type { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public';
import type { LensAttributeService } from '../lens_attribute_service'; import type { LensAttributeService } from '../lens_attribute_service';
@ -37,6 +38,7 @@ import type {
import type { DatasourceMap, EditorFrameInstance, VisualizationMap } from '../types'; import type { DatasourceMap, EditorFrameInstance, VisualizationMap } from '../types';
import type { PresentationUtilPluginStart } from '../../../../../src/plugins/presentation_util/public'; import type { PresentationUtilPluginStart } from '../../../../../src/plugins/presentation_util/public';
import type { FieldFormatsStart } from '../../../../../src/plugins/field_formats/public'; import type { FieldFormatsStart } from '../../../../../src/plugins/field_formats/public';
import type { LensInspector } from '../lens_inspector_service';
export interface RedirectToOriginProps { export interface RedirectToOriginProps {
input?: LensEmbeddableInput; input?: LensEmbeddableInput;
@ -86,6 +88,7 @@ export interface LensTopNavMenuProps {
runSave: RunSave; runSave: RunSave;
datasourceMap: DatasourceMap; datasourceMap: DatasourceMap;
title?: string; title?: string;
lensInspector: LensInspector;
} }
export interface HistoryLocationState { export interface HistoryLocationState {
@ -101,6 +104,7 @@ export interface LensAppServices {
dashboard: DashboardStart; dashboard: DashboardStart;
fieldFormats: FieldFormatsStart; fieldFormats: FieldFormatsStart;
data: DataPublicPluginStart; data: DataPublicPluginStart;
inspector: InspectorStart;
uiSettings: IUiSettingsClient; uiSettings: IUiSettingsClient;
application: ApplicationStart; application: ApplicationStart;
notifications: NotificationsStart; notifications: NotificationsStart;
@ -112,7 +116,6 @@ export interface LensAppServices {
savedObjectsTagging?: SavedObjectTaggingPluginStart; savedObjectsTagging?: SavedObjectTaggingPluginStart;
getOriginatingAppName: () => string | undefined; getOriginatingAppName: () => string | undefined;
presentationUtil: PresentationUtilPluginStart; presentationUtil: PresentationUtilPluginStart;
// Temporarily required until the 'by value' paradigm is default. // Temporarily required until the 'by value' paradigm is default.
dashboardFeatureFlag: DashboardFeatureFlagConfig; dashboardFeatureFlag: DashboardFeatureFlagConfig;
} }
@ -122,6 +125,7 @@ export interface LensTopNavTooltips {
} }
export interface LensTopNavActions { export interface LensTopNavActions {
inspect: () => void;
saveAndReturn: () => void; saveAndReturn: () => void;
showSaveModal: () => void; showSaveModal: () => void;
cancel: () => void; cancel: () => void;

View file

@ -39,6 +39,7 @@ import {
DatasourceMock, DatasourceMock,
createExpressionRendererMock, createExpressionRendererMock,
} from '../../mocks'; } from '../../mocks';
import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks';
import { ReactExpressionRendererType } from 'src/plugins/expressions/public'; import { ReactExpressionRendererType } from 'src/plugins/expressions/public';
import { DragDrop } from '../../drag_drop'; import { DragDrop } from '../../drag_drop';
import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks'; import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks';
@ -46,6 +47,7 @@ import { chartPluginMock } from '../../../../../../src/plugins/charts/public/moc
import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks'; import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks';
import { mockDataPlugin, mountWithProvider } from '../../mocks'; import { mockDataPlugin, mountWithProvider } from '../../mocks';
import { setState } from '../../state_management'; import { setState } from '../../state_management';
import { getLensInspectorService } from '../../lens_inspector_service';
function generateSuggestion(state = {}): DatasourceSuggestion { function generateSuggestion(state = {}): DatasourceSuggestion {
return { return {
@ -79,6 +81,7 @@ function getDefaultProps() {
charts: chartPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(),
}, },
palettes: chartPluginMock.createPaletteRegistry(), palettes: chartPluginMock.createPaletteRegistry(),
lensInspector: getLensInspectorService(inspectorPluginMock.createStartContract()),
showNoDataPopover: jest.fn(), showNoDataPopover: jest.fn(),
}; };
return defaultProps; return defaultProps;

View file

@ -27,6 +27,7 @@ import {
selectDatasourceStates, selectDatasourceStates,
selectVisualization, selectVisualization,
} from '../../state_management'; } from '../../state_management';
import type { LensInspector } from '../../lens_inspector_service';
export interface EditorFrameProps { export interface EditorFrameProps {
datasourceMap: DatasourceMap; datasourceMap: DatasourceMap;
@ -35,6 +36,7 @@ export interface EditorFrameProps {
core: CoreStart; core: CoreStart;
plugins: EditorFrameStartPlugins; plugins: EditorFrameStartPlugins;
showNoDataPopover: () => void; showNoDataPopover: () => void;
lensInspector: LensInspector;
} }
export function EditorFrame(props: EditorFrameProps) { export function EditorFrame(props: EditorFrameProps) {
@ -108,6 +110,7 @@ export function EditorFrame(props: EditorFrameProps) {
core={props.core} core={props.core}
plugins={props.plugins} plugins={props.plugins}
ExpressionRenderer={props.ExpressionRenderer} ExpressionRenderer={props.ExpressionRenderer}
lensInspector={props.lensInspector}
datasourceMap={datasourceMap} datasourceMap={datasourceMap}
visualizationMap={visualizationMap} visualizationMap={visualizationMap}
framePublicAPI={framePublicAPI} framePublicAPI={framePublicAPI}

View file

@ -34,6 +34,8 @@ import { uiActionsPluginMock } from '../../../../../../../src/plugins/ui_actions
import { TriggerContract } from '../../../../../../../src/plugins/ui_actions/public/triggers'; import { TriggerContract } from '../../../../../../../src/plugins/ui_actions/public/triggers';
import { VIS_EVENT_TO_TRIGGER } from '../../../../../../../src/plugins/visualizations/public/embeddable'; import { VIS_EVENT_TO_TRIGGER } from '../../../../../../../src/plugins/visualizations/public/embeddable';
import { LensRootStore, setState } from '../../../state_management'; import { LensRootStore, setState } from '../../../state_management';
import { getLensInspectorService } from '../../../lens_inspector_service';
import { inspectorPluginMock } from '../../../../../../../src/plugins/inspector/public/mocks';
const defaultPermissions: Record<string, Record<string, boolean | Record<string, boolean>>> = { const defaultPermissions: Record<string, Record<string, boolean | Record<string, boolean>>> = {
navLinks: { management: true }, navLinks: { management: true },
@ -59,6 +61,7 @@ const defaultProps = {
data: mockDataPlugin(), data: mockDataPlugin(),
}, },
getSuggestionForField: () => undefined, getSuggestionForField: () => undefined,
lensInspector: getLensInspectorService(inspectorPluginMock.createStartContract()),
toggleFullscreen: jest.fn(), toggleFullscreen: jest.fn(),
}; };

View file

@ -21,14 +21,10 @@ import {
EuiButton, EuiButton,
EuiSpacer, EuiSpacer,
} from '@elastic/eui'; } from '@elastic/eui';
import { CoreStart, ApplicationStart } from 'kibana/public'; import type { CoreStart, ApplicationStart } from 'kibana/public';
import { import type { DataPublicPluginStart, ExecutionContextSearch } from 'src/plugins/data/public';
DataPublicPluginStart,
ExecutionContextSearch,
TimefilterContract,
} from 'src/plugins/data/public';
import { RedirectAppLinks } from '../../../../../../../src/plugins/kibana_react/public'; import { RedirectAppLinks } from '../../../../../../../src/plugins/kibana_react/public';
import { import type {
ExpressionRendererEvent, ExpressionRendererEvent,
ExpressionRenderError, ExpressionRenderError,
ReactExpressionRendererType, ReactExpressionRendererType,
@ -67,6 +63,7 @@ import {
selectActiveDatasourceId, selectActiveDatasourceId,
selectSearchSessionId, selectSearchSessionId,
} from '../../../state_management'; } from '../../../state_management';
import type { LensInspector } from '../../../lens_inspector_service';
export interface WorkspacePanelProps { export interface WorkspacePanelProps {
visualizationMap: VisualizationMap; visualizationMap: VisualizationMap;
@ -76,6 +73,7 @@ export interface WorkspacePanelProps {
core: CoreStart; core: CoreStart;
plugins: { uiActions?: UiActionsStart; data: DataPublicPluginStart }; plugins: { uiActions?: UiActionsStart; data: DataPublicPluginStart };
getSuggestionForField: (field: DragDropIdentifier) => Suggestion | undefined; getSuggestionForField: (field: DragDropIdentifier) => Suggestion | undefined;
lensInspector: LensInspector;
} }
interface WorkspaceState { interface WorkspaceState {
@ -124,6 +122,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
plugins, plugins,
ExpressionRenderer: ExpressionRendererComponent, ExpressionRenderer: ExpressionRendererComponent,
suggestionForDraggedField, suggestionForDraggedField,
lensInspector,
}: Omit<WorkspacePanelProps, 'getSuggestionForField'> & { }: Omit<WorkspacePanelProps, 'getSuggestionForField'> & {
suggestionForDraggedField: Suggestion | undefined; suggestionForDraggedField: Suggestion | undefined;
}) { }) {
@ -335,7 +334,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
<VisualizationWrapper <VisualizationWrapper
expression={expression} expression={expression}
framePublicAPI={framePublicAPI} framePublicAPI={framePublicAPI}
timefilter={plugins.data.query.timefilter.timefilter} lensInspector={lensInspector}
onEvent={onEvent} onEvent={onEvent}
setLocalState={setLocalState} setLocalState={setLocalState}
localState={{ ...localState, configurationValidationError, missingRefsErrors }} localState={{ ...localState, configurationValidationError, missingRefsErrors }}
@ -401,7 +400,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
export const VisualizationWrapper = ({ export const VisualizationWrapper = ({
expression, expression,
framePublicAPI, framePublicAPI,
timefilter, lensInspector,
onEvent, onEvent,
setLocalState, setLocalState,
localState, localState,
@ -411,7 +410,7 @@ export const VisualizationWrapper = ({
}: { }: {
expression: string | null | undefined; expression: string | null | undefined;
framePublicAPI: FramePublicAPI; framePublicAPI: FramePublicAPI;
timefilter: TimefilterContract; lensInspector: LensInspector;
onEvent: (event: ExpressionRendererEvent) => void; onEvent: (event: ExpressionRendererEvent) => void;
setLocalState: (dispatch: (prevState: WorkspaceState) => WorkspaceState) => void; setLocalState: (dispatch: (prevState: WorkspaceState) => WorkspaceState) => void;
localState: WorkspaceState & { localState: WorkspaceState & {
@ -443,9 +442,9 @@ export const VisualizationWrapper = ({
const dispatchLens = useLensDispatch(); const dispatchLens = useLensDispatch();
const onData$ = useCallback( const onData$ = useCallback(
(data: unknown, inspectorAdapters?: Partial<DefaultInspectorAdapters>) => { (data: unknown, adapters?: Partial<DefaultInspectorAdapters>) => {
if (inspectorAdapters && inspectorAdapters.tables) { if (adapters && adapters.tables) {
dispatchLens(onActiveDataChange({ ...inspectorAdapters.tables.tables })); dispatchLens(onActiveDataChange({ ...adapters.tables.tables }));
} }
}, },
[dispatchLens] [dispatchLens]
@ -634,6 +633,7 @@ export const VisualizationWrapper = ({
searchSessionId={searchSessionId} searchSessionId={searchSessionId}
onEvent={onEvent} onEvent={onEvent}
onData$={onData$} onData$={onData$}
inspectorAdapters={lensInspector.adapters}
renderMode="edit" renderMode="edit"
renderError={(errorMessage?: string | null, error?: ExpressionRenderError | null) => { renderError={(errorMessage?: string | null, error?: ExpressionRenderError | null) => {
const errorsFromRequest = getOriginalRequestErrorMessages(error); const errorsFromRequest = getOriginalRequestErrorMessages(error);

View file

@ -107,13 +107,14 @@ export class EditorFrameService {
const { EditorFrame } = await import('../async_services'); const { EditorFrame } = await import('../async_services');
return { return {
EditorFrameContainer: ({ showNoDataPopover }) => { EditorFrameContainer: ({ showNoDataPopover, lensInspector }) => {
return ( return (
<div className="lnsApp__frame"> <div className="lnsApp__frame">
<EditorFrame <EditorFrame
data-test-subj="lnsEditorFrame" data-test-subj="lnsEditorFrame"
core={core} core={core}
plugins={plugins} plugins={plugins}
lensInspector={lensInspector}
showNoDataPopover={showNoDataPopover} showNoDataPopover={showNoDataPopover}
datasourceMap={resolvedDatasources} datasourceMap={resolvedDatasources}
visualizationMap={resolvedVisualizations} visualizationMap={resolvedVisualizations}

View file

@ -23,6 +23,7 @@ import { AttributeService, ViewMode } from '../../../../../src/plugins/embeddabl
import { LensAttributeService } from '../lens_attribute_service'; import { LensAttributeService } from '../lens_attribute_service';
import { OnSaveProps } from '../../../../../src/plugins/saved_objects/public/save_modal'; import { OnSaveProps } from '../../../../../src/plugins/saved_objects/public/save_modal';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { inspectorPluginMock } from '../../../../../src/plugins/inspector/public/mocks';
jest.mock('../../../../../src/plugins/inspector/public/', () => ({ jest.mock('../../../../../src/plugins/inspector/public/', () => ({
isAvailable: false, isAvailable: false,
@ -116,6 +117,7 @@ describe('embeddable', () => {
canSaveDashboards: true, canSaveDashboards: true,
canSaveVisualizations: true, canSaveVisualizations: true,
}, },
inspector: inspectorPluginMock.createStartContract(),
getTrigger, getTrigger,
documentToExpression: () => documentToExpression: () =>
Promise.resolve({ Promise.resolve({
@ -154,6 +156,7 @@ describe('embeddable', () => {
expressionRenderer, expressionRenderer,
basePath, basePath,
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
inspector: inspectorPluginMock.createStartContract(),
capabilities: { canSaveDashboards: true, canSaveVisualizations: true }, capabilities: { canSaveDashboards: true, canSaveVisualizations: true },
getTrigger, getTrigger,
documentToExpression: () => documentToExpression: () =>
@ -193,6 +196,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -234,6 +238,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: ({ indexPatternService: ({
get: (id: string) => Promise.resolve({ id }), get: (id: string) => Promise.resolve({ id }),
} as unknown) as IndexPatternsContract, } as unknown) as IndexPatternsContract,
@ -274,6 +279,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -318,6 +324,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { canSaveDashboards: true, canSaveVisualizations: true }, capabilities: { canSaveDashboards: true, canSaveVisualizations: true },
getTrigger, getTrigger,
@ -363,6 +370,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -409,6 +417,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -462,6 +471,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -515,6 +525,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -567,6 +578,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: ({ get: jest.fn() } as unknown) as IndexPatternsContract, indexPatternService: ({ get: jest.fn() } as unknown) as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -608,6 +620,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -649,6 +662,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -690,6 +704,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -746,6 +761,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -818,6 +834,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -865,6 +882,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,
@ -912,6 +930,7 @@ describe('embeddable', () => {
attributeService, attributeService,
expressionRenderer, expressionRenderer,
basePath, basePath,
inspector: inspectorPluginMock.createStartContract(),
indexPatternService: {} as IndexPatternsContract, indexPatternService: {} as IndexPatternsContract,
capabilities: { capabilities: {
canSaveDashboards: true, canSaveDashboards: true,

View file

@ -8,7 +8,7 @@
import { isEqual, uniqBy } from 'lodash'; import { isEqual, uniqBy } from 'lodash';
import React from 'react'; import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom'; import { render, unmountComponentAtNode } from 'react-dom';
import { import type {
ExecutionContextSearch, ExecutionContextSearch,
Filter, Filter,
Query, Query,
@ -16,11 +16,12 @@ import {
TimeRange, TimeRange,
IndexPattern, IndexPattern,
} from 'src/plugins/data/public'; } from 'src/plugins/data/public';
import { PaletteOutput } from 'src/plugins/charts/public'; import type { PaletteOutput } from 'src/plugins/charts/public';
import type { Start as InspectorStart } from 'src/plugins/inspector/public';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { toExpression, Ast } from '@kbn/interpreter/common'; import { toExpression, Ast } from '@kbn/interpreter/common';
import { DefaultInspectorAdapters, RenderMode } from 'src/plugins/expressions'; import { RenderMode } from 'src/plugins/expressions';
import { map, distinctUntilChanged, skip } from 'rxjs/operators'; import { map, distinctUntilChanged, skip } from 'rxjs/operators';
import fastIsEqual from 'fast-deep-equal'; import fastIsEqual from 'fast-deep-equal';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
@ -40,7 +41,7 @@ import {
ReferenceOrValueEmbeddable, ReferenceOrValueEmbeddable,
} from '../../../../../src/plugins/embeddable/public'; } from '../../../../../src/plugins/embeddable/public';
import { Document, injectFilterReferences } from '../persistence'; import { Document, injectFilterReferences } from '../persistence';
import { ExpressionWrapper } from './expression_wrapper'; import { ExpressionWrapper, ExpressionWrapperProps } from './expression_wrapper';
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import { import {
isLensBrushEvent, isLensBrushEvent,
@ -56,6 +57,7 @@ import { getEditPath, DOC_TYPE, PLUGIN_ID } from '../../common';
import { IBasePath } from '../../../../../src/core/public'; import { IBasePath } from '../../../../../src/core/public';
import { LensAttributeService } from '../lens_attribute_service'; import { LensAttributeService } from '../lens_attribute_service';
import type { ErrorMessage } from '../editor_frame_service/types'; import type { ErrorMessage } from '../editor_frame_service/types';
import { getLensInspectorService, LensInspector } from '../lens_inspector_service';
export type LensSavedObjectAttributes = Omit<Document, 'savedObjectId' | 'type'>; export type LensSavedObjectAttributes = Omit<Document, 'savedObjectId' | 'type'>;
@ -93,6 +95,7 @@ export interface LensEmbeddableDeps {
expressionRenderer: ReactExpressionRendererType; expressionRenderer: ReactExpressionRendererType;
timefilter: TimefilterContract; timefilter: TimefilterContract;
basePath: IBasePath; basePath: IBasePath;
inspector: InspectorStart;
getTrigger?: UiActionsStart['getTrigger'] | undefined; getTrigger?: UiActionsStart['getTrigger'] | undefined;
getTriggerCompatibleActions?: UiActionsStart['getTriggerCompatibleActions']; getTriggerCompatibleActions?: UiActionsStart['getTriggerCompatibleActions'];
capabilities: { canSaveVisualizations: boolean; canSaveDashboards: boolean }; capabilities: { canSaveVisualizations: boolean; canSaveDashboards: boolean };
@ -112,10 +115,10 @@ export class Embeddable
private domNode: HTMLElement | Element | undefined; private domNode: HTMLElement | Element | undefined;
private subscription: Subscription; private subscription: Subscription;
private isInitialized = false; private isInitialized = false;
private activeData: Partial<DefaultInspectorAdapters> | undefined;
private errors: ErrorMessage[] | undefined; private errors: ErrorMessage[] | undefined;
private inputReloadSubscriptions: Subscription[]; private inputReloadSubscriptions: Subscription[];
private isDestroyed?: boolean; private isDestroyed?: boolean;
private lensInspector: LensInspector;
private logError(type: 'runtime' | 'validation') { private logError(type: 'runtime' | 'validation') {
this.deps.usageCollection?.reportUiCounter( this.deps.usageCollection?.reportUiCounter(
@ -144,7 +147,7 @@ export class Embeddable
}, },
parent parent
); );
this.lensInspector = getLensInspectorService(deps.inspector);
this.expressionRenderer = deps.expressionRenderer; this.expressionRenderer = deps.expressionRenderer;
this.initializeSavedVis(initialInput).then(() => this.onContainerStateChanged(initialInput)); this.initializeSavedVis(initialInput).then(() => this.onContainerStateChanged(initialInput));
this.subscription = this.getUpdated$().subscribe(() => this.subscription = this.getUpdated$().subscribe(() =>
@ -246,7 +249,7 @@ export class Embeddable
} }
public getInspectorAdapters() { public getInspectorAdapters() {
return this.activeData; return this.lensInspector.adapters;
} }
async initializeSavedVis(input: LensEmbeddableInput) { async initializeSavedVis(input: LensEmbeddableInput) {
@ -300,11 +303,7 @@ export class Embeddable
return isDirty; return isDirty;
} }
private updateActiveData = ( private updateActiveData: ExpressionWrapperProps['onData$'] = () => {
data: unknown,
inspectorAdapters?: Partial<DefaultInspectorAdapters> | undefined
) => {
this.activeData = inspectorAdapters;
if (this.input.onLoad) { if (this.input.onLoad) {
// once onData$ is get's called from expression renderer, loading becomes false // once onData$ is get's called from expression renderer, loading becomes false
this.input.onLoad(false); this.input.onLoad(false);
@ -341,6 +340,7 @@ export class Embeddable
ExpressionRenderer={this.expressionRenderer} ExpressionRenderer={this.expressionRenderer}
expression={this.expression || null} expression={this.expression || null}
errors={this.errors} errors={this.errors}
lensInspector={this.lensInspector}
searchContext={this.getMergedSearchContext()} searchContext={this.getMergedSearchContext()}
variables={input.palette ? { theme: { palette: input.palette } } : {}} variables={input.palette ? { theme: { palette: input.palette } } : {}}
searchSessionId={this.externalSearchContext.searchSessionId} searchSessionId={this.externalSearchContext.searchSessionId}

View file

@ -18,6 +18,7 @@ import {
} from '../../../../../src/plugins/embeddable/public'; } from '../../../../../src/plugins/embeddable/public';
import { LensByReferenceInput, LensEmbeddableInput } from './embeddable'; import { LensByReferenceInput, LensEmbeddableInput } from './embeddable';
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import { Start as InspectorStart } from '../../../../../src/plugins/inspector/public';
import { Document } from '../persistence/saved_object_store'; import { Document } from '../persistence/saved_object_store';
import { LensAttributeService } from '../lens_attribute_service'; import { LensAttributeService } from '../lens_attribute_service';
import { DOC_TYPE } from '../../common'; import { DOC_TYPE } from '../../common';
@ -27,6 +28,7 @@ import { extract, inject } from '../../common/embeddable_factory';
export interface LensEmbeddableStartServices { export interface LensEmbeddableStartServices {
timefilter: TimefilterContract; timefilter: TimefilterContract;
coreHttp: HttpSetup; coreHttp: HttpSetup;
inspector: InspectorStart;
attributeService: LensAttributeService; attributeService: LensAttributeService;
capabilities: RecursiveReadonly<Capabilities>; capabilities: RecursiveReadonly<Capabilities>;
expressionRenderer: ReactExpressionRendererType; expressionRenderer: ReactExpressionRendererType;
@ -87,6 +89,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
indexPatternService, indexPatternService,
capabilities, capabilities,
usageCollection, usageCollection,
inspector,
} = await this.getStartServices(); } = await this.getStartServices();
const { Embeddable } = await import('../async_services'); const { Embeddable } = await import('../async_services');
@ -96,6 +99,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
attributeService, attributeService,
indexPatternService, indexPatternService,
timefilter, timefilter,
inspector,
expressionRenderer, expressionRenderer,
basePath: coreHttp.basePath, basePath: coreHttp.basePath,
getTrigger: uiActions?.getTrigger, getTrigger: uiActions?.getTrigger,

View file

@ -20,6 +20,7 @@ import { DefaultInspectorAdapters, RenderMode } from 'src/plugins/expressions';
import classNames from 'classnames'; import classNames from 'classnames';
import { getOriginalRequestErrorMessages } from '../editor_frame_service/error_helper'; import { getOriginalRequestErrorMessages } from '../editor_frame_service/error_helper';
import { ErrorMessage } from '../editor_frame_service/types'; import { ErrorMessage } from '../editor_frame_service/types';
import { LensInspector } from '../lens_inspector_service';
export interface ExpressionWrapperProps { export interface ExpressionWrapperProps {
ExpressionRenderer: ReactExpressionRendererType; ExpressionRenderer: ReactExpressionRendererType;
@ -41,6 +42,7 @@ export interface ExpressionWrapperProps {
canEdit: boolean; canEdit: boolean;
onRuntimeError: () => void; onRuntimeError: () => void;
executionContext?: KibanaExecutionContext; executionContext?: KibanaExecutionContext;
lensInspector: LensInspector;
} }
interface VisualizationErrorProps { interface VisualizationErrorProps {
@ -111,6 +113,7 @@ export function ExpressionWrapper({
canEdit, canEdit,
onRuntimeError, onRuntimeError,
executionContext, executionContext,
lensInspector,
}: ExpressionWrapperProps) { }: ExpressionWrapperProps) {
return ( return (
<I18nProvider> <I18nProvider>
@ -126,6 +129,7 @@ export function ExpressionWrapper({
searchContext={searchContext} searchContext={searchContext}
searchSessionId={searchSessionId} searchSessionId={searchSessionId}
onData$={onData$} onData$={onData$}
inspectorAdapters={lensInspector.adapters}
renderMode={renderMode} renderMode={renderMode}
syncColors={syncColors} syncColors={syncColors}
executionContext={executionContext} executionContext={executionContext}

View file

@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type {
Adapters,
Start as InspectorStartContract,
} from '../../../../src/plugins/inspector/public';
import { createDefaultInspectorAdapters } from '../../../../src/plugins/expressions/public';
export const getLensInspectorService = (inspector: InspectorStartContract) => {
const adapters: Adapters = createDefaultInspectorAdapters();
return {
adapters,
inspect: () => inspector.open(adapters),
};
};
export type LensInspector = ReturnType<typeof getLensInspectorService>;

View file

@ -23,6 +23,7 @@ import { navigationPluginMock } from '../../../../src/plugins/navigation/public/
import { LensAppServices } from './app_plugin/types'; import { LensAppServices } from './app_plugin/types';
import { DOC_TYPE, layerTypes } from '../common'; import { DOC_TYPE, layerTypes } from '../common';
import { DataPublicPluginStart, esFilters, UI_SETTINGS } from '../../../../src/plugins/data/public'; import { DataPublicPluginStart, esFilters, UI_SETTINGS } from '../../../../src/plugins/data/public';
import { inspectorPluginMock } from '../../../../src/plugins/inspector/public/mocks';
import { dashboardPluginMock } from '../../../../src/plugins/dashboard/public/mocks'; import { dashboardPluginMock } from '../../../../src/plugins/dashboard/public/mocks';
import type { import type {
LensByValueInput, LensByValueInput,
@ -378,6 +379,7 @@ export function makeDefaultServices(
navigation: navigationStartMock, navigation: navigationStartMock,
notifications: core.notifications, notifications: core.notifications,
attributeService: makeAttributeService(), attributeService: makeAttributeService(),
inspector: inspectorPluginMock.createStartContract(),
dashboard: dashboardPluginMock.createStartContract(), dashboard: dashboardPluginMock.createStartContract(),
presentationUtil: presentationUtilPluginMock.createStartContract(core), presentationUtil: presentationUtilPluginMock.createStartContract(core),
savedObjectsClient: core.savedObjects.client, savedObjectsClient: core.savedObjects.client,

View file

@ -206,6 +206,7 @@ export class LensPlugin {
indexPatternService: deps.data.indexPatterns, indexPatternService: deps.data.indexPatterns,
uiActions: deps.uiActions, uiActions: deps.uiActions,
usageCollection, usageCollection,
inspector: deps.inspector,
}; };
}; };

View file

@ -5,13 +5,11 @@
* 2.0. * 2.0.
*/ */
import { IconType } from '@elastic/eui/src/components/icon/icon'; import type { IconType } from '@elastic/eui/src/components/icon/icon';
import { CoreSetup } from 'kibana/public'; import type { CoreSetup, SavedObjectReference } from 'kibana/public';
import { PaletteOutput } from 'src/plugins/charts/public'; import type { PaletteOutput } from 'src/plugins/charts/public';
import { SavedObjectReference } from 'kibana/public'; import type { MutableRefObject } from 'react';
import { MutableRefObject } from 'react'; import type {
import { RowClickContext } from '../../../../src/plugins/ui_actions/public';
import {
ExpressionAstExpression, ExpressionAstExpression,
ExpressionRendererEvent, ExpressionRendererEvent,
IInterpreterRenderHandlers, IInterpreterRenderHandlers,
@ -19,20 +17,28 @@ import {
} from '../../../../src/plugins/expressions/public'; } from '../../../../src/plugins/expressions/public';
import { DraggingIdentifier, DragDropIdentifier, DragContextState } from './drag_drop'; import { DraggingIdentifier, DragDropIdentifier, DragContextState } from './drag_drop';
import type { DateRange, LayerType } from '../common'; import type { DateRange, LayerType } from '../common';
import { Query, Filter } from '../../../../src/plugins/data/public'; import type { Query, Filter } from '../../../../src/plugins/data/public';
import { VisualizeFieldContext } from '../../../../src/plugins/ui_actions/public'; import type {
import { RangeSelectContext, ValueClickContext } from '../../../../src/plugins/embeddable/public'; RangeSelectContext,
import { ValueClickContext,
LENS_EDIT_SORT_ACTION, } from '../../../../src/plugins/embeddable/public';
LENS_EDIT_RESIZE_ACTION,
LENS_TOGGLE_ACTION,
} from './datatable_visualization/components/constants';
import type { import type {
LensSortActionData, LensSortActionData,
LensResizeActionData, LensResizeActionData,
LensToggleActionData, LensToggleActionData,
} from './datatable_visualization/components/types'; } from './datatable_visualization/components/types';
import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; import type {
UiActionsStart,
RowClickContext,
VisualizeFieldContext,
} from '../../../../src/plugins/ui_actions/public';
import {
LENS_EDIT_SORT_ACTION,
LENS_EDIT_RESIZE_ACTION,
LENS_TOGGLE_ACTION,
} from './datatable_visualization/components/constants';
import type { LensInspector } from './lens_inspector_service';
export type ErrorCallback = (e: { message: string }) => void; export type ErrorCallback = (e: { message: string }) => void;
@ -43,6 +49,7 @@ export interface PublicAPIProps<T> {
export interface EditorFrameProps { export interface EditorFrameProps {
showNoDataPopover: () => void; showNoDataPopover: () => void;
lensInspector: LensInspector;
} }
export type VisualizationMap = Record<string, Visualization>; export type VisualizationMap = Record<string, Visualization>;

View file

@ -43,6 +43,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./lens_tagging')); loadTestFile(require.resolve('./lens_tagging'));
loadTestFile(require.resolve('./formula')); loadTestFile(require.resolve('./formula'));
loadTestFile(require.resolve('./heatmap')); loadTestFile(require.resolve('./heatmap'));
loadTestFile(require.resolve('./inspector'));
// has to be last one in the suite because it overrides saved objects // has to be last one in the suite because it overrides saved objects
loadTestFile(require.resolve('./rollup')); loadTestFile(require.resolve('./rollup'));

View file

@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']);
const elasticChart = getService('elasticChart');
const inspector = getService('inspector');
describe('lens inspector', () => {
before(async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVisType('lens');
await elasticChart.setNewChartUiDebugFlag(true);
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
operation: 'terms',
field: 'clientip',
});
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
operation: 'max',
field: 'bytes',
});
await PageObjects.lens.waitForVisualization();
await inspector.open('lnsApp_inspectButton');
});
after(async () => {
await inspector.close();
});
it('should inspect table data', async () => {
await inspector.expectTableData([
['232.44.243.247', '19,986'],
['252.59.37.77', '19,985'],
['239.180.70.74', '19,984'],
['206.22.226.5', '19,952'],
['80.252.219.9', '19,950'],
['Other', '19,941'],
]);
});
it('should inspect request data', async () => {
await inspector.openInspectorRequestsView();
expect(await inspector.getRequestNames()).to.be('Data,Other bucket');
});
});
}