diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action_visualize_lens_field.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action_visualize_lens_field.md
new file mode 100644
index 00000000000000..b00618f5105105
--- /dev/null
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action_visualize_lens_field.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ACTION\_VISUALIZE\_LENS\_FIELD](./kibana-plugin-plugins-ui_actions-public.action_visualize_lens_field.md)
+
+## ACTION\_VISUALIZE\_LENS\_FIELD variable
+
+Signature:
+
+```typescript
+ACTION_VISUALIZE_LENS_FIELD = "ACTION_VISUALIZE_LENS_FIELD"
+```
diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_lens_field.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_lens_field.md
new file mode 100644
index 00000000000000..96370a07806d32
--- /dev/null
+++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_lens_field.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionContextMapping](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md) > [ACTION\_VISUALIZE\_LENS\_FIELD](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_lens_field.md)
+
+## ActionContextMapping.ACTION\_VISUALIZE\_LENS\_FIELD property
+
+Signature:
+
+```typescript
+[ACTION_VISUALIZE_LENS_FIELD]: VisualizeFieldContext;
+```
diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts
index 4b2d6cae1c8e15..b9f4a4a0426bff 100644
--- a/src/plugins/ui_actions/public/index.ts
+++ b/src/plugins/ui_actions/public/index.ts
@@ -59,6 +59,7 @@ export {
VisualizeFieldContext,
ACTION_VISUALIZE_FIELD,
ACTION_VISUALIZE_GEO_FIELD,
+ ACTION_VISUALIZE_LENS_FIELD,
} from './types';
export {
ActionByType,
diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts
index b00f4628ffb967..0be3c19fc1c4d1 100644
--- a/src/plugins/ui_actions/public/types.ts
+++ b/src/plugins/ui_actions/public/types.ts
@@ -57,10 +57,12 @@ export interface TriggerContextMapping {
const DEFAULT_ACTION = '';
export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD';
export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD';
+export const ACTION_VISUALIZE_LENS_FIELD = 'ACTION_VISUALIZE_LENS_FIELD';
export type ActionType = keyof ActionContextMapping;
export interface ActionContextMapping {
[DEFAULT_ACTION]: BaseContext;
[ACTION_VISUALIZE_FIELD]: VisualizeFieldContext;
[ACTION_VISUALIZE_GEO_FIELD]: VisualizeFieldContext;
+ [ACTION_VISUALIZE_LENS_FIELD]: VisualizeFieldContext;
}
diff --git a/src/plugins/visualize/public/actions/visualize_field_action.ts b/src/plugins/visualize/public/actions/visualize_field_action.ts
index 6671d2c9819106..e570ed5e49e6a7 100644
--- a/src/plugins/visualize/public/actions/visualize_field_action.ts
+++ b/src/plugins/visualize/public/actions/visualize_field_action.ts
@@ -34,6 +34,7 @@ import { AGGS_TERMS_SIZE_SETTING } from '../../common/constants';
export const visualizeFieldAction = createAction({
type: ACTION_VISUALIZE_FIELD,
+ id: ACTION_VISUALIZE_FIELD,
getDisplayName: () =>
i18n.translate('visualize.discover.visualizeFieldLabel', {
defaultMessage: 'Visualize field',
diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json
index f5fba766e60eea..46a0b56a03ec57 100644
--- a/x-pack/plugins/lens/kibana.json
+++ b/x-pack/plugins/lens/kibana.json
@@ -11,9 +11,10 @@
"urlForwarding",
"visualizations",
"dashboard",
- "charts"
+ "charts",
+ "uiActions"
],
- "optionalPlugins": ["embeddable", "usageCollection", "taskManager", "uiActions", "globalSearch"],
+ "optionalPlugins": ["embeddable", "usageCollection", "taskManager", "globalSearch"],
"configPath": ["xpack", "lens"],
"extraPublicDirs": ["common/constants"],
"requiredBundles": ["savedObjects", "kibanaUtils", "kibanaReact", "embeddable"]
diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx
index 24114e2b315181..ee92c52a3cc768 100644
--- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx
+++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx
@@ -280,6 +280,7 @@ describe('Lens App', () => {
},
"doc": undefined,
"filters": Array [],
+ "initialContext": undefined,
"onChange": [Function],
"onError": [Function],
"query": Object {
diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx
index e4af2a33ec68be..3407ea5de49c46 100644
--- a/x-pack/plugins/lens/public/app_plugin/app.tsx
+++ b/x-pack/plugins/lens/public/app_plugin/app.tsx
@@ -47,6 +47,7 @@ export function App({
incomingState,
redirectToOrigin,
setHeaderActionMenu,
+ initialContext,
}: LensAppProps) {
const {
data,
@@ -67,7 +68,7 @@ export function App({
const [state, setState] = useState(() => {
const currentRange = data.query.timefilter.timefilter.getTime();
return {
- query: data.query.queryString.getDefaultQuery(),
+ query: data.query.queryString.getQuery(),
filters: data.query.filterManager.getFilters(),
isLoading: Boolean(initialInput),
indexPatternsForTopNav: [],
@@ -142,8 +143,11 @@ export function App({
useEffect(() => {
// Clear app-specific filters when navigating to Lens. Necessary because Lens
- // can be loaded without a full page refresh
- data.query.filterManager.setAppFilters([]);
+ // can be loaded without a full page refresh. If the user navigates to Lens from Discover
+ // we keep the filters
+ if (!initialContext) {
+ data.query.filterManager.setAppFilters([]);
+ }
const filterSubscription = data.query.filterManager.getUpdates$().subscribe({
next: () => {
@@ -187,6 +191,7 @@ export function App({
uiSettings,
data.query,
history,
+ initialContext,
]);
useEffect(() => {
@@ -576,6 +581,7 @@ export function App({
doc: state.persistedDoc,
onError,
showNoDataPopover,
+ initialContext,
onChange: ({ filterableIndexPatterns, doc, isSaveable }) => {
if (isSaveable !== state.isSaveable) {
setState((s) => ({ ...s, isSaveable }));
diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx
index 0d50e541d3e48e..90ba74decfd809 100644
--- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx
+++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx
@@ -26,8 +26,9 @@ import {
LensByReferenceInput,
LensByValueInput,
} from '../editor_frame_service/embeddable/embeddable';
+import { ACTION_VISUALIZE_LENS_FIELD } from '../../../../../src/plugins/ui_actions/public';
import { LensAttributeService } from '../lens_attribute_service';
-import { LensAppServices, RedirectToOriginProps } from './types';
+import { LensAppServices, RedirectToOriginProps, HistoryLocationState } from './types';
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
export async function mountApp(
@@ -46,6 +47,7 @@ export async function mountApp(
const instance = await createEditorFrame();
const storage = new Storage(localStorage);
const stateTransfer = embeddable?.getStateTransfer(params.history);
+ const historyLocationState = params.history.location.state as HistoryLocationState;
const embeddableEditorIncomingState = stateTransfer?.getIncomingEditorState();
const lensServices: LensAppServices = {
@@ -132,6 +134,11 @@ export async function mountApp(
onAppLeave={params.onAppLeave}
setHeaderActionMenu={params.setHeaderActionMenu}
history={routeProps.history}
+ initialContext={
+ historyLocationState && historyLocationState.type === ACTION_VISUALIZE_LENS_FIELD
+ ? historyLocationState.payload
+ : undefined
+ }
/>
);
};
diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts
index fcdd0b20f8d276..bd5a9b5a8ed0a0 100644
--- a/x-pack/plugins/lens/public/app_plugin/types.ts
+++ b/x-pack/plugins/lens/public/app_plugin/types.ts
@@ -28,6 +28,10 @@ import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigati
import { LensAttributeService } from '../lens_attribute_service';
import { IStorageWrapper } from '../../../../../src/plugins/kibana_utils/public';
import { DashboardFeatureFlagConfig } from '../../../../../src/plugins/dashboard/public';
+import {
+ VisualizeFieldContext,
+ ACTION_VISUALIZE_LENS_FIELD,
+} from '../../../../../src/plugins/ui_actions/public';
import { EmbeddableEditorState } from '../../../../../src/plugins/embeddable/public';
import { EditorFrameInstance } from '..';
@@ -75,6 +79,12 @@ export interface LensAppProps {
// State passed in by the container which is used to determine the id of the Originating App.
incomingState?: EmbeddableEditorState;
+ initialContext?: VisualizeFieldContext;
+}
+
+export interface HistoryLocationState {
+ type: typeof ACTION_VISUALIZE_LENS_FIELD;
+ payload: VisualizeFieldContext;
}
export interface LensAppServices {
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 e628ea0675a8d0..7328cdaf6fc9bb 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
@@ -184,8 +184,8 @@ describe('editor_frame', () => {
/>
);
});
- expect(mockDatasource.initialize).toHaveBeenCalledWith(datasource1State, []);
- expect(mockDatasource2.initialize).toHaveBeenCalledWith(datasource2State, []);
+ expect(mockDatasource.initialize).toHaveBeenCalledWith(datasource1State, [], undefined);
+ expect(mockDatasource2.initialize).toHaveBeenCalledWith(datasource2State, [], undefined);
expect(mockDatasource3.initialize).not.toHaveBeenCalled();
});
@@ -972,6 +972,32 @@ describe('editor_frame', () => {
});
describe('suggestions', () => {
+ it('should fetch suggestions of currently active datasource when initializes from visualization trigger', async () => {
+ await act(async () => {
+ mount(
+
+ );
+ });
+
+ expect(mockDatasource.getDatasourceSuggestionsForVisualizeField).toHaveBeenCalled();
+ });
+
it('should fetch suggestions of currently active datasource', async () => {
await act(async () => {
mount(
@@ -1208,6 +1234,7 @@ describe('editor_frame', () => {
...mockDatasource,
getDatasourceSuggestionsForField: () => [generateSuggestion()],
getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()],
+ getDatasourceSuggestionsForVisualizeField: () => [generateSuggestion()],
},
}}
initialDatasourceId="testDatasource"
@@ -1274,6 +1301,7 @@ describe('editor_frame', () => {
...mockDatasource,
getDatasourceSuggestionsForField: () => [generateSuggestion()],
getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()],
+ getDatasourceSuggestionsForVisualizeField: () => [generateSuggestion()],
renderDataPanel: (_element, { dragDropContext: { setDragging, dragging } }) => {
if (dragging !== 'draggedField') {
setDragging('draggedField');
@@ -1370,6 +1398,7 @@ describe('editor_frame', () => {
...mockDatasource,
getDatasourceSuggestionsForField: () => [generateSuggestion()],
getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()],
+ getDatasourceSuggestionsForVisualizeField: () => [generateSuggestion()],
renderDataPanel: (_element, { dragDropContext: { setDragging, dragging } }) => {
if (dragging !== 'draggedField') {
setDragging('draggedField');
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 72ad8e074226cc..32fd4461dfc8bc 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
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useEffect, useReducer } from 'react';
+import React, { useEffect, useReducer, useState } from 'react';
import { CoreSetup, CoreStart } from 'kibana/public';
import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public';
import { Datasource, FramePublicAPI, Visualization } from '../../types';
@@ -19,8 +19,10 @@ import { RootDragDropProvider } from '../../drag_drop';
import { getSavedObjectFormat } from './save';
import { generateId } from '../../id_generator';
import { Filter, Query, SavedQuery } from '../../../../../../src/plugins/data/public';
+import { VisualizeFieldContext } from '../../../../../../src/plugins/ui_actions/public';
import { EditorFrameStartPlugins } from '../service';
import { initializeDatasources, createDatasourceLayers } from './state_helpers';
+import { applyVisualizeFieldSuggestions } from './suggestion_helpers';
export interface EditorFrameProps {
doc?: Document;
@@ -45,10 +47,14 @@ export interface EditorFrameProps {
isSaveable: boolean;
}) => void;
showNoDataPopover: () => void;
+ initialContext?: VisualizeFieldContext;
}
export function EditorFrame(props: EditorFrameProps) {
const [state, dispatch] = useReducer(reducer, props, getInitialState);
+ const [visualizeTriggerFieldContext, setVisualizeTriggerFieldContext] = useState(
+ props.initialContext
+ );
const { onError } = props;
const activeVisualization =
state.visualization.activeId && props.visualizationMap[state.visualization.activeId];
@@ -63,7 +69,12 @@ export function EditorFrame(props: EditorFrameProps) {
// prevents executing dispatch on unmounted component
let isUnmounted = false;
if (!allLoaded) {
- initializeDatasources(props.datasourceMap, state.datasourceStates, props.doc?.references)
+ initializeDatasources(
+ props.datasourceMap,
+ state.datasourceStates,
+ props.doc?.references,
+ visualizeTriggerFieldContext
+ )
.then((result) => {
if (!isUnmounted) {
Object.entries(result).forEach(([datasourceId, { state: datasourceState }]) => {
@@ -84,7 +95,6 @@ export function EditorFrame(props: EditorFrameProps) {
// eslint-disable-next-line react-hooks/exhaustive-deps
[allLoaded, onError]
);
-
const datasourceLayers = createDatasourceLayers(props.datasourceMap, state.datasourceStates);
const framePublicAPI: FramePublicAPI = {
@@ -180,6 +190,23 @@ export function EditorFrame(props: EditorFrameProps) {
[allLoaded, activeVisualization, state.visualization.state]
);
+ // Get suggestions for visualize field when all datasources are ready
+ useEffect(() => {
+ if (allLoaded && visualizeTriggerFieldContext && !props.doc) {
+ applyVisualizeFieldSuggestions({
+ datasourceMap: props.datasourceMap,
+ datasourceStates: state.datasourceStates,
+ visualizationMap: props.visualizationMap,
+ activeVisualizationId: state.visualization.activeId,
+ visualizationState: state.visualization.state,
+ visualizeTriggerFieldContext,
+ dispatch,
+ });
+ setVisualizeTriggerFieldContext(undefined);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [allLoaded]);
+
// The frame needs to call onChange every time its internal state changes
useEffect(
() => {
@@ -275,6 +302,7 @@ export function EditorFrame(props: EditorFrameProps) {
ExpressionRenderer={props.ExpressionRenderer}
core={props.core}
plugins={props.plugins}
+ visualizeTriggerFieldContext={visualizeTriggerFieldContext}
/>
)
}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts
index 1fe5224d0b1b4e..8b0334ab98c146 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts
@@ -9,18 +9,20 @@ import { Ast } from '@kbn/interpreter/common';
import { Datasource, DatasourcePublicAPI, Visualization } from '../../types';
import { buildExpression } from './expression_helpers';
import { Document } from '../../persistence/saved_object_store';
+import { VisualizeFieldContext } from '../../../../../../src/plugins/ui_actions/public';
export async function initializeDatasources(
datasourceMap: Record,
datasourceStates: Record,
- references?: SavedObjectReference[]
+ references?: SavedObjectReference[],
+ initialContext?: VisualizeFieldContext
) {
const states: Record = {};
await Promise.all(
Object.entries(datasourceMap).map(([datasourceId, datasource]) => {
if (datasourceStates[datasourceId]) {
return datasource
- .initialize(datasourceStates[datasourceId].state || undefined, references)
+ .initialize(datasourceStates[datasourceId].state || undefined, references, initialContext)
.then((datasourceState) => {
states[datasourceId] = { isLoading: false, state: datasourceState };
});
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts
index 63b8b1f0482968..c5c66c1c820e86 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts
@@ -173,6 +173,75 @@ describe('suggestion helpers', () => {
expect(multiDatasourceMap.mock3.getDatasourceSuggestionsForField).not.toHaveBeenCalled();
});
+ it('should call getDatasourceSuggestionsForVisualizeField when a visualizeTriggerField is passed', () => {
+ datasourceMap.mock.getDatasourceSuggestionsForVisualizeField.mockReturnValue([
+ generateSuggestion(),
+ ]);
+ getSuggestions({
+ visualizationMap: {
+ vis1: createMockVisualization(),
+ },
+ activeVisualizationId: 'vis1',
+ visualizationState: {},
+ datasourceMap,
+ datasourceStates,
+ visualizeTriggerFieldContext: {
+ indexPatternId: '1',
+ fieldName: 'test',
+ },
+ });
+ expect(datasourceMap.mock.getDatasourceSuggestionsForVisualizeField).toHaveBeenCalledWith(
+ datasourceStates.mock.state,
+ '1',
+ 'test'
+ );
+ });
+
+ it('should call getDatasourceSuggestionsForVisualizeField from all datasources with a state', () => {
+ const multiDatasourceStates = {
+ mock: {
+ isLoading: false,
+ state: {},
+ },
+ mock2: {
+ isLoading: false,
+ state: {},
+ },
+ };
+ const multiDatasourceMap = {
+ mock: createMockDatasource('a'),
+ mock2: createMockDatasource('a'),
+ mock3: createMockDatasource('a'),
+ };
+ const visualizeTriggerField = {
+ indexPatternId: '1',
+ fieldName: 'test',
+ };
+ getSuggestions({
+ visualizationMap: {
+ vis1: createMockVisualization(),
+ },
+ activeVisualizationId: 'vis1',
+ visualizationState: {},
+ datasourceMap: multiDatasourceMap,
+ datasourceStates: multiDatasourceStates,
+ visualizeTriggerFieldContext: visualizeTriggerField,
+ });
+ expect(multiDatasourceMap.mock.getDatasourceSuggestionsForVisualizeField).toHaveBeenCalledWith(
+ multiDatasourceStates.mock.state,
+ '1',
+ 'test'
+ );
+ expect(multiDatasourceMap.mock2.getDatasourceSuggestionsForVisualizeField).toHaveBeenCalledWith(
+ multiDatasourceStates.mock2.state,
+ '1',
+ 'test'
+ );
+ expect(
+ multiDatasourceMap.mock3.getDatasourceSuggestionsForVisualizeField
+ ).not.toHaveBeenCalled();
+ });
+
it('should rank the visualizations by score', () => {
const mockVisualization1 = createMockVisualization();
const mockVisualization2 = createMockVisualization();
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 2bb1baf9d54f25..c4a92dde6187ce 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 { VisualizeFieldContext } from '../../../../../../src/plugins/ui_actions/public';
import {
Visualization,
Datasource,
@@ -47,6 +48,7 @@ export function getSuggestions({
subVisualizationId,
visualizationState,
field,
+ visualizeTriggerFieldContext,
}: {
datasourceMap: Record;
datasourceStates: Record<
@@ -61,6 +63,7 @@ export function getSuggestions({
subVisualizationId?: string;
visualizationState: unknown;
field?: unknown;
+ visualizeTriggerFieldContext?: VisualizeFieldContext;
}): Suggestion[] {
const datasources = Object.entries(datasourceMap).filter(
([datasourceId]) => datasourceStates[datasourceId] && !datasourceStates[datasourceId].isLoading
@@ -70,10 +73,21 @@ export function getSuggestions({
const datasourceTableSuggestions = _.flatten(
datasources.map(([datasourceId, datasource]) => {
const datasourceState = datasourceStates[datasourceId].state;
- return (field
- ? datasource.getDatasourceSuggestionsForField(datasourceState, field)
- : datasource.getDatasourceSuggestionsFromCurrentState(datasourceState)
- ).map((suggestion) => ({ ...suggestion, datasourceId }));
+ let dataSourceSuggestions;
+ if (visualizeTriggerFieldContext) {
+ dataSourceSuggestions = datasource.getDatasourceSuggestionsForVisualizeField(
+ datasourceState,
+ visualizeTriggerFieldContext.indexPatternId,
+ visualizeTriggerFieldContext.fieldName
+ );
+ } else if (field) {
+ dataSourceSuggestions = datasource.getDatasourceSuggestionsForField(datasourceState, field);
+ } else {
+ dataSourceSuggestions = datasource.getDatasourceSuggestionsFromCurrentState(
+ datasourceState
+ );
+ }
+ return dataSourceSuggestions.map((suggestion) => ({ ...suggestion, datasourceId }));
})
);
@@ -100,6 +114,45 @@ export function getSuggestions({
).sort((a, b) => b.score - a.score);
}
+export function applyVisualizeFieldSuggestions({
+ datasourceMap,
+ datasourceStates,
+ visualizationMap,
+ activeVisualizationId,
+ visualizationState,
+ visualizeTriggerFieldContext,
+ dispatch,
+}: {
+ datasourceMap: Record;
+ datasourceStates: Record<
+ string,
+ {
+ isLoading: boolean;
+ state: unknown;
+ }
+ >;
+ visualizationMap: Record;
+ activeVisualizationId: string | null;
+ subVisualizationId?: string;
+ visualizationState: unknown;
+ visualizeTriggerFieldContext?: VisualizeFieldContext;
+ dispatch: (action: Action) => void;
+}): void {
+ const suggestions = getSuggestions({
+ datasourceMap,
+ datasourceStates,
+ visualizationMap,
+ activeVisualizationId,
+ visualizationState,
+ visualizeTriggerFieldContext,
+ });
+ if (suggestions.length) {
+ const selectedSuggestion =
+ suggestions.find((s) => s.visualizationId === activeVisualizationId) || suggestions[0];
+ switchToSuggestion(dispatch, selectedSuggestion, 'SWITCH_VISUALIZATION');
+ }
+}
+
/**
* Queries a single visualization extensions for a single datasource suggestion and
* creates an array of complete suggestions containing both the target datasource
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 3993b4ffc02b0c..34979083645c36 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
@@ -28,7 +28,10 @@ import { getSuggestions, switchToSuggestion } from '../suggestion_helpers';
import { buildExpression } from '../expression_helpers';
import { debouncedComponent } from '../../../debounced_component';
import { trackUiEvent } from '../../../lens_ui_telemetry';
-import { UiActionsStart } from '../../../../../../../src/plugins/ui_actions/public';
+import {
+ UiActionsStart,
+ VisualizeFieldContext,
+} from '../../../../../../../src/plugins/ui_actions/public';
import { VIS_EVENT_TO_TRIGGER } from '../../../../../../../src/plugins/visualizations/public';
import { DataPublicPluginStart } from '../../../../../../../src/plugins/data/public';
import { WorkspacePanelWrapper } from './workspace_panel_wrapper';
@@ -53,6 +56,7 @@ export interface WorkspacePanelProps {
core: CoreStart | CoreSetup;
plugins: { uiActions?: UiActionsStart; data: DataPublicPluginStart };
title?: string;
+ visualizeTriggerFieldContext?: VisualizeFieldContext;
}
export const WorkspacePanel = debouncedComponent(InnerWorkspacePanel);
@@ -71,6 +75,7 @@ export function InnerWorkspacePanel({
plugins,
ExpressionRenderer: ExpressionRendererComponent,
title,
+ visualizeTriggerFieldContext,
}: WorkspacePanelProps) {
const dragDropContext = useContext(DragContext);
@@ -245,7 +250,9 @@ export function InnerWorkspacePanel({
}
function renderVisualization() {
- if (expression === null) {
+ // we don't want to render the emptyWorkspace on visualizing field from Discover
+ // as it is specific for the drag and drop functionality and can confuse the users
+ if (expression === null && !visualizeTriggerFieldContext) {
return renderEmptyWorkspace();
}
diff --git a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx
index 86b137851d9bdd..93898ef1d43a87 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx
@@ -69,6 +69,7 @@ export function createMockDatasource(id: string): DatasourceMock {
id: 'mockindexpattern',
clearLayer: jest.fn((state, _layerId) => state),
getDatasourceSuggestionsForField: jest.fn((_state, _item) => []),
+ getDatasourceSuggestionsForVisualizeField: jest.fn((_state, _indexpatternId, _fieldName) => []),
getDatasourceSuggestionsFromCurrentState: jest.fn((_state) => []),
getPersistableState: jest.fn((x) => ({ state: x, savedObjectReferences: [] })),
getPublicAPI: jest.fn().mockReturnValue(publicAPIMock),
diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx
index c1b6d74bb49c00..e9f8013ef7e2d1 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx
@@ -53,6 +53,10 @@ describe('editor_frame service', () => {
query: { query: '', language: 'lucene' },
filters: [],
showNoDataPopover: jest.fn(),
+ initialContext: {
+ indexPatternId: '1',
+ fieldName: 'test',
+ },
});
instance.unmount();
})()
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 e6d7f78f5ad071..54250c3bd9300b 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx
@@ -127,7 +127,17 @@ export class EditorFrameService {
return {
mount: async (
element,
- { doc, onError, dateRange, query, filters, savedQuery, onChange, showNoDataPopover }
+ {
+ doc,
+ onError,
+ dateRange,
+ query,
+ filters,
+ savedQuery,
+ onChange,
+ showNoDataPopover,
+ initialContext,
+ }
) => {
domElement = element;
const firstDatasourceId = Object.keys(resolvedDatasources)[0];
@@ -156,6 +166,7 @@ export class EditorFrameService {
savedQuery={savedQuery}
onChange={onChange}
showNoDataPopover={showNoDataPopover}
+ initialContext={initialContext}
/>
,
domElement
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx
index 7f7eb0bc0fdac3..28aeac223e4a6d 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx
@@ -36,6 +36,7 @@ import { IndexPatternDataPanel } from './datapanel';
import {
getDatasourceSuggestionsForField,
getDatasourceSuggestionsFromCurrentState,
+ getDatasourceSuggestionsForVisualizeField,
} from './indexpattern_suggestions';
import { isDraggedField, normalizeOperationDataType } from './utils';
@@ -49,6 +50,7 @@ import {
} from './types';
import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public';
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
+import { VisualizeFieldContext } from '../../../../../src/plugins/ui_actions/public';
import { deleteColumn } from './state_helpers';
import { Datasource, StateSetter } from '../index';
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
@@ -134,7 +136,8 @@ export function getIndexPatternDatasource({
async initialize(
persistedState?: IndexPatternPersistedState,
- references?: SavedObjectReference[]
+ references?: SavedObjectReference[],
+ initialContext?: VisualizeFieldContext
) {
return loadInitialState({
persistedState,
@@ -143,6 +146,7 @@ export function getIndexPatternDatasource({
defaultIndexPatternId: core.uiSettings.get('defaultIndex'),
storage,
indexPatternsService,
+ initialContext,
});
},
@@ -335,6 +339,7 @@ export function getIndexPatternDatasource({
: [];
},
getDatasourceSuggestionsFromCurrentState,
+ getDatasourceSuggestionsForVisualizeField,
};
return indexPatternDatasource;
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx
index 80765627c1fc29..a480cfe408982b 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx
@@ -10,6 +10,7 @@ import { IndexPatternPrivateState } from './types';
import {
getDatasourceSuggestionsForField,
getDatasourceSuggestionsFromCurrentState,
+ getDatasourceSuggestionsForVisualizeField,
} from './indexpattern_suggestions';
jest.mock('./loader');
@@ -1077,6 +1078,70 @@ describe('IndexPattern Data Source suggestions', () => {
});
});
});
+ describe('#getDatasourceSuggestionsForVisualizeField', () => {
+ describe('with no layer', () => {
+ function stateWithoutLayer() {
+ return {
+ ...testInitialState(),
+ layers: {},
+ };
+ }
+
+ it('should return an empty array if the field does not exist', () => {
+ const suggestions = getDatasourceSuggestionsForVisualizeField(
+ stateWithoutLayer(),
+ '1',
+ 'field_not_exist'
+ );
+
+ expect(suggestions).toEqual([]);
+ });
+
+ it('should apply a bucketed aggregation for a string field', () => {
+ const suggestions = getDatasourceSuggestionsForVisualizeField(
+ stateWithoutLayer(),
+ '1',
+ 'source'
+ );
+
+ expect(suggestions).toContainEqual(
+ expect.objectContaining({
+ state: expect.objectContaining({
+ layers: {
+ id1: expect.objectContaining({
+ columnOrder: ['id2', 'id3'],
+ columns: {
+ id2: expect.objectContaining({
+ operationType: 'terms',
+ sourceField: 'source',
+ params: expect.objectContaining({ size: 5 }),
+ }),
+ id3: expect.objectContaining({
+ operationType: 'count',
+ }),
+ },
+ }),
+ },
+ }),
+ table: {
+ changeType: 'initial',
+ label: undefined,
+ isMultiRow: true,
+ columns: [
+ expect.objectContaining({
+ columnId: 'id2',
+ }),
+ expect.objectContaining({
+ columnId: 'id3',
+ }),
+ ],
+ layerId: 'id1',
+ },
+ })
+ );
+ });
+ });
+ });
describe('#getDatasourceSuggestionsFromCurrentState', () => {
it('returns no suggestions if there are no columns', () => {
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts
index 75945529ffb340..c7eeef178c2514 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts
@@ -118,6 +118,25 @@ export function getDatasourceSuggestionsForField(
}
}
+// Called when the user navigates from Discover to Lens (Visualize button)
+export function getDatasourceSuggestionsForVisualizeField(
+ state: IndexPatternPrivateState,
+ indexPatternId: string,
+ fieldName: string
+): IndexPatternSugestion[] {
+ const layers = Object.keys(state.layers);
+ const layerIds = layers.filter((id) => state.layers[id].indexPatternId === indexPatternId);
+ // Identify the field by the indexPatternId and the fieldName
+ const indexPattern = state.indexPatterns[indexPatternId];
+ const field = indexPattern.fields.find((fld) => fld.name === fieldName);
+
+ if (layerIds.length !== 0 || !field) return [];
+ const newId = generateId();
+ return getEmptyLayerSuggestionsForField(state, newId, indexPatternId, field).concat(
+ getEmptyLayerSuggestionsForField({ ...state, layers: {} }, newId, indexPatternId, field)
+ );
+}
+
function getBucketOperation(field: IndexPatternField) {
// We allow numeric bucket types in some cases, but it's generally not the right suggestion,
// so we eliminate it here.
@@ -473,7 +492,6 @@ export function getDatasourceSuggestionsFromCurrentState(
suggestions.push(createChangedNestingSuggestion(state, layerId));
}
}
-
return suggestions;
})
);
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts
index ef6abbec9a34d1..06cfdf7e034817 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts
@@ -441,6 +441,34 @@ describe('loader', () => {
});
});
+ it('should use the indexPatternId of the visualize trigger field, if provided', async () => {
+ const storage = createMockStorage();
+ const state = await loadInitialState({
+ savedObjectsClient: mockClient(),
+ indexPatternsService: mockIndexPatternsService(),
+ storage,
+ initialContext: {
+ indexPatternId: '1',
+ fieldName: '',
+ },
+ });
+
+ expect(state).toMatchObject({
+ currentIndexPatternId: '1',
+ indexPatternRefs: [
+ { id: '1', title: sampleIndexPatterns['1'].title },
+ { id: '2', title: sampleIndexPatterns['2'].title },
+ ],
+ indexPatterns: {
+ '1': sampleIndexPatterns['1'],
+ },
+ layers: {},
+ });
+ expect(storage.set).toHaveBeenCalledWith('lens-settings', {
+ indexPatternId: '1',
+ });
+ });
+
it('should initialize from saved state', async () => {
const savedState: IndexPatternPersistedState = {
layers: {
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts
index c4b1eb9e0c4c4a..fd8e071d524eed 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts
@@ -23,6 +23,7 @@ import {
IndexPatternsContract,
indexPatterns as indexPatternsUtils,
} from '../../../../../src/plugins/data/public';
+import { VisualizeFieldContext } from '../../../../../src/plugins/ui_actions/public';
import { documentField } from './document_field';
import { readFromStorage, writeToStorage } from '../settings_storage';
@@ -179,6 +180,7 @@ export async function loadInitialState({
defaultIndexPatternId,
storage,
indexPatternsService,
+ initialContext,
}: {
persistedState?: IndexPatternPersistedState;
references?: SavedObjectReference[];
@@ -186,6 +188,7 @@ export async function loadInitialState({
defaultIndexPatternId?: string;
storage: IStorageWrapper;
indexPatternsService: IndexPatternsService;
+ initialContext?: VisualizeFieldContext;
}): Promise {
const indexPatternRefs = await loadIndexPatternRefs(savedObjectsClient);
const lastUsedIndexPatternId = getLastUsedIndexPatternId(storage, indexPatternRefs);
@@ -201,13 +204,13 @@ export async function loadInitialState({
: [lastUsedIndexPatternId || defaultIndexPatternId || indexPatternRefs[0].id]
);
- const currentIndexPatternId = requiredPatterns[0];
+ const currentIndexPatternId = initialContext?.indexPatternId ?? requiredPatterns[0];
setLastUsedIndexPatternId(storage, currentIndexPatternId);
const indexPatterns = await loadIndexPatterns({
indexPatternsService,
cache: {},
- patterns: requiredPatterns,
+ patterns: initialContext ? [initialContext.indexPatternId] : requiredPatterns,
});
if (state) {
return {
diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts
index 38d256d2b3afde..90b0f0a2bde84a 100644
--- a/x-pack/plugins/lens/public/plugin.ts
+++ b/x-pack/plugins/lens/public/plugin.ts
@@ -29,10 +29,15 @@ import { PieVisualization, PieVisualizationPluginSetupPlugins } from './pie_visu
import { stopReportManager } from './lens_ui_telemetry';
import { AppNavLinkStatus } from '../../../../src/core/public';
-import { UiActionsStart } from '../../../../src/plugins/ui_actions/public';
+import {
+ UiActionsStart,
+ ACTION_VISUALIZE_FIELD,
+ VISUALIZE_FIELD_TRIGGER,
+} from '../../../../src/plugins/ui_actions/public';
import { NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common';
import { EditorFrameStart } from './types';
import { getLensAliasConfig } from './vis_type_alias';
+import { visualizeFieldAction } from './trigger_actions/visualize_field_actions';
import { getSearchProvider } from './search_provider';
import { getLensAttributeService, LensAttributeService } from './lens_attribute_service';
@@ -155,6 +160,14 @@ export class LensPlugin {
start(core: CoreStart, startDependencies: LensPluginStartDependencies) {
this.attributeService = getLensAttributeService(core, startDependencies);
this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance;
+ // unregisters the Visualize action and registers the lens one
+ if (startDependencies.uiActions.hasAction(ACTION_VISUALIZE_FIELD)) {
+ startDependencies.uiActions.unregisterAction(ACTION_VISUALIZE_FIELD);
+ }
+ startDependencies.uiActions.addTriggerAction(
+ VISUALIZE_FIELD_TRIGGER,
+ visualizeFieldAction(core.application)
+ );
}
stop() {
diff --git a/x-pack/plugins/lens/public/trigger_actions/visualize_field_actions.ts b/x-pack/plugins/lens/public/trigger_actions/visualize_field_actions.ts
new file mode 100644
index 00000000000000..a473d433ac89d2
--- /dev/null
+++ b/x-pack/plugins/lens/public/trigger_actions/visualize_field_actions.ts
@@ -0,0 +1,28 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import {
+ createAction,
+ ACTION_VISUALIZE_LENS_FIELD,
+ VisualizeFieldContext,
+} from '../../../../../src/plugins/ui_actions/public';
+import { ApplicationStart } from '../../../../../src/core/public';
+
+export const visualizeFieldAction = (application: ApplicationStart) =>
+ createAction({
+ type: ACTION_VISUALIZE_LENS_FIELD,
+ id: ACTION_VISUALIZE_LENS_FIELD,
+ getDisplayName: () =>
+ i18n.translate('xpack.lens.discover.visualizeFieldLegend', {
+ defaultMessage: 'Visualize field',
+ }),
+ isCompatible: async () => !!application.capabilities.visualize.show,
+ execute: async (context: VisualizeFieldContext) => {
+ application.navigateToApp('lens', {
+ state: { type: ACTION_VISUALIZE_LENS_FIELD, payload: context },
+ });
+ },
+ });
diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts
index e5e8a645dd0e80..6061f928bce418 100644
--- a/x-pack/plugins/lens/public/types.ts
+++ b/x-pack/plugins/lens/public/types.ts
@@ -22,6 +22,7 @@ import {
SELECT_RANGE_TRIGGER,
TriggerContext,
VALUE_CLICK_TRIGGER,
+ VisualizeFieldContext,
} from '../../../../src/plugins/ui_actions/public';
export type ErrorCallback = (e: { message: string }) => void;
@@ -40,6 +41,7 @@ export interface EditorFrameProps {
query: Query;
filters: Filter[];
savedQuery?: SavedQuery;
+ initialContext?: VisualizeFieldContext;
// Frame loader (app or embeddable) is expected to call this when it loads and updates
// This should be replaced with a top-down state
@@ -145,7 +147,11 @@ export interface Datasource {
// For initializing, either from an empty state or from persisted state
// Because this will be called at runtime, state might have a type of `any` and
// datasources should validate their arguments
- initialize: (state?: P, savedObjectReferences?: SavedObjectReference[]) => Promise;
+ initialize: (
+ state?: P,
+ savedObjectReferences?: SavedObjectReference[],
+ initialContext?: VisualizeFieldContext
+ ) => Promise;
// Given the current state, which parts should be saved?
getPersistableState: (state: T) => { state: P; savedObjectReferences: SavedObjectReference[] };
@@ -166,6 +172,11 @@ export interface Datasource {
toExpression: (state: T, layerId: string) => Ast | string | null;
getDatasourceSuggestionsForField: (state: T, field: unknown) => Array>;
+ getDatasourceSuggestionsForVisualizeField: (
+ state: T,
+ indexPatternId: string,
+ fieldName: string
+ ) => Array>;
getDatasourceSuggestionsFromCurrentState: (state: T) => Array>;
getPublicAPI: (props: PublicAPIProps) => DatasourcePublicAPI;
diff --git a/x-pack/test/functional/apps/discover/index.ts b/x-pack/test/functional/apps/discover/index.ts
index 816428f7b3cc30..93179ac68a0383 100644
--- a/x-pack/test/functional/apps/discover/index.ts
+++ b/x-pack/test/functional/apps/discover/index.ts
@@ -14,6 +14,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./async_scripted_fields'));
loadTestFile(require.resolve('./reporting'));
loadTestFile(require.resolve('./error_handling'));
+ loadTestFile(require.resolve('./visualize_field'));
loadTestFile(require.resolve('./value_suggestions'));
});
}
diff --git a/x-pack/test/functional/apps/discover/visualize_field.ts b/x-pack/test/functional/apps/discover/visualize_field.ts
new file mode 100644
index 00000000000000..b0e4cb591791b0
--- /dev/null
+++ b/x-pack/test/functional/apps/discover/visualize_field.ts
@@ -0,0 +1,76 @@
+/*
+ * 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 expect from '@kbn/expect';
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ getPageObjects, getService }: FtrProviderContext) {
+ const esArchiver = getService('esArchiver');
+ const filterBar = getService('filterBar');
+ const queryBar = getService('queryBar');
+ const testSubjects = getService('testSubjects');
+ const retry = getService('retry');
+ const PageObjects = getPageObjects([
+ 'common',
+ 'error',
+ 'discover',
+ 'timePicker',
+ 'security',
+ 'spaceSelector',
+ 'header',
+ ]);
+
+ async function setDiscoverTimeRange() {
+ await PageObjects.timePicker.setDefaultAbsoluteRange();
+ }
+
+ describe('discover field visualize button', () => {
+ beforeEach(async () => {
+ await esArchiver.loadIfNeeded('logstash_functional');
+ await esArchiver.loadIfNeeded('lens/basic');
+ await PageObjects.common.navigateToApp('discover');
+ await setDiscoverTimeRange();
+ });
+
+ after(async () => {
+ await esArchiver.unload('lens/basic');
+ });
+
+ it('shows "visualize" field button', async () => {
+ await PageObjects.discover.clickFieldListItem('bytes');
+ await PageObjects.discover.expectFieldListItemVisualize('bytes');
+ });
+
+ it('visualizes field to Lens and loads fields to the dimesion editor', async () => {
+ await PageObjects.discover.findFieldByName('bytes');
+ await PageObjects.discover.clickFieldListItemVisualize('bytes');
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await retry.try(async () => {
+ const dimensions = await testSubjects.findAll('lns-dimensionTrigger');
+ expect(dimensions).to.have.length(2);
+ expect(await dimensions[1].getVisibleText()).to.be('Average of bytes');
+ });
+ });
+
+ it('should preserve app filters in lens', async () => {
+ await filterBar.addFilter('bytes', 'is between', '3500', '4000');
+ await PageObjects.discover.findFieldByName('geo.src');
+ await PageObjects.discover.clickFieldListItemVisualize('geo.src');
+ await PageObjects.header.waitUntilLoadingHasFinished();
+
+ expect(await filterBar.hasFilter('bytes', '3,500 to 4,000')).to.be(true);
+ });
+
+ it('should preserve query in lens', async () => {
+ await queryBar.setQuery('machine.os : ios');
+ await queryBar.submitQuery();
+ await PageObjects.discover.findFieldByName('geo.dest');
+ await PageObjects.discover.clickFieldListItemVisualize('geo.dest');
+ await PageObjects.header.waitUntilLoadingHasFinished();
+
+ expect(await queryBar.getQueryString()).to.equal('machine.os : ios');
+ });
+ });
+}