Skip to content

Commit

Permalink
[Lens] Navigate from discover to lens (#77873) (#79626)
Browse files Browse the repository at this point in the history
* Create lens action and unregister the visualize one

* remove console

* Implement Discover to Lens, wip, missing tests

* Add unit tests

* fix embed lens to empty dashboard functional tests

* fix suggestions on save

* Fix bug on save button, query and filters should be transferred from discover

* Add functional test for the navigation from Discover to Lens

* PR update after code review

* unregister visualize action only if the action exists

* Change the test to not be flaky

* Move suggestions to editor frame and hide the emptyWorkspace for visualize field

* Update ui actions docs

* Add a retry to remove test flakiness

* Fix bug of infinite loader when removing the y axis dimension

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
# Conflicts:
#	docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md
#	docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.md
#	docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md
#	docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md
#	docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md
#	docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md
#	docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md
#	docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md
#	src/plugins/ui_actions/public/public.api.md
  • Loading branch information
stratoula authored Oct 6, 2020
1 parent abe78f0 commit 3144c05
Show file tree
Hide file tree
Showing 29 changed files with 529 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) &gt; [ACTION\_VISUALIZE\_LENS\_FIELD](./kibana-plugin-plugins-ui_actions-public.action_visualize_lens_field.md)

## ACTION\_VISUALIZE\_LENS\_FIELD variable

<b>Signature:</b>

```typescript
ACTION_VISUALIZE_LENS_FIELD = "ACTION_VISUALIZE_LENS_FIELD"
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) &gt; [ActionContextMapping](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md) &gt; [ACTION\_VISUALIZE\_LENS\_FIELD](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_lens_field.md)

## ActionContextMapping.ACTION\_VISUALIZE\_LENS\_FIELD property

<b>Signature:</b>

```typescript
[ACTION_VISUALIZE_LENS_FIELD]: VisualizeFieldContext;
```
1 change: 1 addition & 0 deletions src/plugins/ui_actions/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export {
VisualizeFieldContext,
ACTION_VISUALIZE_FIELD,
ACTION_VISUALIZE_GEO_FIELD,
ACTION_VISUALIZE_LENS_FIELD,
} from './types';
export {
ActionByType,
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/ui_actions/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { AGGS_TERMS_SIZE_SETTING } from '../../common/constants';

export const visualizeFieldAction = createAction<typeof ACTION_VISUALIZE_FIELD>({
type: ACTION_VISUALIZE_FIELD,
id: ACTION_VISUALIZE_FIELD,
getDisplayName: () =>
i18n.translate('visualize.discover.visualizeFieldLabel', {
defaultMessage: 'Visualize field',
Expand Down
5 changes: 3 additions & 2 deletions x-pack/plugins/lens/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/lens/public/app_plugin/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ describe('Lens App', () => {
},
"doc": undefined,
"filters": Array [],
"initialContext": undefined,
"onChange": [Function],
"onError": [Function],
"query": Object {
Expand Down
12 changes: 9 additions & 3 deletions x-pack/plugins/lens/public/app_plugin/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export function App({
incomingState,
redirectToOrigin,
setHeaderActionMenu,
initialContext,
}: LensAppProps) {
const {
data,
Expand All @@ -67,7 +68,7 @@ export function App({
const [state, setState] = useState<LensAppState>(() => {
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: [],
Expand Down Expand Up @@ -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: () => {
Expand Down Expand Up @@ -187,6 +191,7 @@ export function App({
uiSettings,
data.query,
history,
initialContext,
]);

useEffect(() => {
Expand Down Expand Up @@ -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 }));
Expand Down
9 changes: 8 additions & 1 deletion x-pack/plugins/lens/public/app_plugin/mounter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 = {
Expand Down Expand Up @@ -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
}
/>
);
};
Expand Down
10 changes: 10 additions & 0 deletions x-pack/plugins/lens/public/app_plugin/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 '..';

Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});

Expand Down Expand Up @@ -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(
<EditorFrame
{...getDefaultProps()}
initialContext={{
indexPatternId: '1',
fieldName: 'test',
}}
visualizationMap={{
testVis: mockVisualization,
}}
datasourceMap={{
testDatasource: mockDatasource,
testDatasource2: mockDatasource2,
}}
initialDatasourceId="testDatasource"
initialVisualizationId="testVis"
ExpressionRenderer={expressionRendererMock}
/>
);
});

expect(mockDatasource.getDatasourceSuggestionsForVisualizeField).toHaveBeenCalled();
});

it('should fetch suggestions of currently active datasource', async () => {
await act(async () => {
mount(
Expand Down Expand Up @@ -1208,6 +1234,7 @@ describe('editor_frame', () => {
...mockDatasource,
getDatasourceSuggestionsForField: () => [generateSuggestion()],
getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()],
getDatasourceSuggestionsForVisualizeField: () => [generateSuggestion()],
},
}}
initialDatasourceId="testDatasource"
Expand Down Expand Up @@ -1274,6 +1301,7 @@ describe('editor_frame', () => {
...mockDatasource,
getDatasourceSuggestionsForField: () => [generateSuggestion()],
getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()],
getDatasourceSuggestionsForVisualizeField: () => [generateSuggestion()],
renderDataPanel: (_element, { dragDropContext: { setDragging, dragging } }) => {
if (dragging !== 'draggedField') {
setDragging('draggedField');
Expand Down Expand Up @@ -1370,6 +1398,7 @@ describe('editor_frame', () => {
...mockDatasource,
getDatasourceSuggestionsForField: () => [generateSuggestion()],
getDatasourceSuggestionsFromCurrentState: () => [generateSuggestion()],
getDatasourceSuggestionsForVisualizeField: () => [generateSuggestion()],
renderDataPanel: (_element, { dragDropContext: { setDragging, dragging } }) => {
if (dragging !== 'draggedField') {
setDragging('draggedField');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand All @@ -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];
Expand All @@ -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 }]) => {
Expand All @@ -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 = {
Expand Down Expand Up @@ -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(
() => {
Expand Down Expand Up @@ -275,6 +302,7 @@ export function EditorFrame(props: EditorFrameProps) {
ExpressionRenderer={props.ExpressionRenderer}
core={props.core}
plugins={props.plugins}
visualizeTriggerFieldContext={visualizeTriggerFieldContext}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Datasource>,
datasourceStates: Record<string, { state: unknown; isLoading: boolean }>,
references?: SavedObjectReference[]
references?: SavedObjectReference[],
initialContext?: VisualizeFieldContext
) {
const states: Record<string, { isLoading: boolean; state: unknown }> = {};
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 };
});
Expand Down
Loading

0 comments on commit 3144c05

Please sign in to comment.