diff --git a/src/plugins/vis_types/timeseries/public/application/components/series_editor.js b/src/plugins/vis_types/timeseries/public/application/components/series_editor.js
index 7bd72b85edc1d..531075b36244b 100644
--- a/src/plugins/vis_types/timeseries/public/application/components/series_editor.js
+++ b/src/plugins/vis_types/timeseries/public/application/components/series_editor.js
@@ -65,6 +65,10 @@ export class SeriesEditor extends Component {
}
};
+ handleSeriesChange = (doc) => {
+ handleChange(this.props, doc);
+ };
+
render() {
const { limit, model, name, fields, colorPicker } = this.props;
const list = model[name].filter((val, index) => index < (limit || Infinity));
@@ -89,7 +93,7 @@ export class SeriesEditor extends Component {
disableDelete={model[name].length < 2}
fields={fields}
onAdd={() => handleAdd(this.props, newSeriesFn)}
- onChange={(doc) => handleChange(this.props, doc)}
+ onChange={this.handleSeriesChange}
onClone={() => this.handleClone(row)}
onDelete={() => handleDelete(this.props, row)}
model={row}
diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/config.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/config.js
index 48ba0e5403665..506ce0dbdf2a9 100644
--- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/config.js
+++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/config.js
@@ -35,7 +35,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { getDefaultQueryLanguage } from '../../lib/get_default_query_language';
import { checkIfNumericMetric } from '../../lib/check_if_numeric_metric';
import { QueryBarWrapper } from '../../query_bar_wrapper';
-import { DATA_FORMATTERS } from '../../../../../common/enums';
+import { DATA_FORMATTERS, BUCKET_TYPES } from '../../../../../common/enums';
import { isConfigurationFeatureEnabled } from '../../../../../common/check_ui_restrictions';
import { filterCannotBeAppliedErrorMessage } from '../../../../../common/errors';
import { tsvbEditorRowStyles } from '../../../styles/common.styles';
@@ -50,13 +50,20 @@ class TableSeriesConfigUi extends Component {
}
}
+ handleAggregateByChange = (selectedOptions) => {
+ this.props.onChange({
+ aggregate_by: selectedOptions?.[0],
+ });
+ };
+
+ handleSelectChange = createSelectHandler(this.props.onChange);
+ handleTextChange = createTextHandler(this.props.onChange);
+
changeModelFormatter = (formatter) => this.props.onChange({ formatter });
render() {
const defaults = { offset_time: '', value_template: '{{value}}' };
const model = { ...defaults, ...this.props.model };
- const handleSelectChange = createSelectHandler(this.props.onChange);
- const handleTextChange = createTextHandler(this.props.onChange);
const htmlId = htmlIdGenerator();
const functionOptions = [
@@ -160,7 +167,7 @@ class TableSeriesConfigUi extends Component {
fullWidth
>
-
+
@@ -222,11 +229,7 @@ class TableSeriesConfigUi extends Component {
fields={this.props.fields}
indexPattern={this.props.panel.index_pattern}
value={model.aggregate_by}
- onChange={(value) =>
- this.props.onChange({
- aggregate_by: value?.[0],
- })
- }
+ onChange={this.handleAggregateByChange}
fullWidth
restrict={[
KBN_FIELD_TYPES.NUMBER,
@@ -236,7 +239,7 @@ class TableSeriesConfigUi extends Component {
KBN_FIELD_TYPES.STRING,
]}
uiRestrictions={this.props.uiRestrictions}
- type={'terms'}
+ type={BUCKET_TYPES.TERMS}
/>
@@ -251,9 +254,10 @@ class TableSeriesConfigUi extends Component {
fullWidth
>
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.test.ts
index cbc899981717b..9943cfa627e23 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.test.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.test.ts
@@ -6,10 +6,11 @@
* Side Public License, v 1.
*/
+import { Vis } from '@kbn/visualizations-plugin/public';
import { METRIC_TYPES } from '@kbn/data-plugin/public';
import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub';
import { TSVB_METRIC_TYPES } from '../../../common/enums';
-import { Metric } from '../../../common/types';
+import { Panel, Metric } from '../../../common/types';
import { convertToLens } from '.';
import { createPanel, createSeries } from '../lib/__mocks__';
import { AvgColumn } from '../lib/convert';
@@ -58,6 +59,10 @@ describe('convertToLens', () => {
series: [createSeries({ metrics: [metric] })],
});
+ const vis = {
+ params: model,
+ } as Vis;
+
const metricColumn: AvgColumn = {
columnId: 'col-id',
dataType: 'number',
@@ -89,51 +94,55 @@ describe('convertToLens', () => {
test('should return null for invalid metrics', async () => {
mockIsValidMetrics.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockIsValidMetrics).toBeCalledTimes(1);
});
test('should return null for invalid or unsupported metrics', async () => {
mockGetMetricsColumns.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetMetricsColumns).toBeCalledTimes(1);
});
test('should return null for invalid or unsupported buckets', async () => {
mockGetBucketsColumns.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetBucketsColumns).toBeCalledTimes(1);
});
test('should return null if metric is staticValue', async () => {
const result = await convertToLens({
- ...model,
- series: [
- {
- ...model.series[0],
- metrics: [...model.series[0].metrics, { type: TSVB_METRIC_TYPES.STATIC } as Metric],
- },
- ],
- });
+ params: {
+ ...model,
+ series: [
+ {
+ ...model.series[0],
+ metrics: [...model.series[0].metrics, { type: TSVB_METRIC_TYPES.STATIC } as Metric],
+ },
+ ],
+ },
+ } as Vis);
expect(result).toBeNull();
expect(mockGetDataSourceInfo).toBeCalledTimes(0);
});
test('should return null if only series agg is specified', async () => {
const result = await convertToLens({
- ...model,
- series: [
- {
- ...model.series[0],
- metrics: [
- { type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'min', id: 'some-id' } as Metric,
- ],
- },
- ],
- });
+ params: {
+ ...model,
+ series: [
+ {
+ ...model.series[0],
+ metrics: [
+ { type: TSVB_METRIC_TYPES.SERIES_AGG, function: 'min', id: 'some-id' } as Metric,
+ ],
+ },
+ ],
+ },
+ } as Vis);
expect(result).toBeNull();
});
@@ -142,7 +151,7 @@ describe('convertToLens', () => {
mockGetSeriesAgg.mockReturnValue({ metrics: [metric] });
mockGetConfigurationForGauge.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
});
@@ -151,8 +160,8 @@ describe('convertToLens', () => {
mockGetSeriesAgg.mockReturnValue({ metrics: [metric] });
mockGetConfigurationForGauge.mockReturnValue({});
- const result = await convertToLens(
- createPanel({
+ const result = await convertToLens({
+ params: createPanel({
series: [
createSeries({
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
@@ -163,8 +172,8 @@ describe('convertToLens', () => {
hidden: false,
}),
],
- })
- );
+ }),
+ } as Vis);
expect(result).toBeDefined();
expect(result?.type).toBe('lnsMetric');
});
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.ts
index b97f8d59e9537..87d5333d4be51 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/gauge/index.ts
@@ -45,7 +45,10 @@ const getMaxFormula = (metric: Metric, column?: Column) => {
}))`;
};
-export const convertToLens: ConvertTsvbToLensVisualization = async (model, timeRange) => {
+export const convertToLens: ConvertTsvbToLensVisualization = async (
+ { params: model },
+ timeRange
+) => {
const dataViews = getDataViewsStart();
const series = model.series[0];
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts
index 309f066b18f29..a97395f64c113 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts
@@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
+import { Vis } from '@kbn/visualizations-plugin/public';
import type { Panel } from '../../common/types';
import { convertTSVBtoLensConfiguration } from '.';
@@ -42,7 +43,9 @@ describe('convertTSVBtoLensConfiguration', () => {
...model,
type: 'markdown',
} as Panel;
- const triggerOptions = await convertTSVBtoLensConfiguration(metricModel);
+ const triggerOptions = await convertTSVBtoLensConfiguration({
+ params: metricModel,
+ } as Vis);
expect(triggerOptions).toBeNull();
});
@@ -51,7 +54,9 @@ describe('convertTSVBtoLensConfiguration', () => {
...model,
use_kibana_indexes: false,
};
- const triggerOptions = await convertTSVBtoLensConfiguration(stringIndexPatternModel);
+ const triggerOptions = await convertTSVBtoLensConfiguration({
+ params: stringIndexPatternModel,
+ } as Vis);
expect(triggerOptions).toBeNull();
});
});
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts
index a3d08e89e91a2..3e1982aa0903e 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts
@@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
+import { Vis } from '@kbn/visualizations-plugin/public';
import { TimeRange } from '@kbn/data-plugin/common';
import type { Panel } from '../../common/types';
import { PANEL_TYPES } from '../../common/enums';
@@ -29,6 +30,10 @@ const getConvertFnByType = (type: PANEL_TYPES) => {
const { convertToLens } = await import('./gauge');
return convertToLens;
},
+ [PANEL_TYPES.TABLE]: async () => {
+ const { convertToLens } = await import('./table');
+ return convertToLens;
+ },
};
return convertionFns[type]?.();
@@ -39,17 +44,17 @@ const getConvertFnByType = (type: PANEL_TYPES) => {
* Returns the Lens model, only if it is supported. If not, it returns null.
* In case of null, the menu item is disabled and the user can't navigate to Lens.
*/
-export const convertTSVBtoLensConfiguration = async (model: Panel, timeRange?: TimeRange) => {
+export const convertTSVBtoLensConfiguration = async (vis: Vis, timeRange?: TimeRange) => {
// Disables the option for not supported charts, for the string mode and for series with annotations
- if (!model.use_kibana_indexes) {
+ if (!vis.params.use_kibana_indexes) {
return null;
}
// Disables if model is invalid
- if (model.isModelInvalid) {
+ if (vis.params.isModelInvalid) {
return null;
}
- const convertFn = await getConvertFnByType(model.type);
+ const convertFn = await getConvertFnByType(vis.params.type);
- return (await convertFn?.(model, timeRange)) ?? null;
+ return (await convertFn?.(vis, timeRange)) ?? null;
};
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts
index 9cb0238f9d265..0fabef6eebdbe 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts
@@ -14,7 +14,7 @@ import { getConfigurationForMetric, getConfigurationForGauge } from '.';
const mockGetPalette = jest.fn();
-jest.mock('./palette', () => ({
+jest.mock('../palette', () => ({
getPalette: jest.fn(() => mockGetPalette()),
}));
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts
index 7b49d604b2343..e6814b0797a1a 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts
@@ -10,7 +10,7 @@ import color from 'color';
import { MetricVisConfiguration } from '@kbn/visualizations-plugin/common';
import { Panel } from '../../../../../common/types';
import { Column, Layer } from '../../convert';
-import { getPalette } from './palette';
+import { getPalette } from '../palette';
import { findMetricColumn, getMetricWithCollapseFn } from '../../../utils';
export const getConfigurationForMetric = (
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/palette/index.test.ts
similarity index 99%
rename from src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts
rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/palette/index.test.ts
index b7356f094f91a..059f0372c451a 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.test.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/palette/index.test.ts
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import { getPalette } from './palette';
+import { getPalette } from '.';
describe('getPalette', () => {
const baseColor = '#fff';
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/palette/index.ts
similarity index 83%
rename from src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts
rename to src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/palette/index.ts
index 4079ffb396647..159804a1ea29b 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/palette.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/palette/index.ts
@@ -8,7 +8,7 @@
import color from 'color';
import { ColorStop, CustomPaletteParams, PaletteOutput } from '@kbn/coloring';
import { uniqBy } from 'lodash';
-import { Panel } from '../../../../../common/types';
+import { Panel, Series } from '../../../../../common/types';
const Operators = {
GTE: 'gte',
@@ -24,9 +24,11 @@ type ColorStopsWithMinMax = Pick<
type MetricColorRules = Exclude;
type GaugeColorRules = Exclude;
+type SeriesColorRules = Exclude;
type MetricColorRule = MetricColorRules[number];
type GaugeColorRule = GaugeColorRules[number];
+type SeriesColorRule = SeriesColorRules[number];
type ValidMetricColorRule = Omit &
(
@@ -44,31 +46,47 @@ type ValidGaugeColorRule = Omit & {
gauge: Exclude;
};
+type ValidSeriesColorRule = Omit & {
+ text: Exclude;
+};
+
const isValidColorRule = (
rule: MetricColorRule | GaugeColorRule
-): rule is ValidMetricColorRule | ValidGaugeColorRule => {
+): rule is ValidMetricColorRule | ValidGaugeColorRule | ValidSeriesColorRule => {
const { background_color: bColor, color: textColor } = rule as MetricColorRule;
const { gauge } = rule as GaugeColorRule;
+ const { text } = rule as SeriesColorRule;
- return rule.operator && (bColor ?? textColor ?? gauge) && rule.value !== undefined ? true : false;
+ return Boolean(
+ rule.operator && (bColor ?? textColor ?? gauge ?? text) && rule.value !== undefined
+ );
};
const isMetricColorRule = (
- rule: ValidMetricColorRule | ValidGaugeColorRule
+ rule: ValidMetricColorRule | ValidGaugeColorRule | ValidSeriesColorRule
): rule is ValidMetricColorRule => {
const metricRule = rule as ValidMetricColorRule;
return metricRule.background_color ?? metricRule.color ? true : false;
};
-const getColor = (rule: ValidMetricColorRule | ValidGaugeColorRule) => {
+const isGaugeColorRule = (
+ rule: ValidMetricColorRule | ValidGaugeColorRule | ValidSeriesColorRule
+): rule is ValidGaugeColorRule => {
+ const metricRule = rule as ValidGaugeColorRule;
+ return Boolean(metricRule.gauge);
+};
+
+const getColor = (rule: ValidMetricColorRule | ValidGaugeColorRule | ValidSeriesColorRule) => {
if (isMetricColorRule(rule)) {
return rule.background_color ?? rule.color;
+ } else if (isGaugeColorRule(rule)) {
+ return rule.gauge;
}
- return rule.gauge;
+ return rule.text;
};
const getColorStopsWithMinMaxForAllGteOrWithLte = (
- rules: Array,
+ rules: Array,
tailOperator: string,
baseColor?: string
): ColorStopsWithMinMax => {
@@ -125,7 +143,7 @@ const getColorStopsWithMinMaxForAllGteOrWithLte = (
};
const getColorStopsWithMinMaxForLtWithLte = (
- rules: Array
+ rules: Array
): ColorStopsWithMinMax => {
const lastRule = rules[rules.length - 1];
const colorStops = rules.reduce((colors, rule, index, rulesArr) => {
@@ -166,7 +184,7 @@ const getColorStopsWithMinMaxForLtWithLte = (
};
const getColorStopWithMinMaxForLte = (
- rule: ValidMetricColorRule | ValidGaugeColorRule
+ rule: ValidMetricColorRule | ValidGaugeColorRule | ValidSeriesColorRule
): ColorStopsWithMinMax => {
const colorStop = {
color: color(getColor(rule)).hex(),
@@ -183,7 +201,7 @@ const getColorStopWithMinMaxForLte = (
};
const getColorStopWithMinMaxForGte = (
- rule: ValidMetricColorRule | ValidGaugeColorRule,
+ rule: ValidMetricColorRule | ValidGaugeColorRule | ValidSeriesColorRule,
baseColor?: string
): ColorStopsWithMinMax => {
const colorStop = {
@@ -224,12 +242,14 @@ const getCustomPalette = (
};
export const getPalette = (
- rules: MetricColorRules | GaugeColorRules,
+ rules: MetricColorRules | GaugeColorRules | SeriesColorRules,
baseColor?: string
): PaletteOutput | null | undefined => {
- const validRules = (rules as Array).filter<
- ValidMetricColorRule | ValidGaugeColorRule
- >((rule): rule is ValidMetricColorRule | ValidGaugeColorRule => isValidColorRule(rule));
+ const validRules = (rules as Array).filter<
+ ValidMetricColorRule | ValidGaugeColorRule | ValidSeriesColorRule
+ >((rule): rule is ValidMetricColorRule | ValidGaugeColorRule | ValidSeriesColorRule =>
+ isValidColorRule(rule)
+ );
validRules.sort((rule1, rule2) => {
return rule1.value! - rule2.value!;
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/table/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/table/index.test.ts
new file mode 100644
index 0000000000000..6deeaaa39fb8f
--- /dev/null
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/table/index.test.ts
@@ -0,0 +1,56 @@
+/*
+ * 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 { createSeries } from '../../__mocks__';
+import { getColumnState } from '.';
+
+const mockGetPalette = jest.fn();
+
+jest.mock('../palette', () => ({
+ getPalette: jest.fn(() => mockGetPalette()),
+}));
+
+describe('getColumnState', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockGetPalette.mockReturnValue({ id: 'custom' });
+ });
+
+ test('should return column state without palette if series is not provided', () => {
+ const config = getColumnState('test');
+ expect(config).toEqual({
+ columnId: 'test',
+ alignment: 'left',
+ colorMode: 'none',
+ });
+ expect(mockGetPalette).toBeCalledTimes(0);
+ });
+
+ test('should return column state with palette if series is provided', () => {
+ const config = getColumnState('test', undefined, createSeries());
+ expect(config).toEqual({
+ columnId: 'test',
+ alignment: 'left',
+ colorMode: 'text',
+ palette: { id: 'custom' },
+ });
+ expect(mockGetPalette).toBeCalledTimes(1);
+ });
+
+ test('should return column state with collapseFn if collapseFn is provided', () => {
+ const config = getColumnState('test', 'max', createSeries());
+ expect(config).toEqual({
+ columnId: 'test',
+ alignment: 'left',
+ colorMode: 'text',
+ palette: { id: 'custom' },
+ collapseFn: 'max',
+ });
+ expect(mockGetPalette).toBeCalledTimes(1);
+ });
+});
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/table/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/table/index.ts
new file mode 100644
index 0000000000000..7f152fa218842
--- /dev/null
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/table/index.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 { Series } from '../../../../../common/types';
+import { getPalette } from '../palette';
+
+export const getColumnState = (columnId: string, collapseFn?: string, series?: Series) => {
+ const palette = series ? getPalette(series.color_rules ?? []) : undefined;
+ return {
+ columnId,
+ alignment: 'left' as const,
+ colorMode: palette ? 'text' : 'none',
+ ...(palette ? { palette } : {}),
+ ...(collapseFn ? { collapseFn } : {}),
+ };
+};
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/column.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/column.ts
index c06cc3e722279..2be4e09bf8898 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/column.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/column.ts
@@ -32,7 +32,7 @@ interface ExtraColumnFields {
const isSupportedFormat = (format: string) => ['bytes', 'number', 'percent'].includes(format);
-export const getFormat = (series: Series): FormatParams => {
+export const getFormat = (series: Pick): FormatParams => {
let suffix;
if (!series.formatter || series.formatter === 'default') {
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/date_histogram.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/date_histogram.ts
index f2173cf56b469..0887ad1168ddf 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/date_histogram.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/date_histogram.ts
@@ -9,28 +9,36 @@
import type { DataView } from '@kbn/data-views-plugin/common';
import uuid from 'uuid';
import { DateHistogramParams, DataType } from '@kbn/visualizations-plugin/common/convert_to_lens';
-import { DateHistogramColumn } from './types';
-import type { Panel, Series } from '../../../../common/types';
+import { DateHistogramColumn, DateHistogramSeries } from './types';
+import type { Panel } from '../../../../common/types';
const getInterval = (interval?: string) => {
return interval && !interval?.includes('=') ? interval : 'auto';
};
-export const convertToDateHistogramParams = (model: Panel, series: Series): DateHistogramParams => {
+export const convertToDateHistogramParams = (
+ model: Panel | undefined,
+ series: DateHistogramSeries,
+ includeEmptyRows: boolean = true
+): DateHistogramParams => {
return {
- interval: getInterval(series.override_index_pattern ? series.series_interval : model.interval),
+ interval: getInterval(series.override_index_pattern ? series.series_interval : model?.interval),
dropPartials: series.override_index_pattern
? series.series_drop_last_bucket > 0
- : model.drop_last_bucket > 0,
- includeEmptyRows: true,
+ : (model?.drop_last_bucket ?? 0) > 0,
+ includeEmptyRows,
};
};
export const convertToDateHistogramColumn = (
- model: Panel,
- series: Series,
+ model: Panel | undefined,
+ series: DateHistogramSeries,
dataView: DataView,
- { fieldName, isSplit }: { fieldName: string; isSplit: boolean }
+ {
+ fieldName,
+ isSplit,
+ includeEmptyRows = true,
+ }: { fieldName: string; isSplit: boolean; includeEmptyRows?: boolean }
): DateHistogramColumn | null => {
const dateField = dataView.getFieldByName(fieldName);
@@ -38,7 +46,7 @@ export const convertToDateHistogramColumn = (
return null;
}
- const params = convertToDateHistogramParams(model, series);
+ const params = convertToDateHistogramParams(model, series, includeEmptyRows);
return {
columnId: uuid(),
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/filters.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/filters.ts
index 9134504813b68..05d74337e848d 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/filters.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/filters.ts
@@ -8,10 +8,9 @@
import uuid from 'uuid';
import { FiltersParams } from '@kbn/visualizations-plugin/common/convert_to_lens';
-import { FiltersColumn } from './types';
-import type { Series } from '../../../../common/types';
+import { FiltersColumn, FiltersSeries } from './types';
-export const convertToFiltersParams = (series: Series): FiltersParams => {
+export const convertToFiltersParams = (series: FiltersSeries): FiltersParams => {
const splitFilters = [];
if (series.split_mode === 'filter' && series.filter) {
splitFilters.push({ filter: series.filter });
@@ -35,7 +34,10 @@ export const convertToFiltersParams = (series: Series): FiltersParams => {
};
};
-export const convertToFiltersColumn = (series: Series, isSplit: boolean): FiltersColumn | null => {
+export const convertToFiltersColumn = (
+ series: FiltersSeries,
+ isSplit: boolean
+): FiltersColumn | null => {
const params = convertToFiltersParams(series);
if (!params.filters.length) {
return null;
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/terms.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/terms.ts
index 977de1947d4f8..c31d8dca68ced 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/terms.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/terms.ts
@@ -9,16 +9,15 @@
import type { DataView } from '@kbn/data-views-plugin/common';
import { DataType, TermsParams } from '@kbn/visualizations-plugin/common';
import uuid from 'uuid';
-import { Series } from '../../../../common/types';
import { excludeMetaFromColumn, getFormat, isColumnWithMeta } from './column';
-import { Column, TermsColumn } from './types';
+import { Column, TermsColumn, TermsSeries } from './types';
interface OrderByWithAgg {
orderAgg?: TermsParams['orderAgg'];
orderBy: TermsParams['orderBy'];
}
-const getOrderByWithAgg = (series: Series, columns: Column[]): OrderByWithAgg | null => {
+const getOrderByWithAgg = (series: TermsSeries, columns: Column[]): OrderByWithAgg | null => {
if (series.terms_order_by === '_key') {
return { orderBy: { type: 'alphabetical' } };
}
@@ -56,7 +55,7 @@ const getOrderByWithAgg = (series: Series, columns: Column[]): OrderByWithAgg |
};
export const convertToTermsParams = (
- series: Series,
+ series: TermsSeries,
columns: Column[],
secondaryFields: string[]
): TermsParams | null => {
@@ -84,10 +83,11 @@ export const convertToTermsParams = (
export const convertToTermsColumn = (
termFields: [string, ...string[]],
- series: Series,
+ series: TermsSeries,
columns: Column[],
dataView: DataView,
- isSplit: boolean = false
+ isSplit: boolean = false,
+ label?: string
): TermsColumn | null => {
const [baseField, ...secondaryFields] = termFields;
const field = dataView.getFieldByName(baseField);
@@ -108,6 +108,7 @@ export const convertToTermsColumn = (
sourceField: field.name,
isBucketed: true,
isSplit,
+ label,
params: { ...params, ...getFormat(series) },
};
};
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts
index 4b3b6c582f915..943550aee0066 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/convert/types.ts
@@ -117,4 +117,24 @@ export interface CommonColumnConverterArgs {
dataView: DataView;
}
+export type TermsSeries = Pick<
+ Series,
+ | 'split_mode'
+ | 'terms_direction'
+ | 'terms_order_by'
+ | 'terms_size'
+ | 'terms_include'
+ | 'terms_exclude'
+ | 'terms_field'
+ | 'formatter'
+ | 'value_template'
+>;
+
+export type FiltersSeries = Pick;
+
+export type DateHistogramSeries = Pick<
+ Series,
+ 'split_mode' | 'override_index_pattern' | 'series_interval' | 'series_drop_last_bucket'
+>;
+
export { FiltersColumn, TermsColumn, DateHistogramColumn };
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts
index debe064940c8e..a2130de36abd4 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts
@@ -68,6 +68,7 @@ const supportedPanelTypes: readonly PANEL_TYPES[] = [
PANEL_TYPES.TOP_N,
PANEL_TYPES.METRIC,
PANEL_TYPES.GAUGE,
+ PANEL_TYPES.TABLE,
];
const supportedTimeRangeModes: readonly TIME_RANGE_DATA_MODES[] = [
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/buckets_columns.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/buckets_columns.ts
index c0aa201de6837..8ca184d443a68 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/buckets_columns.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/buckets_columns.ts
@@ -7,18 +7,21 @@
*/
import type { DataView } from '@kbn/data-views-plugin/common';
-import { Series, Panel } from '../../../../common/types';
+import { Panel } from '../../../../common/types';
import { getFieldsForTerms } from '../../../../common/fields_utils';
import {
Column,
convertToFiltersColumn,
convertToDateHistogramColumn,
convertToTermsColumn,
+ TermsSeries,
+ FiltersSeries,
+ DateHistogramSeries,
} from '../convert';
import { getValidColumns } from './columns';
export const isSplitWithDateHistogram = (
- series: Series,
+ series: TermsSeries,
splitFields: string[],
dataView: DataView
) => {
@@ -39,27 +42,49 @@ export const isSplitWithDateHistogram = (
return false;
};
+const isFiltersSeries = (
+ series: DateHistogramSeries | TermsSeries | FiltersSeries
+): series is FiltersSeries => {
+ return series.split_mode === 'filters' || series.split_mode === 'filter';
+};
+
+const isTermsSeries = (
+ series: DateHistogramSeries | TermsSeries | FiltersSeries
+): series is TermsSeries => {
+ return series.split_mode === 'terms';
+};
+
+const isDateHistogramSeries = (
+ series: DateHistogramSeries | TermsSeries | FiltersSeries,
+ isDateHistogram: boolean
+): series is DateHistogramSeries => {
+ return isDateHistogram && series.split_mode === 'terms';
+};
+
export const getBucketsColumns = (
- model: Panel,
- series: Series,
+ model: Panel | undefined,
+ series: DateHistogramSeries | TermsSeries | FiltersSeries,
columns: Column[],
dataView: DataView,
- isSplit: boolean = false
+ isSplit: boolean = false,
+ label?: string,
+ includeEmptyRowsForDateHistogram: boolean = true
) => {
- if (series.split_mode === 'filters' || series.split_mode === 'filter') {
+ if (isFiltersSeries(series)) {
const filterColumn = convertToFiltersColumn(series, true);
return getValidColumns([filterColumn]);
}
- if (series.split_mode === 'terms') {
+ if (isTermsSeries(series)) {
const splitFields = getFieldsForTerms(series.terms_field);
const isDateHistogram = isSplitWithDateHistogram(series, splitFields, dataView);
if (isDateHistogram === null) {
return null;
}
- if (isDateHistogram) {
+ if (isDateHistogramSeries(series, isDateHistogram)) {
const dateHistogramColumn = convertToDateHistogramColumn(model, series, dataView, {
fieldName: splitFields[0],
isSplit: true,
+ includeEmptyRows: includeEmptyRowsForDateHistogram,
});
return getValidColumns(dateHistogramColumn);
}
@@ -73,7 +98,8 @@ export const getBucketsColumns = (
series,
columns,
dataView,
- isSplit
+ isSplit,
+ label
);
return getValidColumns(termsColumn);
}
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.test.ts
index 9407599573d9d..6d62994be4447 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.test.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.test.ts
@@ -6,10 +6,12 @@
* Side Public License, v 1.
*/
+import { Vis } from '@kbn/visualizations-plugin/public';
import { METRIC_TYPES } from '@kbn/data-plugin/public';
import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub';
import { convertToLens } from '.';
import { createPanel, createSeries } from '../lib/__mocks__';
+import { Panel } from '../../../common/types';
const mockGetMetricsColumns = jest.fn();
const mockGetBucketsColumns = jest.fn();
@@ -54,6 +56,10 @@ describe('convertToLens', () => {
],
});
+ const vis = {
+ params: model,
+ } as Vis;
+
const bucket = {
isBucketed: true,
isSplit: true,
@@ -135,27 +141,27 @@ describe('convertToLens', () => {
test('should return null for invalid metrics', async () => {
mockIsValidMetrics.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockIsValidMetrics).toBeCalledTimes(1);
});
test('should return null for invalid or unsupported metrics', async () => {
mockGetMetricsColumns.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetMetricsColumns).toBeCalledTimes(1);
});
test('should return null for invalid or unsupported buckets', async () => {
mockGetBucketsColumns.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetBucketsColumns).toBeCalledTimes(1);
});
test('should return state for valid model', async () => {
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeDefined();
expect(result?.type).toBe('lnsMetric');
expect(mockGetBucketsColumns).toBeCalledTimes(model.series.length);
@@ -163,16 +169,16 @@ describe('convertToLens', () => {
});
test('should skip hidden series', async () => {
- const result = await convertToLens(
- createPanel({
+ const result = await convertToLens({
+ params: createPanel({
series: [
createSeries({
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
hidden: true,
}),
],
- })
- );
+ }),
+ } as Vis);
expect(result).toBeDefined();
expect(result?.type).toBe('lnsMetric');
expect(mockIsValidMetrics).toBeCalledTimes(0);
@@ -185,8 +191,8 @@ describe('convertToLens', () => {
indexPattern: { id: 'test-index-pattern-1' },
});
- const result = await convertToLens(
- createPanel({
+ const result = await convertToLens({
+ params: createPanel({
series: [
createSeries({
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
@@ -197,8 +203,8 @@ describe('convertToLens', () => {
hidden: false,
}),
],
- })
- );
+ }),
+ } as Vis);
expect(result).toBeNull();
});
@@ -207,8 +213,8 @@ describe('convertToLens', () => {
mockGetBucketsColumns.mockReturnValueOnce([]);
mockGetMetricsColumns.mockReturnValueOnce([metric]);
- const result = await convertToLens(
- createPanel({
+ const result = await convertToLens({
+ params: createPanel({
series: [
createSeries({
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
@@ -219,8 +225,8 @@ describe('convertToLens', () => {
hidden: false,
}),
],
- })
- );
+ }),
+ } as Vis);
expect(result).toBeNull();
});
@@ -229,8 +235,8 @@ describe('convertToLens', () => {
mockGetBucketsColumns.mockReturnValueOnce([bucket2]);
mockGetMetricsColumns.mockReturnValueOnce([metric]);
- const result = await convertToLens(
- createPanel({
+ const result = await convertToLens({
+ params: createPanel({
series: [
createSeries({
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
@@ -241,8 +247,8 @@ describe('convertToLens', () => {
hidden: false,
}),
],
- })
- );
+ }),
+ } as Vis);
expect(result).toBeNull();
});
@@ -251,8 +257,8 @@ describe('convertToLens', () => {
mockGetBucketsColumns.mockReturnValueOnce([bucket]);
mockGetMetricsColumns.mockReturnValueOnce([metric]);
- const result = await convertToLens(
- createPanel({
+ const result = await convertToLens({
+ params: createPanel({
series: [
createSeries({
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
@@ -263,8 +269,8 @@ describe('convertToLens', () => {
hidden: false,
}),
],
- })
- );
+ }),
+ } as Vis);
expect(result).toBeDefined();
expect(result?.type).toBe('lnsMetric');
expect(mockGetConfigurationForMetric).toBeCalledTimes(1);
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts
index 149acc513b9ff..fbf04a2dcf779 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/metric/index.ts
@@ -22,7 +22,10 @@ import { excludeMetaFromLayers, getUniqueBuckets } from '../utils';
const MAX_SERIES = 2;
const MAX_BUCKETS = 2;
-export const convertToLens: ConvertTsvbToLensVisualization = async (model, timeRange) => {
+export const convertToLens: ConvertTsvbToLensVisualization = async (
+ { params: model },
+ timeRange
+) => {
const dataViews = getDataViewsStart();
const seriesNum = model.series.filter((series) => !series.hidden).length;
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/table/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/table/index.test.ts
new file mode 100644
index 0000000000000..37676b302fba1
--- /dev/null
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/table/index.test.ts
@@ -0,0 +1,235 @@
+/*
+ * 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 { TableVisConfiguration } from '@kbn/visualizations-plugin/common';
+import { Vis } from '@kbn/visualizations-plugin/public';
+import { METRIC_TYPES } from '@kbn/data-plugin/public';
+import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub';
+import { convertToLens } from '.';
+import { createPanel, createSeries } from '../lib/__mocks__';
+import { Panel } from '../../../common/types';
+import { TSVB_METRIC_TYPES } from '../../../common/enums';
+
+const mockConvertToDateHistogramColumn = jest.fn();
+const mockGetMetricsColumns = jest.fn();
+const mockGetBucketsColumns = jest.fn();
+const mockGetConfigurationForTimeseries = jest.fn();
+const mockIsValidMetrics = jest.fn();
+const mockGetDatasourceValue = jest
+ .fn()
+ .mockImplementation(() => Promise.resolve(stubLogstashDataView));
+const mockGetDataSourceInfo = jest.fn();
+const mockGetColumnState = jest.fn();
+
+jest.mock('../../services', () => ({
+ getDataViewsStart: jest.fn(() => mockGetDatasourceValue),
+}));
+
+jest.mock('../lib/convert', () => ({
+ excludeMetaFromColumn: jest.fn().mockReturnValue({}),
+}));
+
+jest.mock('../lib/series', () => ({
+ getMetricsColumns: jest.fn(() => mockGetMetricsColumns()),
+ getBucketsColumns: jest.fn(() => mockGetBucketsColumns()),
+}));
+
+jest.mock('../lib/configurations/table', () => ({
+ getColumnState: jest.fn(() => mockGetColumnState()),
+}));
+
+jest.mock('../lib/metrics', () => ({
+ isValidMetrics: jest.fn(() => mockIsValidMetrics()),
+ getReducedTimeRange: jest.fn().mockReturnValue('10'),
+}));
+
+jest.mock('../lib/datasource', () => ({
+ getDataSourceInfo: jest.fn(() => mockGetDataSourceInfo()),
+}));
+
+describe('convertToLens', () => {
+ const model = createPanel({
+ series: [
+ createSeries({
+ metrics: [
+ { id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' },
+ { id: 'some-id-1', type: METRIC_TYPES.COUNT },
+ ],
+ }),
+ ],
+ });
+
+ const vis = {
+ params: model,
+ uiState: {
+ get: () => ({}),
+ },
+ } as Vis;
+
+ beforeEach(() => {
+ mockIsValidMetrics.mockReturnValue(true);
+ mockGetDataSourceInfo.mockReturnValue({
+ indexPatternId: 'test-index-pattern',
+ timeField: 'timeField',
+ indexPattern: { id: 'test-index-pattern' },
+ });
+ mockConvertToDateHistogramColumn.mockReturnValue({});
+ mockGetMetricsColumns.mockReturnValue([{}]);
+ mockGetBucketsColumns.mockReturnValue([{}]);
+ mockGetConfigurationForTimeseries.mockReturnValue({ layers: [] });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('should return null for invalid metrics', async () => {
+ mockIsValidMetrics.mockReturnValue(null);
+ const result = await convertToLens(vis);
+ expect(result).toBeNull();
+ expect(mockIsValidMetrics).toBeCalledTimes(1);
+ });
+
+ test('should return null for invalid or unsupported metrics', async () => {
+ mockGetMetricsColumns.mockReturnValue(null);
+ const result = await convertToLens(vis);
+ expect(result).toBeNull();
+ expect(mockGetMetricsColumns).toBeCalledTimes(1);
+ });
+
+ test('should return null if several series have different “Field” + “Aggregate function”', async () => {
+ const result = await convertToLens({
+ params: createPanel({
+ series: [createSeries({ aggregate_by: 'new' }), createSeries({ aggregate_by: 'test' })],
+ }),
+ uiState: {
+ get: () => ({}),
+ },
+ } as Vis);
+ expect(result).toBeNull();
+ expect(mockGetBucketsColumns).toBeCalledTimes(1);
+ });
+
+ test('should return null if “Aggregate function” is not supported', async () => {
+ const result = await convertToLens({
+ params: createPanel({
+ series: [createSeries({ aggregate_by: 'new', aggregate_function: 'cumulative_sum' })],
+ }),
+ uiState: {
+ get: () => ({}),
+ },
+ } as Vis);
+ expect(result).toBeNull();
+ expect(mockGetBucketsColumns).toBeCalledTimes(1);
+ });
+
+ test('should return null if model have not visible metrics', async () => {
+ const result = await convertToLens({
+ params: createPanel({
+ series: [
+ createSeries({
+ metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
+ hidden: true,
+ }),
+ ],
+ }),
+ uiState: {
+ get: () => ({}),
+ },
+ } as Vis);
+ expect(result).toBeNull();
+ });
+
+ test('should return null if only static value is visible metric', async () => {
+ mockGetMetricsColumns.mockReturnValue([
+ { columnId: 'metric-column-1', operationType: 'static_value' },
+ ]);
+ const result = await convertToLens({
+ params: createPanel({
+ series: [
+ createSeries({
+ metrics: [{ id: 'some-id', type: TSVB_METRIC_TYPES.STATIC }],
+ hidden: true,
+ }),
+ ],
+ }),
+ uiState: {
+ get: () => ({}),
+ },
+ } as Vis);
+ expect(result).toBeNull();
+ });
+
+ test('should return state for valid model', async () => {
+ const result = await convertToLens(vis);
+ expect(result).toBeDefined();
+ expect(result?.type).toBe('lnsDatatable');
+ expect(mockGetBucketsColumns).toBeCalledTimes(1);
+ // every series + group by
+ expect(mockGetColumnState).toBeCalledTimes(model.series.length + 1);
+ });
+
+ test('should return state for valid model with “Field” + “Aggregate function”', async () => {
+ const result = await convertToLens({
+ params: createPanel({
+ series: [createSeries({ aggregate_by: 'new', aggregate_function: 'sum' })],
+ }),
+ uiState: {
+ get: () => ({}),
+ },
+ } as Vis);
+ expect(result).toBeDefined();
+ expect(result?.type).toBe('lnsDatatable');
+ expect(mockGetBucketsColumns).toBeCalledTimes(2);
+ // every series + group by + (“Field” + “Aggregate function”)
+ expect(mockGetColumnState).toBeCalledTimes(model.series.length + 2);
+ });
+
+ test('should return correct sorting config', async () => {
+ mockGetMetricsColumns.mockReturnValue([{ columnId: 'metric-column-1' }]);
+ const result = await convertToLens({
+ params: createPanel({
+ series: [createSeries({ id: 'test' })],
+ }),
+ uiState: {
+ get: () => ({ sort: { order: 'decs', column: 'test' } }),
+ },
+ } as Vis);
+ expect(result).toBeDefined();
+ expect(result?.type).toBe('lnsDatatable');
+ expect((result?.configuration as TableVisConfiguration).sorting).toEqual({
+ direction: 'decs',
+ columnId: 'metric-column-1',
+ });
+ expect(mockGetBucketsColumns).toBeCalledTimes(1);
+ // every series + group by
+ expect(mockGetColumnState).toBeCalledTimes(model.series.length + 1);
+ });
+
+ test('should skip hidden series', async () => {
+ const result = await convertToLens({
+ params: createPanel({
+ series: [
+ createSeries({
+ metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
+ hidden: true,
+ }),
+ createSeries({
+ metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
+ }),
+ ],
+ }),
+ uiState: {
+ get: () => ({}),
+ },
+ } as Vis);
+ expect(result).toBeDefined();
+ expect(result?.type).toBe('lnsDatatable');
+ expect(mockIsValidMetrics).toBeCalledTimes(1);
+ });
+});
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/table/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/table/index.ts
new file mode 100644
index 0000000000000..0219d1080724b
--- /dev/null
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/table/index.ts
@@ -0,0 +1,183 @@
+/*
+ * 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 uuid from 'uuid';
+import { parseTimeShift } from '@kbn/data-plugin/common';
+import { getIndexPatternIds, Layer } from '@kbn/visualizations-plugin/common/convert_to_lens';
+import { PANEL_TYPES } from '../../../common/enums';
+import { getDataViewsStart } from '../../services';
+import { getColumnState } from '../lib/configurations/table';
+import { getDataSourceInfo } from '../lib/datasource';
+import { getMetricsColumns, getBucketsColumns } from '../lib/series';
+import { getReducedTimeRange, isValidMetrics } from '../lib/metrics';
+import { ConvertTsvbToLensVisualization } from '../types';
+import { Layer as ExtendedLayer, excludeMetaFromColumn, Column } from '../lib/convert';
+
+const excludeMetaFromLayers = (layers: Record): Record => {
+ const newLayers: Record = {};
+ Object.entries(layers).forEach(([layerId, layer]) => {
+ const columns = layer.columns.map(excludeMetaFromColumn);
+ newLayers[layerId] = { ...layer, columns };
+ });
+
+ return newLayers;
+};
+
+export const convertToLens: ConvertTsvbToLensVisualization = async (
+ { params: model, uiState },
+ timeRange
+) => {
+ const columnStates = [];
+ const dataViews = getDataViewsStart();
+ const seriesNum = model.series.filter((series) => !series.hidden).length;
+ const sortConfig = uiState.get('table')?.sort ?? {};
+
+ const datasourceInfo = await getDataSourceInfo(
+ model.index_pattern,
+ model.time_field,
+ false,
+ undefined,
+ undefined,
+ dataViews
+ );
+
+ if (!datasourceInfo) {
+ return null;
+ }
+
+ const { indexPatternId, indexPattern } = datasourceInfo;
+
+ const commonBucketsColumns = getBucketsColumns(
+ undefined,
+ {
+ split_mode: 'terms',
+ terms_field: model.pivot_id,
+ terms_size: model.pivot_rows ? model.pivot_rows.toString() : undefined,
+ },
+ [],
+ indexPattern!,
+ false,
+ model.pivot_label,
+ false
+ );
+
+ if (!commonBucketsColumns) {
+ return null;
+ }
+
+ const sortConfiguration = {
+ columnId: commonBucketsColumns[0].columnId,
+ direction: sortConfig.order,
+ };
+
+ columnStates.push(getColumnState(commonBucketsColumns[0].columnId));
+
+ let bucketsColumns: Column[] | null = [];
+
+ if (
+ !model.series.every(
+ (s) =>
+ ((!s.aggregate_by && !model.series[0].aggregate_by) ||
+ s.aggregate_by === model.series[0].aggregate_by) &&
+ ((!s.aggregate_function && !model.series[0].aggregate_function) ||
+ s.aggregate_function === model.series[0].aggregate_function)
+ )
+ ) {
+ return null;
+ }
+
+ if (model.series[0].aggregate_by) {
+ if (
+ !model.series[0].aggregate_function ||
+ !['sum', 'mean', 'min', 'max'].includes(model.series[0].aggregate_function)
+ ) {
+ return null;
+ }
+ bucketsColumns = getBucketsColumns(
+ undefined,
+ {
+ split_mode: 'terms',
+ terms_field: model.series[0].aggregate_by,
+ },
+ [],
+ indexPattern!,
+ false
+ );
+ if (bucketsColumns === null) {
+ return null;
+ }
+
+ columnStates.push(
+ getColumnState(
+ bucketsColumns[0].columnId,
+ model.series[0].aggregate_function === 'mean' ? 'avg' : model.series[0].aggregate_function
+ )
+ );
+ }
+
+ const metrics = [];
+
+ // handle multiple layers/series
+ for (const [_, series] of model.series.entries()) {
+ if (series.hidden) {
+ continue;
+ }
+
+ // not valid time shift
+ if (series.offset_time && parseTimeShift(series.offset_time) === 'invalid') {
+ return null;
+ }
+
+ if (!isValidMetrics(series.metrics, PANEL_TYPES.TABLE, series.time_range_mode)) {
+ return null;
+ }
+
+ const reducedTimeRange = getReducedTimeRange(model, series, timeRange);
+
+ // handle multiple metrics
+ const metricsColumns = getMetricsColumns(series, indexPattern!, seriesNum, {
+ reducedTimeRange,
+ });
+ if (!metricsColumns) {
+ return null;
+ }
+
+ columnStates.push(getColumnState(metricsColumns[0].columnId, undefined, series));
+
+ if (sortConfig.column === series.id) {
+ sortConfiguration.columnId = metricsColumns[0].columnId;
+ }
+
+ metrics.push(...metricsColumns);
+ }
+
+ if (!metrics.length || metrics.every((metric) => metric.operationType === 'static_value')) {
+ return null;
+ }
+
+ const extendedLayer: ExtendedLayer = {
+ indexPatternId: indexPatternId as string,
+ layerId: uuid(),
+ columns: [...metrics, ...commonBucketsColumns, ...bucketsColumns],
+ columnOrder: [],
+ };
+
+ const layers = Object.values(excludeMetaFromLayers({ 0: extendedLayer }));
+
+ return {
+ type: 'lnsDatatable',
+ layers,
+ configuration: {
+ columns: columnStates,
+ layerId: extendedLayer.layerId,
+ layerType: 'data',
+ sorting: sortConfiguration,
+ },
+ indexPatternIds: getIndexPatternIds(layers),
+ };
+};
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts
index c81db38e05384..64fee3484b3b6 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts
@@ -6,10 +6,12 @@
* Side Public License, v 1.
*/
+import { Vis } from '@kbn/visualizations-plugin/public';
import { METRIC_TYPES } from '@kbn/data-plugin/public';
import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub';
import { convertToLens } from '.';
import { createPanel, createSeries } from '../lib/__mocks__';
+import { Panel } from '../../../common/types';
const mockConvertToDateHistogramColumn = jest.fn();
const mockGetMetricsColumns = jest.fn();
@@ -60,6 +62,10 @@ describe('convertToLens', () => {
],
});
+ const vis = {
+ params: model,
+ } as Vis;
+
beforeEach(() => {
mockIsValidMetrics.mockReturnValue(true);
mockGetDataSourceInfo.mockReturnValue({
@@ -79,35 +85,35 @@ describe('convertToLens', () => {
test('should return null for invalid metrics', async () => {
mockIsValidMetrics.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockIsValidMetrics).toBeCalledTimes(1);
});
test('should return null for empty time field', async () => {
mockGetDataSourceInfo.mockReturnValue({ timeField: null });
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetDataSourceInfo).toBeCalledTimes(1);
});
test('should return null for invalid date histogram', async () => {
mockConvertToDateHistogramColumn.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockConvertToDateHistogramColumn).toBeCalledTimes(1);
});
test('should return null for invalid or unsupported metrics', async () => {
mockGetMetricsColumns.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetMetricsColumns).toBeCalledTimes(1);
});
test('should return null for invalid or unsupported buckets', async () => {
mockGetBucketsColumns.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetBucketsColumns).toBeCalledTimes(1);
});
@@ -119,14 +125,14 @@ describe('convertToLens', () => {
operationType: 'static_value',
},
]);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetMetricsColumns).toBeCalledTimes(1);
expect(mockGetBucketsColumns).toBeCalledTimes(1);
});
test('should return state for valid model', async () => {
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeDefined();
expect(result?.type).toBe('lnsXY');
expect(mockGetBucketsColumns).toBeCalledTimes(model.series.length);
@@ -134,16 +140,16 @@ describe('convertToLens', () => {
});
test('should skip hidden series', async () => {
- const result = await convertToLens(
- createPanel({
+ const result = await convertToLens({
+ params: createPanel({
series: [
createSeries({
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
hidden: true,
}),
],
- })
- );
+ }),
+ } as Vis);
expect(result).toBeDefined();
expect(result?.type).toBe('lnsXY');
expect(mockIsValidMetrics).toBeCalledTimes(0);
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts
index ef678fcc2dab4..a08b7113c4a7b 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts
@@ -14,7 +14,6 @@ import {
} from '@kbn/visualizations-plugin/common/convert_to_lens';
import uuid from 'uuid';
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
-import { Panel } from '../../../common/types';
import { PANEL_TYPES } from '../../../common/enums';
import { getDataViewsStart } from '../../services';
import { getDataSourceInfo } from '../lib/datasource';
@@ -41,7 +40,7 @@ const excludeMetaFromLayers = (layers: Record): Record {
+export const convertToLens: ConvertTsvbToLensVisualization = async ({ params: model }) => {
const dataViews: DataViewsPublicPluginStart = getDataViewsStart();
const extendedLayers: Record = {};
const seriesNum = model.series.filter((series) => !series.hidden).length;
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.test.ts
index 7e4776f10ac9f..646323a6691d5 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.test.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.test.ts
@@ -6,10 +6,12 @@
* Side Public License, v 1.
*/
+import { Vis } from '@kbn/visualizations-plugin/public';
import { METRIC_TYPES } from '@kbn/data-plugin/public';
import { stubLogstashDataView } from '@kbn/data-views-plugin/common/data_view.stub';
import { convertToLens } from '.';
import { createPanel, createSeries } from '../lib/__mocks__';
+import { Panel } from '../../../common/types';
const mockGetMetricsColumns = jest.fn();
const mockGetBucketsColumns = jest.fn();
@@ -59,6 +61,10 @@ describe('convertToLens', () => {
],
});
+ const vis = {
+ params: model,
+ } as Vis;
+
beforeEach(() => {
mockIsValidMetrics.mockReturnValue(true);
mockGetDataSourceInfo.mockReturnValue({
@@ -77,27 +83,27 @@ describe('convertToLens', () => {
test('should return null for invalid metrics', async () => {
mockIsValidMetrics.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockIsValidMetrics).toBeCalledTimes(1);
});
test('should return null for invalid or unsupported metrics', async () => {
mockGetMetricsColumns.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetMetricsColumns).toBeCalledTimes(1);
});
test('should return null for invalid or unsupported buckets', async () => {
mockGetBucketsColumns.mockReturnValue(null);
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeNull();
expect(mockGetBucketsColumns).toBeCalledTimes(1);
});
test('should return state for valid model', async () => {
- const result = await convertToLens(model);
+ const result = await convertToLens(vis);
expect(result).toBeDefined();
expect(result?.type).toBe('lnsXY');
expect(mockGetBucketsColumns).toBeCalledTimes(model.series.length);
@@ -105,16 +111,16 @@ describe('convertToLens', () => {
});
test('should skip hidden series', async () => {
- const result = await convertToLens(
- createPanel({
+ const result = await convertToLens({
+ params: createPanel({
series: [
createSeries({
metrics: [{ id: 'some-id', type: METRIC_TYPES.AVG, field: 'test-field' }],
hidden: true,
}),
],
- })
- );
+ }),
+ } as Vis);
expect(result).toBeDefined();
expect(result?.type).toBe('lnsXY');
expect(mockIsValidMetrics).toBeCalledTimes(0);
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts
index 130646f72f127..9505b7f5f0c7e 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts
@@ -28,7 +28,10 @@ const excludeMetaFromLayers = (layers: Record): Record {
+export const convertToLens: ConvertTsvbToLensVisualization = async (
+ { params: model },
+ timeRange
+) => {
const dataViews = getDataViewsStart();
const extendedLayers: Record = {};
const seriesNum = model.series.filter((series) => !series.hidden).length;
diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts
index 69a90c7864eb3..9f00a669ea5c3 100644
--- a/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts
+++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts
@@ -6,18 +6,22 @@
* Side Public License, v 1.
*/
+import { Vis } from '@kbn/visualizations-plugin/public';
import {
MetricVisConfiguration,
NavigateToLensContext,
XYConfiguration,
+ TableVisConfiguration,
} from '@kbn/visualizations-plugin/common';
import { TimeRange } from '@kbn/data-plugin/common';
import type { Panel } from '../../common/types';
export type ConvertTsvbToLensVisualization = (
- model: Panel,
+ vis: Vis,
timeRange?: TimeRange
-) => Promise | null>;
+) => Promise | null>;
export interface Filter {
kql?: string | { [key: string]: any } | undefined;
diff --git a/src/plugins/vis_types/timeseries/public/metrics_type.ts b/src/plugins/vis_types/timeseries/public/metrics_type.ts
index 3bf9f9b90bf1a..43be3ee3004f4 100644
--- a/src/plugins/vis_types/timeseries/public/metrics_type.ts
+++ b/src/plugins/vis_types/timeseries/public/metrics_type.ts
@@ -171,15 +171,13 @@ export const metricsVisDefinition: VisTypeDefinition<
return {
canNavigateToLens: Boolean(
vis?.params
- ? await convertTSVBtoLensConfiguration(vis.params as Panel, timeFilter?.getAbsoluteTime())
+ ? await convertTSVBtoLensConfiguration(vis, timeFilter?.getAbsoluteTime())
: null
),
};
},
navigateToLens: async (vis, timeFilter) =>
- vis?.params
- ? await convertTSVBtoLensConfiguration(vis?.params as Panel, timeFilter?.getAbsoluteTime())
- : null,
+ vis?.params ? await convertTSVBtoLensConfiguration(vis, timeFilter?.getAbsoluteTime()) : null,
inspectorAdapters: () => ({
requests: new RequestAdapter(),
diff --git a/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts b/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts
index 4f7a5ad715215..f62f61f0c50ab 100644
--- a/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts
+++ b/src/plugins/visualizations/common/convert_to_lens/types/configurations.ts
@@ -183,6 +183,7 @@ export interface ColumnState {
summaryRow?: 'none' | 'sum' | 'avg' | 'count' | 'min' | 'max';
alignment?: 'left' | 'right' | 'center';
collapseFn?: CollapseFunction;
+ palette?: PaletteOutput;
}
export interface TableVisConfiguration {
diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx
index e7512c6dd6473..0111c9026397d 100644
--- a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx
+++ b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx
@@ -114,6 +114,7 @@ const TopNav = ({
vis.type,
vis.params,
uiStateJSON?.vis,
+ uiStateJSON?.table,
vis.data.indexPattern,
]);
diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts
index edbd53e80d8c5..1aacde4127f37 100644
--- a/test/functional/page_objects/visual_builder_page.ts
+++ b/test/functional/page_objects/visual_builder_page.ts
@@ -659,6 +659,28 @@ export class VisualBuilderPageObject extends FtrService {
await this.comboBox.setElement(fieldEl, field);
}
+ public async setFieldForAggregateBy(field: string): Promise {
+ const aggregateBy = await this.testSubjects.find('tsvbAggregateBySelect');
+
+ await this.retry.try(async () => {
+ await this.comboBox.setElement(aggregateBy, field);
+ if (!(await this.comboBox.isOptionSelected(aggregateBy, field))) {
+ throw new Error(`aggregate by field - ${field} is not selected`);
+ }
+ });
+ }
+
+ public async setFunctionForAggregateFunction(func: string): Promise {
+ const aggregateFunction = await this.testSubjects.find('tsvbAggregateFunctionCombobox');
+
+ await this.retry.try(async () => {
+ await this.comboBox.setElement(aggregateFunction, func);
+ if (!(await this.comboBox.isOptionSelected(aggregateFunction, func))) {
+ throw new Error(`aggregate function - ${func} is not selected`);
+ }
+ });
+ }
+
public async checkFieldForAggregationValidity(aggNth: number = 0): Promise {
const fieldEl = await this.getFieldForAggregation(aggNth);
diff --git a/x-pack/test/functional/apps/lens/open_in_lens/tsvb/index.ts b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/index.ts
index c0b5197983aa4..8428d145c60ef 100644
--- a/x-pack/test/functional/apps/lens/open_in_lens/tsvb/index.ts
+++ b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/index.ts
@@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./timeseries'));
loadTestFile(require.resolve('./dashboard'));
loadTestFile(require.resolve('./top_n'));
+ loadTestFile(require.resolve('./table'));
});
}
diff --git a/x-pack/test/functional/apps/lens/open_in_lens/tsvb/table.ts b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/table.ts
new file mode 100644
index 0000000000000..e3d52852cd61b
--- /dev/null
+++ b/x-pack/test/functional/apps/lens/open_in_lens/tsvb/table.ts
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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 { FtrProviderContext } from '../../../../ftr_provider_context';
+
+export default function ({ getPageObjects, getService }: FtrProviderContext) {
+ const { visualize, visualBuilder, lens, header } = getPageObjects([
+ 'visualBuilder',
+ 'visualize',
+ 'header',
+ 'lens',
+ ]);
+
+ const testSubjects = getService('testSubjects');
+ const retry = getService('retry');
+
+ describe('Table', function describeIndexTests() {
+ before(async () => {
+ await visualize.initTests();
+ });
+
+ beforeEach(async () => {
+ await visualBuilder.resetPage();
+ await visualBuilder.clickTable();
+ await header.waitUntilLoadingHasFinished();
+ await visualBuilder.checkTableTabIsPresent();
+ await visualBuilder.selectGroupByField('machine.os.raw');
+ });
+
+ it('should not allow converting of not valid panel', async () => {
+ await visualBuilder.selectAggType('Max');
+ await header.waitUntilLoadingHasFinished();
+ expect(await visualize.hasNavigateToLensButton()).to.be(false);
+ });
+
+ it('should not allow converting of unsupported aggregations', async () => {
+ await visualBuilder.selectAggType('Sum of Squares');
+ await visualBuilder.setFieldForAggregation('machine.ram');
+
+ await header.waitUntilLoadingHasFinished();
+ expect(await visualize.hasNavigateToLensButton()).to.be(false);
+ });
+
+ it('should not allow converting sibling pipeline aggregations', async () => {
+ await visualBuilder.createNewAgg();
+
+ await visualBuilder.selectAggType('Overall Average', 1);
+ await visualBuilder.setFieldForAggregation('Count', 1);
+ await header.waitUntilLoadingHasFinished();
+ expect(await visualize.hasNavigateToLensButton()).to.be(false);
+ });
+
+ it('should not allow converting parent pipeline aggregations', async () => {
+ await visualBuilder.clickPanelOptions('table');
+ await visualBuilder.setMetricsDataTimerangeMode('Last value');
+ await visualBuilder.clickDataTab('table');
+ await visualBuilder.createNewAgg();
+
+ await visualBuilder.selectAggType('Cumulative Sum', 1);
+ await visualBuilder.setFieldForAggregation('Count', 1);
+ await header.waitUntilLoadingHasFinished();
+ expect(await visualize.hasNavigateToLensButton()).to.be(false);
+ });
+
+ it('should not allow converting not valid aggregation function', async () => {
+ await visualBuilder.clickSeriesOption();
+ await visualBuilder.setFieldForAggregateBy('clientip');
+ await visualBuilder.setFunctionForAggregateFunction('Cumulative Sum');
+ await header.waitUntilLoadingHasFinished();
+ expect(await visualize.hasNavigateToLensButton()).to.be(false);
+ });
+
+ it('should not allow converting series with different aggregation fucntion or aggregation by', async () => {
+ await visualBuilder.createNewAggSeries();
+ await visualBuilder.selectAggType('Static Value', 1);
+ await visualBuilder.setStaticValue(10);
+ await visualBuilder.clickSeriesOption();
+ await visualBuilder.setFieldForAggregateBy('bytes');
+ await visualBuilder.setFunctionForAggregateFunction('Sum');
+ await visualBuilder.clickSeriesOption(1);
+ await visualBuilder.setFieldForAggregateBy('bytes');
+ await visualBuilder.setFunctionForAggregateFunction('Min');
+ await header.waitUntilLoadingHasFinished();
+ expect(await visualize.hasNavigateToLensButton()).to.be(false);
+ });
+
+ it('should allow converting a count aggregation', async () => {
+ expect(await visualize.hasNavigateToLensButton()).to.be(true);
+ });
+
+ it('should convert last value mode to reduced time range', async () => {
+ await visualBuilder.clickPanelOptions('table');
+ await visualBuilder.setMetricsDataTimerangeMode('Last value');
+ await visualBuilder.setIntervalValue('1m');
+ await visualBuilder.clickDataTab('table');
+ await header.waitUntilLoadingHasFinished();
+
+ await visualize.navigateToLensFromAnotherVisulization();
+ await lens.waitForVisualization('lnsDataTable');
+ await lens.openDimensionEditor('lnsDatatable_metrics > lns-dimensionTrigger');
+ await testSubjects.click('indexPattern-advanced-accordion');
+ const reducedTimeRange = await testSubjects.find('indexPattern-dimension-reducedTimeRange');
+ expect(await reducedTimeRange.getVisibleText()).to.be('1 minute (1m)');
+ await retry.try(async () => {
+ const layerCount = await lens.getLayerCount();
+ expect(layerCount).to.be(1);
+ const metricDimensionText = await lens.getDimensionTriggerText('lnsDatatable_metrics', 0);
+ expect(metricDimensionText).to.be('Count of records last 1m');
+ });
+ });
+
+ it('should convert static value to the metric dimension', async () => {
+ await visualBuilder.createNewAggSeries();
+ await visualBuilder.selectAggType('Static Value', 1);
+ await visualBuilder.setStaticValue(10);
+
+ await header.waitUntilLoadingHasFinished();
+
+ await visualize.navigateToLensFromAnotherVisulization();
+ await lens.waitForVisualization('lnsDataTable');
+ await retry.try(async () => {
+ const layerCount = await lens.getLayerCount();
+ expect(layerCount).to.be(1);
+ const metricDimensionText1 = await lens.getDimensionTriggerText('lnsDatatable_metrics', 0);
+ const metricDimensionText2 = await lens.getDimensionTriggerText('lnsDatatable_metrics', 1);
+ expect(metricDimensionText1).to.be('Count of records');
+ expect(metricDimensionText2).to.be('10');
+ });
+ });
+
+ it('should convert aggregate by to split row dimension', async () => {
+ await visualBuilder.clickSeriesOption();
+ await visualBuilder.setFieldForAggregateBy('clientip');
+ await visualBuilder.setFunctionForAggregateFunction('Sum');
+ await header.waitUntilLoadingHasFinished();
+
+ await visualize.navigateToLensFromAnotherVisulization();
+ await lens.waitForVisualization('lnsDataTable');
+ await retry.try(async () => {
+ const layerCount = await lens.getLayerCount();
+ expect(layerCount).to.be(1);
+ const splitRowsText1 = await lens.getDimensionTriggerText('lnsDatatable_rows', 0);
+ const splitRowsText2 = await lens.getDimensionTriggerText('lnsDatatable_rows', 1);
+ expect(splitRowsText1).to.be('Top 10 values of machine.os.raw');
+ expect(splitRowsText2).to.be('Top 10 values of clientip');
+ });
+
+ await lens.openDimensionEditor('lnsDatatable_rows > lns-dimensionTrigger', 0, 1);
+ const collapseBy = await testSubjects.find('indexPattern-collapse-by');
+ expect(await collapseBy.getAttribute('value')).to.be('sum');
+ });
+
+ it('should convert group by field with custom label', async () => {
+ await visualBuilder.setColumnLabelValue('test');
+ await header.waitUntilLoadingHasFinished();
+
+ await visualize.navigateToLensFromAnotherVisulization();
+ await lens.waitForVisualization('lnsDataTable');
+ await retry.try(async () => {
+ const layerCount = await lens.getLayerCount();
+ expect(layerCount).to.be(1);
+ const splitRowsText = await lens.getDimensionTriggerText('lnsDatatable_rows', 0);
+ expect(splitRowsText).to.be('test');
+ });
+ });
+
+ it('should convert color ranges', async () => {
+ await visualBuilder.clickSeriesOption();
+
+ await visualBuilder.setColorRuleOperator('>= greater than or equal');
+ await visualBuilder.setColorRuleValue(10);
+ await visualBuilder.setColorPickerValue('#54B399');
+
+ await visualBuilder.createColorRule(1);
+
+ await visualBuilder.setColorRuleOperator('>= greater than or equal');
+ await visualBuilder.setColorRuleValue(100, 1);
+ await visualBuilder.setColorPickerValue('#54A000', 1);
+
+ await header.waitUntilLoadingHasFinished();
+ await visualize.navigateToLensFromAnotherVisulization();
+
+ await lens.waitForVisualization('lnsDataTable');
+ await retry.try(async () => {
+ const closePalettePanels = await testSubjects.findAll(
+ 'lns-indexPattern-PalettePanelContainerBack'
+ );
+ if (closePalettePanels.length) {
+ await lens.closePalettePanel();
+ await lens.closeDimensionEditor();
+ }
+
+ await lens.openDimensionEditor('lnsDatatable_metrics > lns-dimensionTrigger');
+
+ await lens.openPalettePanel('lnsDatatable');
+ const colorStops = await lens.getPaletteColorStops();
+
+ expect(colorStops).to.eql([
+ { stop: '10', color: 'rgba(84, 179, 153, 1)' },
+ { stop: '100', color: 'rgba(84, 160, 0, 1)' },
+ { stop: '', color: undefined },
+ ]);
+ });
+ });
+ });
+}