Skip to content

Commit

Permalink
[TSVB][Lens] Navigate to Lens with your current configuration (elasti…
Browse files Browse the repository at this point in the history
…c#114794)

* [Lens][TSVB] Convert to Lens

* Add logic for multiple series

* Basic formula

* Fix circular dependencies

* Minor cleanup

* Fix types

* fix jest tests

* Fix test

* Change the schema, add more styling options, fix bugs

* Supports time shift and custom date interval

* Fix types

* Fix some types

* Move edit in lens button to top nav menu

* Cleanup

* Further cleanup

* Add try it badge in menu, controlled by localStorage

* Add go back to app button

* Discard changes modal and go back to TSVB

* Update by value and by reference visualizations, delete existing by ref

* Fix bug

* Apply some changes

* get title and description only if has context

* Pass originating app, title and description from the savedVis

* By ref TSVB to by ref Lens

* Match TSVB cardinality with Lens unique_count function

* Support moving average

* Fix test

* Support derivative

* Support cumulative_sum

* Add overall functions

* Support filter ratio

* Refactor code for easier testing

* Fix bug with auto interval

* Fetch types from visualizations plugin

* Pipeline aggs compatible with percentile

* Add some bugs

* Support nesred aggs

* Mini refactor and support all aggregations to Math

* Transfer terms sorting options

* Transfer axis position

* Fix translations keys

* Revert

* Fix redirectToOrigin buttion when the there is no embeddableId but comes from dashboard

* Improve context identification

* Support yExtents

* Fix bug in formula caused by changes in the main branch

* Support formatters

* Support custom label

* Cleaning up

* Fix terms bugs

* Support filter breakdown by

* Fixes math bug and escapes filter ratio query

* Add some unit tests

* Testing triggerOptions payload

* Fix console warning

* Add more unit tests on TSVB function helpers

* Adds a unit test on the vis top nav menu testing the new menu item

* Add unit tests

* Fix unsupported palette bug, clean up, add a unit test case

* Add final unit tests

* Support timeScale in derivative

* Add functional tests

* Cleanup

* Fix jest test

* Fix some bugs

* Fix some math agg bugs

* Fix more bugs

* Fixes jest test

* Fix the problem with the dashboard state

* Hides the badge and link instead of disabling it

* Changes the text

* Adds menu item vertical separator

* Enhance the appLeace confirm modal to change the confirm button text and color

* Fixes CI

* Adress code review comments

* Address some of the comments

* Fix more bugs

* Fix more bugs

* Zero decimals for formatting

* fix tests

* Navigate from dashboard to TSVB to Lens hides the appLeave modal

* Adds support for terms on a date field

* Support filter by

* Move the trigger to the visualizations plugin

* Minor

* Fix CI

* Support percentage charts

* Improve the vertical separator

* Fixes on the appLeave logic

* Remove unecessary import

* Add badge to the nav item level

* Fix jest test

* Fi filter ratio and filter by bug

* Replace all occurences of a variable

* Nest badge into the button level

* Design improvements

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
stratoula and kibanamachine authored Feb 14, 2022
1 parent 1f4a7d4 commit d364f23
Show file tree
Hide file tree
Showing 72 changed files with 4,473 additions and 132 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-core-public](./kibana-plugin-core-public.md) &gt; [AppLeaveConfirmAction](./kibana-plugin-core-public.appleaveconfirmaction.md) &gt; [buttonColor](./kibana-plugin-core-public.appleaveconfirmaction.buttoncolor.md)

## AppLeaveConfirmAction.buttonColor property

<b>Signature:</b>

```typescript
buttonColor?: ButtonColor;
```
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-core-public](./kibana-plugin-core-public.md) &gt; [AppLeaveConfirmAction](./kibana-plugin-core-public.appleaveconfirmaction.md) &gt; [confirmButtonText](./kibana-plugin-core-public.appleaveconfirmaction.confirmbuttontext.md)

## AppLeaveConfirmAction.confirmButtonText property

<b>Signature:</b>

```typescript
confirmButtonText?: string;
```
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export interface AppLeaveConfirmAction

| Property | Type | Description |
| --- | --- | --- |
| [buttonColor?](./kibana-plugin-core-public.appleaveconfirmaction.buttoncolor.md) | ButtonColor | <i>(Optional)</i> |
| [callback?](./kibana-plugin-core-public.appleaveconfirmaction.callback.md) | () =&gt; void | <i>(Optional)</i> |
| [confirmButtonText?](./kibana-plugin-core-public.appleaveconfirmaction.confirmbuttontext.md) | string | <i>(Optional)</i> |
| [text](./kibana-plugin-core-public.appleaveconfirmaction.text.md) | string | |
| [title?](./kibana-plugin-core-public.appleaveconfirmaction.title.md) | string | <i>(Optional)</i> |
| [type](./kibana-plugin-core-public.appleaveconfirmaction.type.md) | AppLeaveActionType.confirm | |
Expand Down
12 changes: 12 additions & 0 deletions src/core/public/application/application_leave.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,17 @@ describe('getLeaveAction', () => {
title: 'a title',
callback,
});
expect(
getLeaveAction((actions) =>
actions.confirm('another message', 'a title', callback, 'confirm button text', 'danger')
)
).toEqual({
type: AppLeaveActionType.confirm,
text: 'another message',
title: 'a title',
callback,
confirmButtonText: 'confirm button text',
buttonColor: 'danger',
});
});
});
19 changes: 16 additions & 3 deletions src/core/public/application/application_leave.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { ButtonColor } from '@elastic/eui';
import {
AppLeaveActionFactory,
AppLeaveActionType,
Expand All @@ -15,8 +15,21 @@ import {
} from './types';

const appLeaveActionFactory: AppLeaveActionFactory = {
confirm(text: string, title?: string, callback?: () => void) {
return { type: AppLeaveActionType.confirm, text, title, callback };
confirm(
text: string,
title?: string,
callback?: () => void,
confirmButtonText?: string,
buttonColor?: ButtonColor
) {
return {
type: AppLeaveActionType.confirm,
text,
title,
confirmButtonText,
buttonColor,
callback,
};
},
default() {
return { type: AppLeaveActionType.default };
Expand Down
2 changes: 2 additions & 0 deletions src/core/public/application/application_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ export class ApplicationService {
const confirmed = await overlays.openConfirm(action.text, {
title: action.title,
'data-test-subj': 'appLeaveConfirmModal',
confirmButtonText: action.confirmButtonText,
buttonColor: action.buttonColor,
});
if (!confirmed) {
if (action.callback) {
Expand Down
14 changes: 12 additions & 2 deletions src/core/public/application/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { ButtonColor } from '@elastic/eui';
import { Observable } from 'rxjs';
import { History } from 'history';
import { RecursiveReadonly } from '@kbn/utility-types';
Expand Down Expand Up @@ -597,6 +597,8 @@ export interface AppLeaveConfirmAction {
type: AppLeaveActionType.confirm;
text: string;
title?: string;
confirmButtonText?: string;
buttonColor?: ButtonColor;
callback?: () => void;
}

Expand All @@ -621,9 +623,17 @@ export interface AppLeaveActionFactory {
* @param text The text to display in the confirmation message
* @param title (optional) title to display in the confirmation message
* @param callback (optional) to know that the user want to stay on the page
* @param confirmButtonText (optional) text for the confirmation button
* @param buttonColor (optional) color for the confirmation button
* so we can show to the user the right UX for him to saved his/her/their changes
*/
confirm(text: string, title?: string, callback?: () => void): AppLeaveConfirmAction;
confirm(
text: string,
title?: string,
callback?: () => void,
confirmButtonText?: string,
buttonColor?: ButtonColor
): AppLeaveConfirmAction;

/**
* Returns a default action, resulting on executing the default behavior when
Expand Down
5 changes: 5 additions & 0 deletions src/core/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { Action } from 'history';
import Boom from '@hapi/boom';
import type { ButtonColor } from '@elastic/eui';
import { ByteSizeValue } from '@kbn/config-schema';
import type { Client } from '@elastic/elasticsearch';
import { ConfigPath } from '@kbn/config';
Expand Down Expand Up @@ -115,9 +116,13 @@ export enum AppLeaveActionType {
//
// @public
export interface AppLeaveConfirmAction {
// (undocumented)
buttonColor?: ButtonColor;
// (undocumented)
callback?: () => void;
// (undocumented)
confirmButtonText?: string;
// (undocumented)
text: string;
// (undocumented)
title?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@ export const buildDashboardContainer = async ({
gridData: originalPanelState.gridData,
type: incomingEmbeddable.type,
explicitInput: {
...originalPanelState.explicitInput,
...(incomingEmbeddable.type === originalPanelState.type && {
...originalPanelState.explicitInput,
}),
...incomingEmbeddable.input,
id: incomingEmbeddable.embeddableId,
},
Expand Down
13 changes: 13 additions & 0 deletions src/plugins/navigation/public/top_nav_menu/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,16 @@
.kbnTopNavMenu__badgeGroup {
margin-right: $euiSizeM;
}

.kbnTopNavMenu__betaBadgeItem {
margin-right: $euiSizeS;
vertical-align: middle;

button:hover &,
button:focus & {
text-decoration: underline;
}
button:hover & {
cursor: pointer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import { EuiButtonProps } from '@elastic/eui';
import { EuiButtonProps, EuiBetaBadgeProps } from '@elastic/eui';

export type TopNavMenuAction = (anchorElement: HTMLElement) => void;

Expand All @@ -19,6 +19,7 @@ export interface TopNavMenuData {
className?: string;
disableButton?: boolean | (() => boolean);
tooltip?: string | (() => string | undefined);
badge?: EuiBetaBadgeProps;
emphasize?: boolean;
isLoading?: boolean;
iconType?: string;
Expand Down
19 changes: 16 additions & 3 deletions src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import { upperFirst, isFunction } from 'lodash';
import React, { MouseEvent } from 'react';
import { EuiToolTip, EuiButton, EuiHeaderLink } from '@elastic/eui';
import { EuiToolTip, EuiButton, EuiHeaderLink, EuiBetaBadge } from '@elastic/eui';
import { TopNavMenuData } from './top_nav_menu_data';

export function TopNavMenuItem(props: TopNavMenuData) {
Expand All @@ -22,6 +22,19 @@ export function TopNavMenuItem(props: TopNavMenuData) {
return val!;
}

function getButtonContainer() {
if (props.badge) {
return (
<>
<EuiBetaBadge className="kbnTopNavMenu__betaBadgeItem" {...props.badge} size="s" />
{upperFirst(props.label || props.id!)}
</>
);
} else {
return upperFirst(props.label || props.id!);
}
}

function handleClick(e: MouseEvent<HTMLButtonElement>) {
if (isDisabled()) return;
props.run(e.currentTarget);
Expand All @@ -39,11 +52,11 @@ export function TopNavMenuItem(props: TopNavMenuData) {

const btn = props.emphasize ? (
<EuiButton size="s" {...commonButtonProps} fill>
{upperFirst(props.label || props.id!)}
{getButtonContainer()}
</EuiButton>
) : (
<EuiHeaderLink size="s" color="primary" {...commonButtonProps}>
{upperFirst(props.label || props.id!)}
{getButtonContainer()}
</EuiHeaderLink>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export interface Series {
chart_type: string;
color: string;
color_rules?: ColorRules[];
fill?: number;
fill?: string;
filter?: Query;
formatter: string;
hidden?: boolean;
Expand Down
7 changes: 7 additions & 0 deletions src/plugins/vis_types/timeseries/public/metrics_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from '../../../visualizations/public';
import { getDataStart } from './services';
import type { TimeseriesVisDefaultParams, TimeseriesVisParams } from './types';
import { triggerTSVBtoLensConfiguration } from './trigger_action';
import type { IndexPatternValue, Panel } from '../common/types';
import { RequestAdapter } from '../../../inspector/public';

Expand Down Expand Up @@ -167,6 +168,12 @@ export const metricsVisDefinition: VisTypeDefinition<
}
return [];
},
navigateToLens: async (params?: VisParams) => {
const triggerConfiguration = params
? await triggerTSVBtoLensConfiguration(params as Panel)
: null;
return triggerConfiguration;
},
inspectorAdapters: () => ({
requests: new RequestAdapter(),
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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 type { DataView } from '../../../../data/common';
import { getDataSourceInfo } from './get_datasource_info';
const dataViewsMap: Record<string, DataView> = {
test1: { id: 'test1', title: 'test1', timeFieldName: 'timeField1' } as DataView,
test2: {
id: 'test2',
title: 'test2',
timeFieldName: 'timeField2',
} as DataView,
test3: { id: 'test3', title: 'test3', timeFieldName: 'timeField3' } as DataView,
};

const getDataview = (id: string): DataView | undefined => dataViewsMap[id];
jest.mock('../services', () => {
return {
getDataStart: jest.fn(() => {
return {
dataViews: {
getDefault: jest.fn(() => {
return { id: '12345', title: 'default', timeFieldName: '@timestamp' };
}),
get: getDataview,
},
};
}),
};
});

describe('getDataSourceInfo', () => {
test('should return the default dataview if model_indexpattern is string', async () => {
const { indexPatternId, timeField } = await getDataSourceInfo(
'test',
undefined,
false,
undefined
);
expect(indexPatternId).toBe('12345');
expect(timeField).toBe('@timestamp');
});

test('should return the correct dataview if model_indexpattern is object', async () => {
const { indexPatternId, timeField } = await getDataSourceInfo(
{ id: 'dataview-1-id' },
'timeField-1',
false,
undefined
);
expect(indexPatternId).toBe('dataview-1-id');
expect(timeField).toBe('timeField-1');
});

test('should fetch the correct data if overwritten dataview is provided', async () => {
const { indexPatternId, timeField } = await getDataSourceInfo(
{ id: 'dataview-1-id' },
'timeField-1',
true,
{ id: 'test2' }
);
expect(indexPatternId).toBe('test2');
expect(timeField).toBe('timeField2');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 { fetchIndexPattern, isStringTypeIndexPattern } from '../../common/index_patterns_utils';
import type { IndexPatternValue } from '../../common/types';
import { getDataStart } from '../services';

export const getDataSourceInfo = async (
modelIndexPattern: IndexPatternValue,
modelTimeField: string | undefined,
isOverwritten: boolean,
overwrittenIndexPattern: IndexPatternValue | undefined
) => {
const { dataViews } = getDataStart();
let indexPatternId =
modelIndexPattern && !isStringTypeIndexPattern(modelIndexPattern) ? modelIndexPattern.id : '';

let timeField = modelTimeField;
// handle override index pattern
if (isOverwritten) {
const { indexPattern } = await fetchIndexPattern(overwrittenIndexPattern, dataViews);
if (indexPattern) {
indexPatternId = indexPattern.id ?? '';
timeField = indexPattern.timeFieldName;
}
}

if (!indexPatternId) {
const defaultIndex = await dataViews.getDefault();
indexPatternId = defaultIndex?.id ?? '';
timeField = defaultIndex?.timeFieldName;
}
if (!timeField) {
const indexPattern = await dataViews.get(indexPatternId);
timeField = indexPattern.timeFieldName;
}

return {
indexPatternId,
timeField,
};
};
Loading

0 comments on commit d364f23

Please sign in to comment.